def evaluate_line(board, player, index1, index2, index3): """ Evaluates a line in board given by index1, index2, index3 for the provided player. Returns a score for that line assuming player is the max player and get_opponent(player) is the min player. You can see more details at the sites where I researched this algorithm. @param board: the tic-tac-toe board you want to evaluate. @param player: the max player you are scoring the line for. @param index1: first index in board that you want to check. @param index2: second index in board that you want to check. @param index3: third index in board that you want to check. @return: will return an int value based on the contents of the line given by index1, index2, and index3. Possible values are: - +100: all three spaces have player values (player wins) - +10: two spaces have player values, the remaining one is blank - +1: one space has a player value, the remaining two are blank - 0: there is at least one player value and one opponent value (the line is dead) - -1: one space has an opponent value, the remaining two are blank - -10: two spaces have opponent values, the remaining one is blank - -100: all three spaces have opponent values (opponent wins) """ opponent = utils.get_opponent(player) line_counts = Counter([board[index1], board[index2], board[index3]]) if line_counts[player] > 0 and line_counts[opponent] > 0: return 0 # line has player and opponent values = dead line if line_counts[opponent] == 0: return int(pow(10, line_counts[player] - 1)) # line has only player values return -int(pow(10, line_counts[opponent] - 1)) # line has only opponent values
def _minimax_score(board, player_to_move, player_to_optimize): winner = ttt.get_winner(board) if winner is not None: if winner == player_to_optimize: return 10 else: return -10 elif ttt.is_board_full(board): return 0 legal_moves = utils.get_all_legal_moves(board) scores = [] for move in legal_moves: _board = copy.deepcopy(board) ttt.make_move(player_to_move, _board, move) opp = utils.get_opponent(player_to_move) opp_best_response_score = _minimax_score(_board, opp, player_to_optimize) scores.append(opp_best_response_score) if player_to_move == player_to_optimize: return max(scores) else: return min(scores)
def minimax_score(board, player_to_move, current_player): winner = has_winner(board) if winner is not None: if winner == current_player: return 10 else: return -10 elif is_board_full(board): return 0 legal_moves = get_all_available_moves(board) scores = [] for move in legal_moves: # First make the move if move == None: continue _board = copy.deepcopy(board) make_move(_board, move, player_to_move) opponent = get_opponent(player_to_move) score = minimax_score(_board, opponent, current_player) scores.append(score) if player_to_move == current_player: return max(scores) else: return min(scores)
def blocks_their_winning_moves_ai(board, who_am_i): their_winning_move = _find_winning_move(board, utils.get_opponent(who_am_i)) if their_winning_move: return their_winning_move else: return _random_move(board)
def _cached_minimax_score(board, player_current, player_main): winner = engine.get_winner(board) opponent = utils.get_opponent(player_current) if winner == player_main: return 10 elif winner == opponent: return -10 elif engine.is_board_full(board): return 0 board_cache_key = str(board) is_main_player = player_current == player_main if board_cache_key in cache: scores = cache[board_cache_key] return max(scores) if is_main_player else min(scores) legal_moves = utils.get_legal_moves(board, player_current) scores = [] for move in legal_moves: _board = copy.deepcopy(board) engine.make_move(_board, move, player_current) score = _cached_minimax_score(_board, opponent, player_main) scores.append(score) cache[board_cache_key] = scores if player_current == player_main: return max(scores) else: return min(scores)
def finds_all_winning_moves_ai(board, who_am_i): my_winning_move = _find_winning_move(board, who_am_i) if my_winning_move: return my_winning_move their_winning_move = _find_winning_move(board, utils.get_opponent(who_am_i)) if their_winning_move: return their_winning_move return _random_move(board)
def minimax_ai(board, player): legal_moves = utils.get_legal_moves(board, player) opponent = utils.get_opponent(player) scores = [] for move in legal_moves: _board = copy.deepcopy(board) engine.make_move(_board, move, player) score = _cached_minimax_score(_board, opponent, player) scores.append(score) sorted_best_moves = [x for _, x in sorted(zip(scores, legal_moves))] return sorted_best_moves[-1]
def minimax_ai(board, who_am_i): best_move = None best_score = None for move in utils.get_all_legal_moves(board): _board = copy.deepcopy(board) ttt.make_move(who_am_i, _board, move) opp = utils.get_opponent(who_am_i) score = _minimax_score(_board, opp, who_am_i) if best_score is None or score > best_score: best_move = move best_score = score return best_move
def minimax(state, actions, player): # minimax(state, actions, player = 'O') """heiristic algorithm to improve the winning ratio""" winner = check_winner(state) if winner: return (None, None), winner else: state = (list([list(row) for row in state])) draw_move = None # print("I am going to evaluate", actions) for action in actions: # print("Evaluated Action: ", action) newBoard = deepcopy(state) row = action[0] column = action[1] newBoard[row][column] = player # insert 'O' # updates the empty_cells list and calls the function itself with the board as the newBoard _newBoard = (tuple([tuple(row) for row in newBoard])) newEmptyCells = findEmptyCells(_newBoard) # print("Who is the winner for?", newBoard) move, winner = minimax(_newBoard, newEmptyCells, get_opponent(player)) # print("The winner is", winner) # output: who is going to win? # ----> 'O', 'X', DRAW if player == 'O': if winner == 'O': # print("The winner is 'O'") return (row, column), winner if winner == "It's a Draw": draw_move = action if player == 'X': if winner == 'X': return (row, column), winner output = pick_random_move(state) # TODO: BUG HERE! vvvv return output, winner
def minimax(board, player, depth, alpha, beta): """ I researched the algorithm for the tic-tac-toe ai here: http://www.ntu.edu.sg/home/ehchua/programming/java/JavaGame_TicTacToe_AI.html Additional research at : http://en.wikipedia.org/wiki/Minimax#Minimax_algorithm_with_alternate_moves http://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning Uses the Minimax algorithm with alpha-beta pruning. Returns the best move for the provided player on the given board as a minimax_result namedtuple. This tuple has the score of the minimax algorithm as well as the x and y of the best move. @param board: a 1D array as generated by the game_board module representing the current tic-tac-toe board. @param player: the (int) value of the player looking to get the best move possible. @param depth: the depth of the tree to search. In tic-tac-toe, we are only looking one move ahead so we use 2 as the value. @param alpha: the default alpha for alpha-beta pruning. We use -inf. @param beta: the default beta for alpha-beta pruning. We use inf. @return: An int representing the best move for the provided player. The int will be the index on the given board where the given player should make their move. """ best_index = -1 moves = get_all_moves(board, player) opponent = utils.get_opponent(player) if moves == [] or depth == 0: return minimax_result(score=evaluate(board, config.COMPUTER), index=best_index) else: for move in moves: new_board = make_move(board, move, player) result = minimax(new_board, opponent, depth - 1, alpha, beta) if player == config.COMPUTER and result.score > alpha: alpha = result.score best_index = move elif player == config.HUMAN and result.score < beta: beta = result.score best_index = move if alpha >= beta: break best_score = alpha if player == config.COMPUTER else beta return minimax_result(score=best_score, index=best_index)
def minimax_ai(board, player_state): best_move = None best_score = None legal_moves = get_all_available_moves(board) for move in legal_moves: _board = copy.deepcopy(board) make_move(_board, move, player_state) opp = get_opponent(player_state) score = _minimax_score(_board, opp, player_state) if best_score is None or score > best_score: best_move = move best_score = score return best_move
def get_all_moves(board, player): """ Get a list of all moves available to the player in the current board. @param board: the board state you want to evaluate. @param player: the player you are getting moves for. @return: A list of all the moves available to the given player on the given board. If the board is invalid, the player has won, or the player's opponent has won, then there are no moves to make so an empty list is returned. """ moves = [] if not ( player_has_won(board, player) or player_has_won(board, utils.get_opponent(player)) or (not is_valid_board(board)) ): for index in range(9): if board[index] == config.NO_PLAYER: moves += [index] return moves
def _minimax_score(board, player_current, player_main): winner = engine.get_winner(board) opponent = utils.get_opponent(player_current) if winner == player_main: return 10 elif winner == opponent: return -10 elif engine.is_board_full(board): return 0 legal_moves = utils.get_legal_moves(board, player_current) scores = [] for move in legal_moves: _board = copy.deepcopy(board) engine.make_move(_board, move, player_current) score = _minimax_score(_board, opponent, player_main) scores.append(score) if player_current == player_main: return max(scores) else: return min(scores)