コード例 #1
0
def human_vs_agent(
    generate_move_1: GenMove,
    generate_move_2: GenMove,
    player_1: str = "Player 1",
    player_2: str = "Player 2",
    args_1: tuple = (),
    args_2: tuple = (),
    init_1: Callable = lambda board, player: None,
    init_2: Callable = lambda board, player: None,
):
    import time
    from agents.common import PLAYER1, PLAYER2, PLAYER1_PRINT, PLAYER2_PRINT, GameState
    from agents.common import initialize_game_state, pretty_print_board, apply_player_action, check_end_state

    players = (PLAYER1, PLAYER2)
    for play_first in (1, -1):
        for init, player in zip((init_1, init_2)[::play_first], players):
            init(initialize_game_state(), player)

        saved_state = {PLAYER1: None, PLAYER2: None}
        board = initialize_game_state()
        gen_moves = (generate_move_1, generate_move_2)[::play_first]
        player_names = (player_1, player_2)[::play_first]
        gen_args = (args_1, args_2)[::play_first]
        if play_first == 1:
            machine_player = PLAYER1
        else:
            machine_player = PLAYER2

        playing = True
        while playing:
            for player, player_name, gen_move, args in zip(
                    players,
                    player_names,
                    gen_moves,
                    gen_args,
            ):
                print(player, player_name)
                t0 = time.time()
                print(pretty_print_board(board))
                print(
                    f'{player_name} you are playing with {PLAYER1_PRINT if player == PLAYER1 else PLAYER2_PRINT}'
                )
                action, saved_state[player] = gen_move(board.copy(), player,
                                                       saved_state[player],
                                                       *args)
                print(f"Move time: {time.time() - t0:.3f}s")
                apply_player_action(board, action, player)
                end_state = check_end_state(board, player)
                if end_state != GameState.STILL_PLAYING:
                    print(pretty_print_board(board))
                    if end_state == GameState.IS_DRAW:
                        print("Game ended in draw")
                    else:
                        print(
                            f'{player_name} won playing {PLAYER1_PRINT if player == PLAYER1 else PLAYER2_PRINT}'
                        )
                    playing = False
                    break
コード例 #2
0
    def testPrettyPrintBoard(self):

        from agents.common import pretty_print_board, string_to_board

        board1 = np.ones((6, 7))
        board1[1:, 2:] = np.zeros((5, 5)) * player
        board2 = np.zeros((6, 7))
        board2[:4, :4] = np.eye(4) * PLAYER2
        board3 = initialize_game_state()
        board3[0, 0] = player  #checks that (0,0) is bottom left corner

        #representation of empty board:
        str0 = '|===============================|\n' \
               '|\t \t \t \t \t \t \t \t|\n' \
               '|\t \t \t \t \t \t \t \t|\n' \
               '|\t \t \t \t \t \t \t \t|\n' \
               '|\t \t \t \t \t \t \t \t|\n' \
               '|\t \t \t \t \t \t \t \t|\n' \
               '|\t \t \t \t \t \t \t \t|\n' \
               '|===============================|\n' \
               '|\t0\t1\t2\t3\t4\t5\t6\t|\n'

        #representation of board1:
        str1 = '|===============================|\n' \
               '|\tX\tX\t \t \t \t \t \t|\n' \
               '|\tX\tX\t \t \t \t \t \t|\n' \
               '|\tX\tX\t \t \t \t \t \t|\n' \
               '|\tX\tX\t \t \t \t \t \t|\n' \
               '|\tX\tX\t \t \t \t \t \t|\n' \
               '|\tX\tX\tX\tX\tX\tX\tX\t|\n' \
               '|===============================|\n' \
               '|\t0\t1\t2\t3\t4\t5\t6\t|\n'

        #representation of board2:
        str2 ='|===============================|\n' \
              '|\t \t \t \t \t \t \t \t|\n' \
              '|\t \t \t \t \t \t \t \t|\n' \
              '|\t \t \t \tO\t \t \t \t|\n' \
              '|\t \t \tO\t \t \t \t \t|\n' \
              '|\t \tO\t \t \t \t \t \t|\n' \
              '|\tO\t \t \t \t \t \t \t|\n' \
              '|===============================|\n' \
              '|\t0\t1\t2\t3\t4\t5\t6\t|\n'

        #representation of board3:
        str3 = '|===============================|\n' \
               '|\t \t \t \t \t \t \t \t|\n' \
               '|\t \t \t \t \t \t \t \t|\n' \
               '|\t \t \t \t \t \t \t \t|\n' \
               '|\t \t \t \t \t \t \t \t|\n' \
               '|\t \t \t \t \t \t \t \t|\n' \
               '|\tX\t \t \t \t \t \t \t|\n' \
               '|===============================|\n' \
               '|\t0\t1\t2\t3\t4\t5\t6\t|\n'

        self.assertEqual(pretty_print_board(initialize_game_state()), str0)
        self.assertEqual(pretty_print_board(board1), str1)
        self.assertEqual(pretty_print_board(board2), str2)
        self.assertEqual(pretty_print_board(board3), str3)
コード例 #3
0
def generate_move(
    board: np.ndarray, player: cm.BoardPiece,
    saved_state: Optional[cm.SavedState]
) -> Tuple[cm.PlayerAction, Optional[cm.SavedState]]:

    gamestate = cm.check_end_state(board, player)
    current_state = State(board, player, gamestate, None)
    root = Node.make_root(current_state)
    cm.pretty_print_board(root.state.board)
    bestmove, _ = Node.mcts(root)

    return bestmove, saved_state
コード例 #4
0
ファイル: test_common.py プロジェクト: AnnaLange32/connect_4
def test_pretty_print_board():
    from agents.common import pretty_print_board
    from agents.common import board

    ret = pretty_print_board(board)
    ret2 = pretty_print_board(
        np.array([[0, 2, 2, 1, 2, 1, 1], [0, 2, 2, 1, 2, 1, 2],
                  [0, 1, 1, 2, 2, 1, 1], [0, 1, 2, 0, 0, 0, 1],
                  [0, 2, 0, 0, 0, 0, 2], [0, 0, 0, 0, 0, 0, 0]]))

    assert isinstance(ret, str)
    assert ret2 == '| - - - - - - - |\n| . . . . . . . |\n| . X . . . . X |\n| . O X . . . O |\n| . O O X X O O |\n| . X X O X O X |\n| . X X O X O O |\n| - - - - - - - |\n| 0 1 2 3 4 5 6 |'
    with pytest.raises(ValueError):
        pretty_print_board('hello')
コード例 #5
0
ファイル: mcts.py プロジェクト: AndreeaGui/Connect_4
def run_simulation(start_node: MCTSNode,
                   root_player: BoardPiece,
                   print_final=False) -> (np.ndarray, GameState):
    """
    The 3rd part of the algorithm.
    This function runs a complete game with random moves from the start node board until one player wins.
    This is one simulation in the MCTS algorithm.
    :param start_node: the expended node from which we start the simulation
    :param root_player:
    :param print_final: flag variable for printing the final board of the game
    :return: the final board state (np.narray), the game end state (GameState)
    """
    current_board = start_node.board.copy()
    current_player = start_node.player
    while check_end_state(current_board,
                          current_player) == GameState.STILL_PLAYING:
        # action = np.random.randint(0, 7)
        possible_actions = possible_moves(current_board)
        action = possible_actions[np.random.randint(len(possible_actions))]
        current_board = apply_player_action(current_board, np.int8(action),
                                            current_player)
        current_player = find_opponent(current_player)

    game_result = check_end_state(current_board, root_player)
    if print_final:
        print(pretty_print_board(current_board))
    return current_board, game_result
コード例 #6
0
def test_string_to_board() -> str:

    board = initialize_game_state()
    board_test = pretty_print_board(board)
    ret = string_to_board(board_test)

    assert isinstance(ret, np.ndarray)
コード例 #7
0
ファイル: test_common.py プロジェクト: tah0/conn4
def test_pretty_print_board():
    from agents.common import pretty_print_board
    from agents.common import initialize_game_state

    dummy_board = initialize_game_state()
    ret = pretty_print_board(dummy_board)

    # Split string at newlines
    rows = ret.split('\n')

    # 1. Evaluate board area

    # number of rows (excluding borders)  = 1st dim of board
    assert len(rows[1:-2]) == dummy_board.shape[0]

    # length of each row (excluding border rows) = 2nd dim of board
    assert all((len(r) - 1) / 2 == dummy_board.shape[1]
               for r in rows[1:-2])  # div by 2 to account for space

    # 2. Evaluate border area
    borderForm = '|{}|'.format('=' * (2 * (dummy_board.shape[1]) - 1))
    column_label_form = '|{}|'.format(' '.join(
        [str(n) for n in range(dummy_board.shape[1])]))

    assert rows[0] == borderForm
    assert rows[-2] == borderForm
    assert rows[-1] == column_label_form
コード例 #8
0
def test_pretty_print_board():
    board = initialize_game_state()

    pp_string = pretty_print_board(board)

    assert isinstance(pp_string, str)
    print(pp_string)
    return pp_string
コード例 #9
0
    def testStringtoBoard(self):

        from agents.common import string_to_board, pretty_print_board

        #It's enough to check that string_to_board ° pretty_print = id:
        board = np.random.choice([player, PLAYER2], ((6, 7)))
        self.assertTrue(
            np.array_equal(board, string_to_board(pretty_print_board(board))))
コード例 #10
0
def test_pretty_print_board(board=board_to_test):
    """
    Test the printing of the board and also the function of num:to char included
    :param board : Selected board
    """
    from agents.common import pretty_print_board
    ret = pretty_print_board(board)
    assert isinstance(ret, str)
    print(ret)
コード例 #11
0
def test_pretty_print_board():
    from agents.common import pretty_print_board

    pretty_boards = [
        pretty_print_board(game_state) for game_state in game_states
    ]

    for ind, board in enumerate(pretty_boards):
        assert isinstance(board, str)
        assert board == boards[ind]
コード例 #12
0
ファイル: test_common.py プロジェクト: AndreeaGui/Connect_4
def test_pretty_print_board():
    from agents.common import pretty_print_board
    assert pretty_print_board(b4) == "|==============|\n" \
                                     "|              |\n" \
                                     "|              |\n" \
                                     "|    X X       |\n" \
                                     "|    O X X     |\n" \
                                     "|  O O X X     |\n" \
                                     "|  O O O X X   |\n" \
                                     "|==============|\n" \
                                     "|0 1 2 3 4 5 6 |"
コード例 #13
0
def test_string_to_board():
    from agents.common import string_to_board
    from agents.common import pretty_print_board, initialize_game_state

    board = initialize_game_state()
    board_str = pretty_print_board(board)
    ret = string_to_board(board_str)

    assert isinstance(ret, np.ndarray)
    assert ret.shape == (6, 7)
    assert np.all(ret == NO_PLAYER)
コード例 #14
0
def test_pretty_print_board():
    from agents.common import pretty_print_board, initialize_game_state

    board = initialize_game_state()
    board_str = pretty_print_board(board)
    nlines = 9

    assert len(board_str.splitlines()) == nlines
    assert board_str[-1] == '|'
    assert board_str[0] == '|'
    assert isinstance(board_str, str)
コード例 #15
0
ファイル: test_common.py プロジェクト: AnnaLange32/connect_4
def test_string_to_board():
    from agents.common import string_to_board
    from agents.common import pretty_print_board

    board1 = np.zeros((6, 7), dtype=np.int8)
    board1[0, :] = np.array([0, 1, 1, 1, 1, 1, 0])
    board1[1, :] = np.array([0, 1, 1, 1, 1, 1, 0])
    board1[2, :] = np.array([0, 1, 1, 1, 1, 1, 0])
    board1[3, :] = np.array([0, 2, 2, 2, 2, 0, 0])

    board2 = np.zeros((6, 7), dtype=np.int8)
    board2[:, 4] = np.array([2, 2, 2, 2, 0, 0])
    ret = pretty_print_board(board1)
    ret1 = string_to_board(ret)
    ret2 = pretty_print_board(board2)
    ret3 = string_to_board(ret2)

    assert isinstance(ret1, np.ndarray)
    assert ret1.dtype == np.int8
    assert (board1 == ret1).all()
    assert (board2 == ret3).all()
コード例 #16
0
def test_string_to_board(board=board_to_test):
    """
    Test the implementation of the function string to board
    :param board: Selected board
    """
    from agents.common import string_to_board, pretty_print_board

    ret_test = string_to_board(pretty_print_board(board))
    assert isinstance(ret_test, np.ndarray)
    assert ret_test.dtype == BoardPiece
    comparison = board == ret_test
    comparison.all()
    assert comparison.all()
コード例 #17
0
def test_pretty_print_board() -> str:

    test_board = initialize_game_state()
    ret = pretty_print_board(test_board)

    test_board[0, 1] = PLAYER1
    test_board[1, 1] = PLAYER2
    test_board[0, 2] = PLAYER2
    test_board[2, 2] = PLAYER2
    test_board[1, 3] = PLAYER2
    test_board[0, 0] = PLAYER1

    assert isinstance(ret, str)
コード例 #18
0
def test_pretty_print_board():

    test_board = cm.initialize_game_state()
    test_board = np.random.randint(0, 3, test_board.shape)
    print('')
    print(test_board)

    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)

    assert isinstance(board_str, str)
    return
コード例 #19
0
def test_pretty_print_board():
    from agents.common import pretty_print_board
    """
    Test the given boards and see if they output the correct string representation
    """

    board = np.array([[BoardPiece(2), BoardPiece(0), BoardPiece(2), BoardPiece(0), BoardPiece(1), BoardPiece(0), BoardPiece(1)],
                    [BoardPiece(1), BoardPiece(2), BoardPiece(1), BoardPiece(2), BoardPiece(1), BoardPiece(2), BoardPiece(1)],
                    [BoardPiece(2), BoardPiece(0), BoardPiece(2), BoardPiece(0), BoardPiece(1), BoardPiece(0), BoardPiece(1)],
                    [BoardPiece(1), BoardPiece(2), BoardPiece(1), BoardPiece(2), BoardPiece(1), BoardPiece(2), BoardPiece(1)],
                    [BoardPiece(2), BoardPiece(0), BoardPiece(2), BoardPiece(0), BoardPiece(1), BoardPiece(0), BoardPiece(1)],
                    [BoardPiece(1), BoardPiece(2), BoardPiece(1), BoardPiece(2), BoardPiece(1), BoardPiece(2), BoardPiece(1)]])

    test_pretty_board = '''|=======|
|XOXOXOX|
|O O X X|
|XOXOXOX|
|O O X X|
|XOXOXOX|
|O O X X|
|=======|
|0123456|'''

    assert pretty_print_board(board) == test_pretty_board

    empty_board = np.ndarray(shape=(6, 7), dtype=BoardPiece)
    empty_board[:] = NO_PLAYER

    test_empty_pretty_board = '''|=======|
|       |
|       |
|       |
|       |
|       |
|       |
|=======|
|0123456|'''

    assert pretty_print_board(empty_board) == test_empty_pretty_board
コード例 #20
0
ファイル: test_common.py プロジェクト: tah0/conn4
def test_string_to_board():
    from agents.common import string_to_board
    from agents.common import initialize_game_state, pretty_print_board

    dummy_board = initialize_game_state()

    # pretty print the board to get a string
    dummy_string = pretty_print_board(dummy_board)

    # convert string back to board
    ret = string_to_board(dummy_string)

    # new board should match original board
    assert (ret == dummy_board).all()
コード例 #21
0
ファイル: test_mcts.py プロジェクト: marshineer/PCP2020
def test_node_initialization():
    # Initialize a game
    player = cm.PLAYER1
    arr_bd, bit_bd, mask_bd, player = generate_full_board(player, 1)
    bd_shp = arr_bd.shape

    # Generate a board that is not a win, with only a single piece missing
    while cm.check_end_state(bit_bd, mask_bd, bd_shp) == cm.GameState.IS_WIN:
        arr_bd, bit_bd, mask_bd, player = generate_full_board(player, 1)
    print(cm.pretty_print_board(arr_bd))

    # Test whether node initializes and plays in proper (only empty) column
    move = agmcts.generate_move_mcts(arr_bd, player, None)[0]
    empty_col = cm.valid_actions(mask_bd, bd_shp)[0]
    print('MCTS plays in column {}'.format(move))
    assert move == empty_col
コード例 #22
0
def test_string_to_board():

    test_board = cm.initialize_game_state()
    test_board = np.random.randint(0, 3, test_board.shape)
    print('')
    print(test_board)
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    board_arr = cm.string_to_board(board_str)
    print('')
    print(board_arr)

    assert isinstance(test_board, np.ndarray)
    assert board_arr.dtype == np.int8
    assert board_arr.shape == test_board.shape
    return
コード例 #23
0
ファイル: test_common.py プロジェクト: bramantyois/minic4
def test_pretty_print_board():
    from agents.common import pretty_print_board

    test_str = '|====|\n'
    test_str += '|' + NO_PLAYER_PRINT + ' ' + PLAYER1_PRINT + ' |\n'
    test_str += '|' + PLAYER2_PRINT + ' ' + NO_PLAYER_PRINT + ' |\n'
    test_str += '|====|\n'
    test_str += '|0 1 |'

    test_arr = np.full((2, 2), NO_PLAYER, dtype=BoardPiece)
    test_arr[0, 1] = PLAYER1
    test_arr[1, 0] = PLAYER2

    ret = pretty_print_board(test_arr)

    assert isinstance(ret, str)
    assert ret == test_str
コード例 #24
0
def test_pretty_print_board():
    from agents.common import pretty_print_board
    board = np.zeros((6, 7))
    board[0, 0:7] = [1, 2, 2, 1, 1, 1, 2]
    board[1, 0:7] = [2, 1, 2, 1, 2, 2, 1]
    board[2, 0:7] = [1, 2, 1, 2, 0, 0, 0]
    board[3, 0:7] = [2, 2, 1, 0, 0, 0, 0]
    board[4, 0:7] = [1, 1, 0, 0, 0, 0, 0]
    board[5, 0:7] = [2, 0, 0, 0, 0, 0, 0]
    boardStr = '|==============|\n' \
               '|O             |\n' \
               '|X X           |\n' \
               '|O O X         |\n' \
               '|X O X O       |\n' \
               '|O X O X O O X |\n' \
               '|X O O X X X O |\n' \
               '|==============|\n' \
               '|0 1 2 3 4 5 6 |'
    assert boardStr == pretty_print_board(board)
コード例 #25
0
def test_board_to_bitmap():

    test_board = cm.initialize_game_state()
    test_board[0, 2] = cm.PLAYER1
    test_board[0, 3] = cm.PLAYER2
    test_board[0, 4] = cm.PLAYER2
    test_board[0, 5] = cm.PLAYER1
    test_board[1, 2] = cm.PLAYER1
    test_board[1, 3] = cm.PLAYER1
    test_board[1, 4] = cm.PLAYER2
    test_board[2, 2] = cm.PLAYER1
    test_board[2, 3] = cm.PLAYER2
    test_board[3, 2] = cm.PLAYER2
    test_board[3, 3] = cm.PLAYER2
    test_board[4, 3] = cm.PLAYER1

    # test_board[0, 2:4] = cm.PLAYER1
    # test_board[0, 5] = cm.PLAYER1
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)

    # mask_pos = int('000000000000100000001100000011000001110000111100', 2)
    # p1_pos = int('000000000000100000000000000001000000110000100100', 2)
    # p2_pos = int('000000000000000000001100000010000001000000011000', 2)
    mask_pos = int('0000000000000100000110011111000111100000000000000', 2)
    p1_pos = int('000000000000010000000001001000001110000000000000', 2)
    p2_pos = int('0000000000000000000110001101000100000000000000000', 2)

    print('')
    # print(bin(p1_pos))
    print(bin(cm.board_to_bitmap(test_board, cm.PLAYER1)[0]))
    # print(bin(p2_pos))
    print(bin(cm.board_to_bitmap(test_board, cm.PLAYER2)[0]))
    # print(bin(mask_pos))
    print(bin(cm.board_to_bitmap(test_board, cm.PLAYER2)[1]))
    # print(bin(cm.board_to_bitmap(test_board, cm.PLAYER1)[1]))

    assert cm.board_to_bitmap(test_board, cm.PLAYER2)[0] == p2_pos
    assert cm.board_to_bitmap(test_board, cm.PLAYER2)[1] == mask_pos
    assert cm.board_to_bitmap(test_board, cm.PLAYER1)[0] == \
        (mask_pos ^ p2_pos)
コード例 #26
0
def test_pretty_print_board_and_string_to_board():
    """Test the printing of a board and the transformation back to an array"""

    # Check that the print of the board to a string and the transformation back to the board works for every position on
    # the board for each player
    for player in players:
        board = initialize_game_state()
        n_rows, n_cols = board.shape
        for i in range(n_rows):
            for j in range(n_cols):
                board[i, j] = player
                # Transform the board into a pretty string
                pp_board = pretty_print_board(board)
                # Transform it back to an ndarray
                trans_board = string_to_board(pp_board)

                # Check that everything worked
                assert isinstance(pp_board, str)
                assert len(pp_board) > 42
                assert np.array_equal(board, trans_board)
コード例 #27
0
    def test_buildGameStateFromID(self):
        node = GameState(PLAYER1, '6421', self.board0)
        node2 = GameState(PLAYER2, '1420', self.board0)
        node.buildGameStateFromID()
        print(pretty_print_board(node.board))
        print(pretty_print_board(self.board_6421))
        assert node.possible and (node.board == self.board_6421).all()
        node2.buildGameStateFromID()
        print(pretty_print_board(node2.board))
        print(pretty_print_board(self.board_1420))
        assert node2.positionID and (node2.board == self.board_1420).all()

        node3 = GameState(PLAYER2, '1420', self.board1)
        node3.buildGameStateFromID()
        print(pretty_print_board(node3.board))
        print(pretty_print_board(self.board1))
        assert node3.possible
コード例 #28
0
def test_top_row():

    test_board = cm.initialize_game_state()
    test_board[0, 0] = cm.PLAYER1
    test_board[:6, 1] = cm.PLAYER1
    test_board[:2, 2] = cm.PLAYER1
    test_board[:3, 3] = cm.PLAYER1
    test_board[:4, 4] = cm.PLAYER1

    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)

    assert cm.top_row(test_board, cm.PlayerAction(0)) == 1
    assert cm.top_row(test_board, cm.PlayerAction(2)) == 2
    assert cm.top_row(test_board, cm.PlayerAction(3)) == 3
    assert cm.top_row(test_board, cm.PlayerAction(4)) == 4
    assert cm.top_row(test_board, cm.PlayerAction(5)) == 0
    assert cm.top_row(test_board, cm.PlayerAction(6)) == 0
    try:
        assert not cm.top_row(test_board, cm.PlayerAction(1))
    except IndexError:
        assert True
コード例 #29
0
def test_pretty_print_board():
    """
    test for pretty_print_board
        - be str type
        - have a length of 153
        - PLAYER1 and PLAYER2 tokens placed correctly [ visual inspection ]
    """
    # Make transfer variables for ease of use
    n = NO_PLAYER
    o = PLAYER1
    x = PLAYER2

    # Define test board:
    #   ARROW pointing UP
    #   PLAYER1 -> 'O' make the head of the arrow
    #   PLAYER2 -> 'X' make the body of the arrow
    test_board = np.array([
        [n, n, n, o, n, n, n],
        [n, n, o, o, o, n, n],
        [n, o, o, o, o, o, n],
        [n, n, x, x, x, n, n],
        [n, n, x, x, x, n, n],
        [n, n, x, x, x, n, n],
    ])

    # Function output
    out = cc.pretty_print_board(test_board)

    # Test output
    assert isinstance(out, str)
    assert len(out) == 153

    # Visual inspection
    print(
        '\n\nThis should be an ARROW pointing DOWN \n \'O\': head\n \'X\': body'
    )
    print(out)
コード例 #30
0
ファイル: test_mcts.py プロジェクト: marshineer/PCP2020
def test_mcts_algorithm():
    """ MCTS plays against itself and tries to catch guaranteed wins

     Use the oracle in the while loop. Calculates statistics based on how well
     it performs at playing optimally once a guaranteed win is detected by the
     oracle.
    """

    # Set parameter values and initialize counters
    a0 = -100000
    b0 = 100000
    n_games = 40
    n_wins = 0
    n_wins_opt = 0
    n_def_wins = 0

    for i in range(n_games):
        # Generate an empty board
        arr_board = cm.initialize_game_state()
        # Convert board to bitmap
        player = cm.PLAYER1
        bit_b, bit_m = cm.board_to_bitmap(arr_board, player)
        # Calculate the board shape
        bd_shp = arr_board.shape
        # Initialize the board state variable
        bd_state = cm.check_end_state(bit_b, bit_m, bd_shp)
        # Initialize a list of moves
        mv_list = []
        # Initialize counters
        mv_cnt = 0
        num_mvs = 0
        def_win = False

        while bd_state == cm.GameState.STILL_PLAYING:
            # Generate an action using MCTS
            action, _ = generate_move(arr_board.copy(), player, None)
            # Update the list of moves
            mv_list.append(action)
            # Apply the action to both boards
            cm.apply_action(arr_board, action, player)
            bit_b, bit_m = cm.apply_action_cp(bit_b, bit_m, action, bd_shp)
            # Switch to the next player
            player = cm.BoardPiece(player % 2 + 1)

            # Check for guaranteed win, if none detected, continue playing
            if not def_win:
                score, depth = alpha_beta_oracle(bit_b, bit_m, True, a0, b0,
                                                 bd_shp, 0)
                # If a win is guaranteed, determine when it should occur
                if score > 50 and abs(score) < 200:
                    print('Score returned is {}'.format(score))
                    num_mvs = depth
                    n_def_wins += 1
                    def_win = True
                    print(cm.pretty_print_board(arr_board))
                    print('Last move by player {}, in column {}, player {} '
                          'should win in {} move(s) at most'.format(
                              player % 2 + 1, action, player, num_mvs))
            # Once a win is detected, check whether MCTS finds it optimally
            else:
                mv_cnt += 1
                print(cm.pretty_print_board(arr_board))
                bd_state = cm.check_end_state(bit_b ^ bit_m, bit_m, bd_shp)
                if bd_state == cm.GameState.IS_WIN:
                    print(mv_list)
                    print('Player {} won in {} move(s)'.format(
                        player % 2 + 1, mv_cnt))
                    n_wins += 1
                    if mv_cnt <= num_mvs:
                        n_wins_opt += 1
                    break

            # Check the game state
            bd_state = cm.check_end_state(bit_b, bit_m, bd_shp)

    # Print the number of wins and how many were optimal
    print('The MCTS algorithm clinched {:4.1f}% of its guaranteed wins, '
          'and won in an optimal number of moves {}% of the time'.format(
              100 * (n_wins / n_def_wins), 100 * (n_wins_opt / n_wins)))