Python Forum
tic-tac-toe with a computer player
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
tic-tac-toe with a computer player
#1
Seeing the recent tic-tac-toe thread in general programming made me curious about writing tic-tac-toe with a computer player. Here is the result:
import random

board = []
lines = ((0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6))
corners = (0, 8, 2, 6)
corner_pairs = ((0, 8), (2, 6), (0, 2), (2, 8), (8, 6), (0, 6))
for count in range(9):
	board.append(str(count + 1))

pieces = ["X", "O"]
human_players = {"X": False, "O": False}

def draw_board():
	for row in range(0, 9, 3):
		print(f"{board[0 + row]}|{board[1 + row]}|{board[2+row]}")
		if row < 6:
			print("-----")

def winner():
	for line in lines:
		if board[line[0]] == board[line[1]] and board[line[0]] == board[line[2]]:
			return True
	return False

def computer_turn(piece):
	opponent = [x for x in pieces if x != piece][0]
	# 2nd turn to a corner open, select center
	used_squares = {piece: [], opponent: []}
	for index, square in enumerate(board):
		if square in pieces:
			used_squares[square].append(index)
	num_used = len(used_squares[piece]) + len(used_squares[opponent])
	if num_used == 1 and len(used_squares[opponent]) == 1:
		used = used_squares[opponent][0]
		if  used in corners:
			board[4] = piece
			return
	# 4th turn to opposing corners, select any edge square
	if num_used == 3 and len(used_squares[opponent]) == 2:
		if used_squares[opponent][0] in corner_pairs[0] and used_squares[opponent][1] in corner_pairs[0] or used_squares[opponent][0] in corner_pairs[1] and used_squares[opponent][1] in corner_pairs[1]:
			board[random.choice([1, 3, 5, 7])] = piece
			return

	# for the win, then the block
	for player in [piece, opponent]:
		for line in lines:
			for shuffle in range(3):
				if board[line[0]] == player:
					if board[line[1]] == player and board[line[2]] not in pieces:
						board[line[2]] = piece
						return
					elif board[line[2]] == player and board[line[1]] not in pieces:
						board[line[1]] = piece
						return
				line = (line[1], line[2], line[0])
	
	# Get a second corner opposite, then adjacent
	for pair in corner_pairs:
		if board[pair[0]] == piece and board[pair[1]] not in pieces:
			board[pair[1]] = piece
			return
		if board[pair[1]] == piece and board[pair[0]] not in pieces:
			board[pair[0]] = piece
			return
		
	# pick any available corner
	available_corners = [corner for corner in corners if board[corner] not in pieces]
	if len(available_corners) > 0:
		board[random.choice(available_corners)] = piece
		return
	
	# pick any remaining square
	choice = piece
	while choice in pieces:
		choice = random.choice(board)
	board[int(choice) - 1] = piece

def tic_tac_toe():
	for turn in range(9):
		piece = pieces[turn % 2]
		draw_board()
		square = -1
		if not human_players[piece]:
			computer_turn(piece)
		else:
			while square not in board and square not in pieces:
				square = input(f"\n{piece}'s turn, select a square: ")
			board[int(square) - 1] = piece
		print()
		if winner():
			print(f"{piece} is the winner")
			break
		else:
			if turn == 8:
				print("It's a draw")
	draw_board()

def main():
	num_players = int(input("Number of players: "))
	if num_players == 1:
		print("Select a piece")
		print("1) X")
		print("2) O")
		human_players["X" if int(input(":")) == 1 else "O"] = True
	elif num_players == 2:
		human_players["X"] = True
		human_players["O"] = True
	tic_tac_toe()

main()
Try and beat the computer. Let me know what you think. Can it be improved or simplified?
Number of players can be 0, 1, or 2. The computer will play all the non-players.
Reply
#2
If you want to learn about algorithms for choosing the best move, look at minimax. It's fairly straightforward to understand and it assumes both players choose the best move on their turn. It basically boils down to a depth first search of a tree, where the tree represents the board states that can be reached from the start. Minimax will search that tree unconditionally, meaning some paths will be explored even if that's unnecessary - alpha-beta pruning is an improvement on this.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020