def select_move(self, game_state: GameState): best_moves: List[Move] = [] best_score = MIN_SCORE best_black = MIN_SCORE best_white = MIN_SCORE for possible_move in game_state.legal_moves(): next_state = game_state.apply_move(possible_move) # Since our opponent plays next, figure out their best # possible outcome from there. opponent_best_outcome = alpha_beta_result(next_state, self.max_depth, best_black, best_white, self.eval_fn) # Our outcome is the opposite of our opponent's outcome. our_best_outcome = -opponent_best_outcome if len(best_moves) == 0 or our_best_outcome > best_score: # This is the best move so far. best_moves = [possible_move] best_score = our_best_outcome if game_state.next_player == Player.BLACK: best_black = best_score elif game_state.next_player == Player.WHITE: best_white = best_score elif our_best_outcome == best_score: # This is as good as our previous best move. best_moves.append(possible_move) # For variety, randomly select among all equally good moves. return random.choice(best_moves)
def alpha_beta_result(game_state: GameState, max_depth: int, best_black: int, best_white: int, eval_fn: Callable) -> int: if game_state.is_over(): return MAX_SCORE if game_state.winner( ) == game_state.next_player else MIN_SCORE if max_depth == 0: return eval_fn(game_state) best_so_far = MIN_SCORE for candidate_move in game_state.legal_moves(): next_state = game_state.apply_move(candidate_move) opponent_best_result = alpha_beta_result(next_state, max_depth - 1, best_black, best_white, eval_fn) our_result = -opponent_best_result if our_result > best_so_far: best_so_far = our_result if game_state.next_player == Player.WHITE: if best_so_far > best_white: best_white = best_so_far outcome_for_black = -best_so_far if outcome_for_black < best_black: return best_so_far elif game_state.next_player == Player.BLACK: if best_so_far > best_black: best_black = best_so_far outcome_for_white = -best_so_far if outcome_for_white < best_white: return best_so_far return best_so_far
def best_result(game_state: GameState, max_depth: int, eval_fn: Callable): if game_state.is_over(): return MAX_SCORE if game_state.winner( ) == game_state.next_player else MIN_SCORE if max_depth == 0: return eval_fn(game_state) best_so_far = MIN_SCORE for candidate_move in game_state.legal_moves(): next_state = game_state.apply_move(candidate_move) opponent_best_result = best_result(next_state, max_depth - 1, eval_fn) our_result = -1 * opponent_best_result if our_result > best_so_far: best_so_far = our_result return best_so_far
def best_result(game_state: GameState) -> GameResult: if game_state.is_over(): if game_state.winner() == game_state.next_player: return GameResult.Win elif game_state.winner() is None: return GameResult.Draw else: return GameResult.Loss best_result_so_far = GameResult.Loss for candidate_move in game_state.legal_moves(): next_state = game_state.apply_move(candidate_move) opponent_best_result = best_result(next_state) our_result = reverse_game_result(opponent_best_result) if our_result.value > best_result_so_far.value: best_result_so_far = our_result return best_result_so_far
def select_move(self, game_state: GameState) -> Move: best_moves: List[Move] = [] best_score = None # Loop over all legal moves. for possible_move in game_state.legal_moves(): # Calculate the game state if we select this move. next_state = game_state.apply_move(possible_move) # Since our opponent plays next, figure out their best # possible outcome from there. opponent_best_outcome = best_result(next_state, self.max_depth, self.eval_fn) # Our outcome is the opposite of our opponent's outcome. our_best_outcome = -1 * opponent_best_outcome if (not best_moves) or our_best_outcome > best_score: # This is the best move so far. best_moves = [possible_move] best_score = our_best_outcome elif our_best_outcome == best_score: # This is as good as our previous best move. best_moves.append(possible_move) # For variety, randomly select among all equally good moves. return random.choice(best_moves)
def select_move(self, game_state: GameState) -> Move: winning_moves: List[Move] = [] draw_moves: List[Move] = [] losing_moves: List[Move] = [] for possible_move in game_state.legal_moves(): next_state = game_state.apply_move(possible_move) opponent_best_outcome = best_result(next_state) our_best_outcome = reverse_game_result(opponent_best_outcome) if our_best_outcome == GameResult.Win: winning_moves.append(possible_move) elif our_best_outcome == GameResult.Draw: draw_moves.append(possible_move) else: losing_moves.append(possible_move) if len(winning_moves) > 0: return random.choice(winning_moves) elif len(draw_moves) > 0: return random.choice(draw_moves) else: return random.choice(losing_moves)