def test_jump_crowning(self): b = Board() b.set_board("...,...o,....x") b.push(b.legal_moves()[0]) self.assertEqual("..X.......,,,,,,,,,", b.get_board()) self.assertEqual(Checker(WHITE, True), b.checker_at(2, 0)) b.pop() self.assertEqual(",...o......,....x.....,,,,,,,", b.get_board()) self.assertEqual(Checker(WHITE, False), b.checker_at(4, 2))
def test_turns(self): b = Board() self.assertTrue(b.color) b.legal_moves() self.assertTrue(b.color) b.legal_moves()[0] self.assertTrue(b.color) b.push(b.legal_moves()[0]) self.assertTrue(not b.color)
def test_turns2(self): b = Board() b.set_board(",...o.o,,...o,..x") self.assertTrue(b.color) b.legal_moves() self.assertTrue(b.color) b.legal_moves()[0] self.assertTrue(b.color) b.push(b.legal_moves()[0]) self.assertTrue(not b.color)
def _alpha_beta(board: Board, transpositions: Dict[int, int], color: bool, depth: int, alpha: int, beta: int, opt: bool): """ Implementation of minimax with alpha-beta pruning follows the pseudo-code on Wikipedia: https://en.wikipedia.org/wiki/Alpha%E2%80%93beta_pruning#Pseudocode :param board: :param depth: Maximum tree depth to search. Decreases with recursive calls. :param alpha: :param beta: :param opt: :return: """ if hash(board) in transpositions: return transpositions[hash(board)] # if reached max depth or there's no legal moves left, return board value if depth == 0 or len(board.legal_moves()) <= 0: return _board_value(board, color) # if searching for optimal move when it's player's turn, look for MAX value if opt == MAX: value = -INF for move in board.legal_moves(): board.push(move) value = max( value, _alpha_beta(board, transpositions, color, depth - 1, alpha, beta, MIN)) transpositions[hash(board)] = value board.pop() alpha = max(alpha, value) if alpha >= beta: break # beta-prune return value # else (it's other player's turn), look for MIN value (that'd be MAX from other player's perspective) else: # opt == MIN value = +INF for move in board.legal_moves(): board.push(move) value = min( value, _alpha_beta(board, transpositions, color, depth - 1, alpha, beta, MAX)) transpositions[hash(board)] = value board.pop() beta = min(beta, value) if alpha >= beta: break # alpha-prune return value
def test_push_jump_chain(self): b = Board() b.set_board( ",...,.o.o,.....,.o...o,.......,...o...o,....x....,.o.o,...") b.push(b.legal_moves()[0]) self.assertEqual(",,,,,,,........x.,.o.o......,", b.get_board()) b.pop() self.assertEqual( ",,.o.o......,,.o...o....,,...o...o..,....x.....,.o.o......,", b.get_board()) pass
def test_border_twice(self): b = Board() b.set_board("...X") cx = Checker(WHITE, True) b.push([Move(cx, (3, 0), (5, 2))]) self.assertTrue(True, cx.crowned) b.push([Move(cx, (5, 2), (7, 0))]) self.assertTrue(True, cx.crowned) b.pop() self.assertTrue(True, cx.crowned) b.pop() self.assertTrue(True, cx.crowned)
def test_push_jump_chain_end_on_jumped(self): b = Board() b.set_board( ",,,,,....x.x...,.x.x......,......x.x.,.x........,..O.......") b.color = BLACK print(b.legal_moves()[0]) b.push(b.legal_moves()[0]) self.assertEqual(",,,,,,...O......,,,", b.get_board()) b.pop() self.assertEqual( ",,,,,....x.x...,.x.x......,......x.x.,.x........,..O.......", b.get_board())
def test_push_chain_no_crowning(self): b = Board() b.set_board(",......o.o.,,....o.....,...x......") b.push(b.legal_moves()[0]) self.assertIsNotNone(b.checker_at(9, 2)) self.assertFalse(b.checker_at(9, 2).crowned) b.pop() self.assertEqual(",......o.o.,,....o.....,...x......,,,,,", b.get_board()) self.assertIsNone(b.checker_at(9, 2)) self.assertFalse(b.checker_at(3, 4).crowned) pass
def test_promotion_pop(self): b = Board() b.set_board(",x") b.push(b.legal_moves()[0]) chk = Checker(WHITE, True) self.assertTrue(b.move_stack[-1][-1].is_promotion) self.assertEqual(b.checker_at(1, 0), chk) b.push([Move(chk, (1, 0), (5, 4))]) self.assertEqual(b.checker_at(5, 4), chk) b.pop() self.assertEqual(b.checker_at(1, 0), Checker(WHITE, True)) b.pop() self.assertEqual(b.checker_at(0, 1), Checker(WHITE, False))
def test_push_chain_crowning(self): # crowning can only happen if chain ended at the proper place (crowning can't happen in the middle of the chain) b = Board() b.set_board(",......o...,,....o.....,...x......") b.push(b.legal_moves()[0]) self.assertIsNotNone(b.checker_at(7, 0)) self.assertTrue(b.checker_at(7, 0).crowned) b.pop() self.assertEqual(",......o...,,....o.....,...x......,,,,,", b.get_board()) self.assertIsNone(b.checker_at(7, 0)) self.assertFalse(b.checker_at(3, 4).crowned) pass
def test_push_simple(self): b = Board() b.set_board(",,,,,,.x........,,,") b.push(b.legal_moves()[0]) self.assertEqual(",,,,,x.........,,,,", b.get_board()) b.pop() self.assertEqual(",,,,,,.x........,,,", b.get_board()) b = Board() b.set_board(",,....o.....,,,,,,,") b.color = checkers.BLACK b.push(b.legal_moves()[0]) self.assertEqual(",,,...o......,,,,,,", b.get_board()) b.pop() self.assertEqual(",,....o.....,,,,,,,", b.get_board()) pass
def test_push_crowning(self): b = Board() b.set_board(",.x") b.push(b.legal_moves()[1]) self.assertEqual("..X.......,,,,,,,,,", b.get_board()) self.assertTrue(b.checker_at(2, 0).crowned) b = Board() b.set_board(",,,,,,,,....o,") b.color = False b.push(b.legal_moves()[0]) self.assertEqual(",,,,,,,,,...O......", b.get_board()) self.assertTrue(b.checker_at(3, 9).crowned) b.pop() self.assertEqual(",,,,,,,,....o.....,", b.get_board()) self.assertFalse(b.checker_at(4, 8).crowned) pass
def get_state(move: Move, board: Board): board.push(move) n_our_un_crwn_pices = board.get_board().count(our_pics) n_their_un_crwn_pices = board.get_board().count(their_pics) n_our_king = board.get_board().count(our_kings) n_their_king = board.get_board().count(their_kings) n_pcs_on_edge = get_pics_on_edge(our_pics, our_kings, board) own_center_of_mass = 0 their_center_of_mass = 0 board.pop() return State(n_our_un_crwn_pices, n_their_un_crwn_pices, n_our_king, n_their_king, n_pcs_on_edge, own_center_of_mass, their_center_of_mass)
def test_push_jump(self): b = Board() b.set_board(",,....o,...x") b.push(b.legal_moves()[0]) self.assertIsNone(b.checker_at(4, 2)) self.assertEqual(",.....x....,,,,,,,,", b.get_board()) b.pop() self.assertIsNotNone(b.checker_at(4, 2)) self.assertEqual(",,....o.....,...x......,,,,,,", b.get_board()) b = Board() b.set_board(",,,,......x...,.....o....,,,,") b.color = checkers.BLACK b.push(b.legal_moves()[0]) self.assertIsNone(b.checker_at(6, 4)) self.assertEqual(",,,.......o..,,,,,,", b.get_board()) b.pop() self.assertIsNotNone(b.checker_at(6, 4)) self.assertEqual(",,,,......x...,.....o....,,,,", b.get_board()) pass
def alpha_beta_search(board: Board, max_depth: int) -> Move: """ Returns best Move for given Board, found by performing minimax algorithm with alpha-beta pruning. :param board: Board to start search from :param max_depth: Maximum tree depth to search :return: best found Move """ # By international checker rules and for given board_value function, board_value has a range of [-20, 20] best_move, best_value = None, -INF # Perform MAX step explicitly, storing best move and it's value player_color = True for move in board.legal_moves(): board.push(move) value = _alpha_beta(board, {}, player_color, max_depth, -INF, +INF, MAX) board.pop() if value > best_value: best_move = move return best_move
if new_state.to_string() not in T: optimal_future_value = 0 else: key_of_max = max(T[new_state.to_string()].keys(), key=(lambda k: T[new_state.to_string()][k])) optimal_future_value = T[new_state.to_string()][key_of_max] # Now we update transition table: # new_value <- (1-_alpha)old_value + _alpha*(reward_for_transition + _delta*(max (all_new_potential_transitions - current_transition))) if current_state not in T: T[current_state.to_string()] = {} T[current_state.to_string()][new_state.to_string()] = old_transition_value + _alpha*value_of_transition + _delta*optimal_future_value # Make move move = action_by_transition[chosen_transition_key] board.push(move) print("Q-learn made a move:\n") print(board) print("----- ----- -----") # AlphaBeta makes move # !! important: work with this copy of a board to prevent # accidental changes to actual game state. board_copy = copy.deepcopy(board) best_move = alpha_beta_search(board_copy, 4) if best_move is not None: board.push(best_move) else: