diff --git a/tictactoe/tictactoe.py b/tictactoe/tictactoe.py index 3d8d187..ac79dfb 100644 --- a/tictactoe/tictactoe.py +++ b/tictactoe/tictactoe.py @@ -1,8 +1,4 @@ -""" -Tic Tac Toe Player -""" import copy -from math import inf X = "X" O = "O" @@ -10,196 +6,120 @@ def initial_state(): - """ - Returns starting state of the board. - """ - return [[EMPTY, EMPTY, EMPTY], [EMPTY, EMPTY, EMPTY], [EMPTY, EMPTY, EMPTY]] + """Returns the starting state of the board.""" + return [[EMPTY, EMPTY, EMPTY] for _ in range(3)] def player(board): - """ - Returns player who has the next turn on a board. - """ - count = [0, 0] - for i in range(3): - for j in range(3): - if board[i][j] == "X": - count[0] = count[0] + 1 - elif board[i][j] == "O": - count[1] = count[1] + 1 - - if count[0] > count[1]: - return O - else: - return X + """Returns the player who has the next turn on the board.""" + x_count = sum(row.count(X) for row in board) + o_count = sum(row.count(O) for row in board) + return O if x_count > o_count else X def actions(board): - """ - Returns set of all possible actions (i, j) available on the board. - """ - possible_action = [] - for i in range(3): - for j in range(3): - if board[i][j] == None: - possible_action.append((i, j)) - - return possible_action + """Returns a list of all possible actions (i, j) available on the board.""" + return [(i, j) for i in range(3) for j in range(3) if board[i][j] is EMPTY] def result(board, action): - """ - Returns the board that results from making move (i, j) on the board. - """ - copy_board = copy.deepcopy(board) - - if copy_board[action[0]][action[1]] is None: - a = player(copy_board) - copy_board[action[0]][action[1]] = a - return copy_board - else: - raise "eror" + """Returns the board that results from making move (i, j) on the board.""" + if board[action[0]][action[1]] is not EMPTY: + raise ValueError("Invalid action") + + new_board = copy.deepcopy(board) + new_board[action[0]][action[1]] = player(board) + return new_board def winner(board): - """ - Returns the winner of the game, if there is one. - """ + """Returns the winner of the game, if there is one.""" + # Check rows, columns, and diagonals for i in range(3): - if ( - board[i][0] == board[i][1] - and board[i][0] == board[i][2] - and board[i][0] != None - ): - if board[i][0] == "X": - return X - elif board[i][0] == "O": - return O - else: - return None + if board[i][0] == board[i][1] == board[i][2] != EMPTY: + return board[i][0] + if board[0][i] == board[1][i] == board[2][i] != EMPTY: + return board[0][i] - for i in range(3): - if ( - board[0][i] == board[1][i] - and board[1][i] == board[2][i] - and board[0][i] != None - ): - if board[0][i] == "X": - return X - elif board[0][i] == "O": - return O - else: - return None - - if ( - board[0][0] == board[1][1] - and board[1][1] == board[2][2] - and board[0][0] != None - ): - if board[0][0] == "X": - return X - elif board[0][0] == "O": - return O - else: - return None - - if ( - board[2][0] == board[1][1] - and board[1][1] == board[0][2] - and board[2][0] != None - ): - if board[2][0] == "X": - return X - elif board[2][0] == "O": - return O - else: - return None + if board[0][0] == board[1][1] == board[2][2] != EMPTY: + return board[0][0] + if board[2][0] == board[1][1] == board[0][2] != EMPTY: + return board[2][0] return None def terminal(board): - """ - Returns True if game is over, False otherwise. - """ - if winner(board) == "X": - return True - elif winner(board) == "O": - return True - - for i in range(3): - for j in range(3): - if board[i][j] == None: - return False - return True + """Returns True if the game is over, False otherwise.""" + return winner(board) is not None or all(cell is not EMPTY for row in board for cell in row) def utility(board): - """ - Returns 1 if X has won the game, -1 if O has won, 0 otherwise. - """ - if winner(board) == "X": - return 1 - elif winner(board) == "O": - return -1 - else: - return 0 + """Returns 1 if X has won, -1 if O has won, 0 otherwise.""" + win = winner(board) + return 1 if win == X else -1 if win == O else 0 def minimax(board): - """ - Returns the optimal move for the current player on the board. - """ - # Check for terminal state + """Returns the optimal move for the current player on the board.""" if terminal(board): return None - # If X's turn - elif player(board) == X: - options = [] - for action in actions(board): - score = min_value(result(board, action)) - # Store options in list - options.append([score, action]) - # Return highest value action - return sorted(options, reverse=True)[0][1] - - # If O's turn + if player(board) == X: + return max_move(board) else: - options = [] - for action in actions(board): - score = max_value(result(board, action)) - # Store options in list - options.append([score, action]) - # Return lowest value action - return sorted(options)[0][1] + return min_move(board) + + +def max_move(board): + """Finds the best move for player X.""" + best_value = -float('inf') + best_action = None + for action in actions(board): + value = min_value(result(board, action)) + if value > best_value: + best_value = value + best_action = action + return best_action + + +def min_move(board): + """Finds the best move for player O.""" + best_value = float('inf') + best_action = None + for action in actions(board): + value = max_value(result(board, action)) + if value < best_value: + best_value = value + best_action = action + return best_action def max_value(board): - """ - Returns the highest value option of a min-value result - """ - # Check for terminal state + """Returns the maximum value (for X) of a move.""" if terminal(board): return utility(board) - - # Loop through possible steps - v = -inf + value = -float('inf') for action in actions(board): - v = max(v, min_value(result(board, action))) - return v + value = max(value, min_value(result(board, action))) + return value def min_value(board): - """ - Returns the smallest value option of a max-value result - """ - # Check for terminal state + """Returns the minimum value (for O) of a move.""" if terminal(board): return utility(board) - - # Loop through possible steps - v = inf + value = float('inf') for action in actions(board): - v = min(v, max_value(result(board, action))) - return v + value = min(value, max_value(result(board, action))) + return value + + +if __name__ == "__main__": + board = initial_state() + while not terminal(board): + move = minimax(board) + board = result(board, move) + print(f"Player {player(board)} made move: {move}") + for row in board: + print(row)