Пример #1
0
def test_apply_player_action_ab():
    test_board1 = cm.initialize_game_state()
    test_board2 = cm.initialize_game_state()
    player_map, mask_map = cm.board_to_bitmap(test_board1, cm.PLAYER1)
    n_rows = test_board1.shape[0]

    moves = np.array([3, 4, 3, 2, 2, 2, 2, 4, 1, 3, 4, 1, 0, 5])
    player = cm.PLAYER1
    for mv in moves:
        print('\n')
        cm.apply_player_action(test_board2, mv, player)
        player = cm.BoardPiece(player % 2 + 1)
        test2_map = cm.board_to_bitmap(test_board2, player)[1]
        print(bin(test2_map))
        player_map, mask_map = cm.apply_player_action_ab(
            player_map, mask_map, mv, n_rows)
        print(bin(mask_map))

    test_board1 = cm.initialize_game_state()
    player_map, mask_map = cm.board_to_bitmap(test_board1, cm.PLAYER1)
    n_rows = test_board1.shape[0]
    moves = np.array([3, 3, 3, 3, 3, 3, 3])
    for mv in moves:
        print('\n')
        player_map, mask_map = cm.apply_player_action_ab(
            player_map, mask_map, mv, n_rows)
        print(bin(mask_map))
Пример #2
0
def test_check_end_state():

    test_board = cm.initialize_game_state()
    test_board = np.ones(test_board.shape) * 3
    board_map, board_mask = cm.board_to_bitmap(test_board, cm.PLAYER1)
    empty_board = mpz('0' * test_board.size)
    board_map = mpz(board_map)
    board_mask = mpz(board_mask)
    assert cm.check_end_state(board_map, board_mask, empty_board,
                              test_board.shape) == cm.GameState.IS_DRAW

    test_board[0, 1] = cm.PLAYER1
    test_board[1, 2] = cm.PLAYER1
    test_board[2, 3] = cm.PLAYER1
    test_board[3, 4] = cm.PLAYER1
    board_map, board_mask = cm.board_to_bitmap(test_board, cm.PLAYER1)
    empty_board = mpz('0' * test_board.size)
    board_map = mpz(board_map)
    board_mask = mpz(board_mask)
    assert cm.check_end_state(board_map, board_mask, empty_board,
                              test_board.shape) == cm.GameState.IS_WIN

    board_map, board_mask = cm.board_to_bitmap(test_board, cm.PLAYER1)
    empty_board = mpz('0' * test_board.size)
    board_map = mpz(board_map)
    board_mask = mpz(board_mask)
    assert cm.check_end_state(board_map, board_mask, empty_board,
                              test_board.shape) == cm.GameState.IS_WIN
Пример #3
0
def generate_move_mcts(
    board: Board, player: BoardPiece, saved_state: Optional[SavedState]
) -> Tuple[PlayerAction, Optional[SavedState]]:
    """
    Agent selects a move based on a minimax depth first search, with
    alpha-beta pruning.

    :param board: 2d array representing current state of the game
    :param player: the player who made the last move (active player)
    :param saved_state: ???

    :return: the agent's selected move
    """

    # TODO: return chosen action subtree using saved_state, to improve
    #  performance
    # Calculate the board shape
    bd_shp = board.shape
    # If the board is empty, play in the center column
    if np.all(board == NO_PLAYER):
        action = np.floor(np.median(np.arange(bd_shp[1])))
        return PlayerAction(action), saved_state

    # Convert the board to bitmaps and define the max_player board
    max_board, mask_board = board_to_bitmap(board, player)
    # Create a root node
    root_mcts = Connect4Node(max_board, mask_board, bd_shp, -1, True)
    # Call MCTS
    action = mcts(root_mcts)

    return PlayerAction(action), saved_state
Пример #4
0
def generate_move_alpha_beta(
    board: Board, player: BoardPiece, saved_state: Optional[SavedState]
) -> Tuple[PlayerAction, Optional[SavedState]]:
    """
    Agent selects a move based on a minimax depth first search, with
    alpha-beta pruning.

    :param board: 2d array representing current state of the game
    :param player: the player who made the last move (active player)
    :param saved_state: ???

    :return: the agent's selected move
    """

    # If the board is empty, play in the center column
    if np.all(board == NO_PLAYER):
        action = np.floor(np.median(np.arange(board.shape[1])))
        return PlayerAction(action), saved_state

    # Convert the board to bitmaps and define the min_player board
    max_board, mask_board = board_to_bitmap(board, player)

    # Call alpha_beta
    alpha0 = -100000
    beta0 = 100000
    score, action = alpha_beta(max_board, mask_board, True, 0, alpha0, beta0,
                               board.shape)

    return PlayerAction(action), saved_state
Пример #5
0
def heuristic_solver_bits(board: Board,
                          player: BoardPiece,
                          max_player: bool = True):
    """

    """

    # Convert the boards to bitmaps and define the min_player board
    max_board, mask_board = board_to_bitmap(board, player)
    min_board = max_board ^ mask_board
    empty_board = ~mask_board

    # Convert bitmaps to mpz objects
    max_board = mpz(max_board)
    min_board = mpz(min_board)
    empty_board = mpz(empty_board)

    # Initialize the score and point values
    score = 0
    # Define the shift constants
    b_cols = board.shape[1]
    # Shift order: horizontal, vertical, \, /
    shift_list = [1, b_cols + 1, b_cols, b_cols + 2]

    # Accumulate score for max_player position
    for shift in shift_list:
        score += bit_solver(shift, max_board, empty_board)
    # Reduce score for min_player position
    for shift in shift_list:
        score -= bit_solver(shift, min_board, empty_board)

    if max_player:
        return GameScore(score)
    else:
        return GameScore(-score)
Пример #6
0
def generate_full_board(player, empty_spaces=0):
    # Generate an empty board
    arr_board = cm.initialize_game_state()
    # Convert board to bitmap
    bit_board, bit_mask = cm.board_to_bitmap(arr_board, player)
    # Calculate the board shape
    bd_shp = arr_board.shape

    # While the board is not full, continue placing pieces
    while popcount(bit_mask) != bd_shp[0] * bd_shp[1] - empty_spaces:
        # Select a random move in a column that is not full
        move = -1
        while not (0 <= move < bd_shp[1]):
            move = np.random.choice(bd_shp[1])
            try:
                move = cm.PlayerAction(move)
                cm.top_row(arr_board, move)
            except IndexError:
                move = -1

        # Apply the move to both boards
        cm.apply_action(arr_board, move, player)
        bit_board, bit_mask = cm.apply_action_cp(bit_board, bit_mask, move,
                                                 bd_shp)
        # Switch to the next player
        player = cm.BoardPiece(player % 2 + 1)

    return arr_board, bit_board, bit_mask, player
Пример #7
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)
Пример #8
0
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)))
Пример #9
0
def test_alpha_beta_oracle():
    # Generate an empty board
    arr_board = cm.initialize_game_state()
    # Convert board to bitmap
    player = cm.PLAYER1
    bit_board, bit_mask = cm.board_to_bitmap(arr_board, player)
    # Calculate the board shape
    bd_shp = arr_board.shape
    a0 = -100000
    b0 = 100000
    # Define a list of moves
    move_list = [3, 3, 4, 4, 5, 5]
    for mv in move_list[:-2]:
        # Apply the move to both boards
        cm.apply_action(arr_board, mv, player)
        bit_board, bit_mask = cm.apply_action_cp(bit_board, bit_mask, mv,
                                                 bd_shp)
        # Switch to the next player
        player = cm.BoardPiece(player % 2 + 1)
    print(cm.pretty_print_board(arr_board))
    score, depth = alpha_beta_oracle(bit_board, bit_mask, True, a0, b0, bd_shp,
                                     0)
    print('Player {} should win in {} moves.'.format(
        ('X' if player == cm.BoardPiece(1) else 'O'), depth))
    assert depth == 3

    # Apply next move to both boards
    cm.apply_action(arr_board, move_list[-2], player)
    bit_board, bit_mask = cm.apply_action_cp(bit_board, bit_mask,
                                             move_list[-2], bd_shp)
    # Switch to the next player
    player = cm.BoardPiece(player % 2 + 1)
    print(cm.pretty_print_board(arr_board))
    score, depth = alpha_beta_oracle(bit_board, bit_mask, True, a0, b0, bd_shp,
                                     0)
    print('Player {} should lose in {} moves.'.format(
        ('X' if player == cm.BoardPiece(1) else 'O'), depth))
    assert depth == 2

    # Apply next move to both boards
    cm.apply_action(arr_board, move_list[-1], player)
    bit_board, bit_mask = cm.apply_action_cp(bit_board, bit_mask,
                                             move_list[-1], bd_shp)
    # Switch to the next player
    player = cm.BoardPiece(player % 2 + 1)
    print(cm.pretty_print_board(arr_board))
    score, depth = alpha_beta_oracle(bit_board, bit_mask, True, a0, b0, bd_shp,
                                     0)
    print('Player {} should win in {} move.'.format(
        ('X' if player == cm.BoardPiece(1) else 'O'), depth))
    assert depth == 1
    print('\n##########################################################\n')

    # Generate an empty board
    arr_board = cm.initialize_game_state()
    # Convert board to bitmap
    player = cm.PLAYER1
    bit_board, bit_mask = cm.board_to_bitmap(arr_board, player)
    # Full game
    # move_list = [3, 2, 3, 3, 3, 2, 2, 2, 5, 4, 0, 4, 4, 4, 1, 1, 5, 2, 6]
    move_list = [3, 2, 3, 3, 3, 2, 2, 2, 5, 4, 0, 4, 4, 4]
    for mv in move_list[:-1]:
        # Apply the move to both boards
        cm.apply_action(arr_board, mv, player)
        bit_board, bit_mask = cm.apply_action_cp(bit_board, bit_mask, mv,
                                                 bd_shp)
        # Switch to the next player
        player = cm.BoardPiece(player % 2 + 1)
    print(cm.pretty_print_board(arr_board))
    action, _ = generate_move(arr_board.copy(), player, None)
    print('MCTS plays in column {}'.format(action))
    try:
        assert (action == 2 or action == 5)
    except AssertionError:
        print('NOTE: MCTS doesn\'t block this win unless it is given '
              'over 5s to search. It should play in column 2 or 5.')

    # Apply next move to both boards
    cm.apply_action(arr_board, move_list[-1], player)
    bit_board, bit_mask = cm.apply_action_cp(bit_board, bit_mask,
                                             move_list[-1], bd_shp)
    # Switch to the next player
    player = cm.BoardPiece(player % 2 + 1)
    print(cm.pretty_print_board(arr_board))
    score, depth = alpha_beta_oracle(bit_board, bit_mask, True, a0, b0, bd_shp,
                                     0)
    print('Player {} should win in {} move.'.format(
        ('X' if player == cm.BoardPiece(1) else 'O'), depth))
    assert depth == 5
    print('\n##########################################################\n')

    # Test other hard coded boards
    move_list_list = [[3, 4, 3, 3, 1, 0, 4, 4, 1, 1, 3, 0, 0, 4, 5, 5],
                      [
                          3, 3, 4, 5, 1, 2, 4, 4, 3, 4, 3, 4, 4, 3, 1, 1, 0, 5,
                          1, 5, 5, 1, 0, 0
                      ]]
    # Full games
    # [3, 4, 3, 3, 1, 0, 4, 4, 1, 1, 3, 0, 0, 4, 5, 5, 4, 6, 2, 2, 2]
    # [3, 4, 3, 3, 1, 0, 4, 4, 1, 1, 3, 0, 0, 4, 4, 1, 1, 5, 5, 5, 3,
    #  5, 5, 4, 5, 0, 2, 0, 2]
    for move_list in move_list_list:
        # Generate an empty board
        arr_board = cm.initialize_game_state()
        # Convert board to bitmap
        player = cm.PLAYER1
        bit_board, bit_mask = cm.board_to_bitmap(arr_board, player)

        for mv in move_list:
            # Apply the move to both boards
            cm.apply_action(arr_board, mv, player)
            bit_board, bit_mask = cm.apply_action_cp(bit_board, bit_mask, mv,
                                                     bd_shp)
            # Switch to the next player
            player = cm.BoardPiece(player % 2 + 1)

        # Print the current board state
        print(cm.pretty_print_board(arr_board))
        # Check for guaranteed wins
        score, depth = alpha_beta_oracle(bit_board, bit_mask, True, a0, b0,
                                         bd_shp, 0)
        print('It is Player {}\'s turn. They should win in {} moves.'.format(
            ('X' if player == cm.BoardPiece(1) else 'O'), depth))
        action, _ = generate_move(arr_board.copy(), player, None)
        print('Player {} plays in column {}'.format(
            ('X' if player == cm.BoardPiece(1) else 'O'), action))
        print('\n##########################################################\n')
Пример #10
0
def test_heuristic_solver():
    max_depth = 0
    # test_board = cm.initialize_game_state()
    # test_board[0, 1:4] = cm.PLAYER1
    # test_board[1, 3] = cm.PLAYER2
    # board_str = cm.pretty_print_board(test_board)
    # print('')
    # print(board_str)
    # assert (agm.heuristic_solver_bits(test_board, cm.PLAYER1, True) ==
    #         agm.heuristic_solver(test_board, cm.PLAYER1, True))
    #
    # test_board = cm.initialize_game_state()
    # test_board[0, 2:5] = cm.PLAYER1
    # test_board[1, 2:5] = cm.PLAYER2
    # board_str = cm.pretty_print_board(test_board)
    # print('')
    # print(board_str)
    # assert (agm.heuristic_solver_bits(test_board, cm.PLAYER2, False) ==
    #         agm.heuristic_solver(test_board, cm.PLAYER1, True))

    # test_board = cm.initialize_game_state()
    # test_board[0, 2:4] = cm.PLAYER1
    # test_board[1, 2] = cm.PLAYER1
    # test_board[0, 1] = cm.PLAYER2
    # test_board[2, 2] = cm.PLAYER2
    # test_board[1, 3] = cm.PLAYER2
    # board_str = cm.pretty_print_board(test_board)
    # print('')
    # print(board_str)
    # # assert agm.heuristic_solver(test_board, cm.PLAYER1, True) == 0
    # test_board_cp = test_board.copy()
    # assert agm.alpha_beta(test_board_cp, cm.PLAYER1, True, max_depth,
    #                       -1000, 1000)[1] == 4
    #
    # test_board = cm.initialize_game_state()
    # test_board[0, 3:6] = cm.PLAYER1
    # test_board[1, 3] = cm.PLAYER2
    # test_board[0, 2] = cm.PLAYER2
    # board_str = cm.pretty_print_board(test_board)
    # print('')
    # print(board_str)
    # # assert agm.heuristic_solver(test_board, cm.PLAYER2, True) == -3
    # test_board_cp = test_board.copy()
    # assert agm.alpha_beta(test_board_cp, cm.PLAYER2, True, max_depth,
    #                       -1000, 1000)[1] == 6
    #
    # test_board = cm.initialize_game_state()
    # test_board[:3, 1] = cm.PLAYER1
    # test_board[0, 3] = cm.PLAYER1
    # test_board[3, 1] = cm.PLAYER2
    # test_board[0, 2] = cm.PLAYER2
    # test_board[1, 3] = cm.PLAYER2
    # test_board[0, 4] = cm.PLAYER2
    # board_str = cm.pretty_print_board(test_board)
    # print('')
    # print(board_str)
    # # assert agm.heuristic_solver(test_board, cm.PLAYER2, True) == 5
    # test_board_cp = test_board.copy()
    # assert not agm.alpha_beta(test_board_cp, cm.PLAYER2, True, max_depth,
    #                           -1000, 1000)[1] == 2
    #
    # test_board = cm.initialize_game_state()
    # moves = np.array([3, 1, 2, 2, 5, 4, 4, 1, 2, 4, 2, 4, 2, 2, 3])
    # player = cm.PLAYER1
    # for mv in moves:
    #     cm.apply_player_action(test_board, mv, player, False)
    #     player = cm.switch_player(player)
    #
    # board_str = cm.pretty_print_board(test_board)
    # print('')
    # print(board_str)
    # test_board_cp = test_board.copy()
    # assert agm.alpha_beta(test_board_cp, cm.PLAYER2, True, max_depth,
    #                       -1000, 1000)[1] == 3
    #
    # test_board = cm.initialize_game_state()
    # moves = np.array([3, 1, 4, 5, 3, 3, 4, 5, 4, 4, 3, 5, 5, 5, 4, 6, 3, 3,
    #                   5, 0, 1, 1, 1, 1, 1, 0, 4, 6, 0, 6])
    # player = cm.PLAYER1
    # for mv in moves:
    #     cm.apply_player_action(test_board, mv, player, False)
    #     player = cm.switch_player(player)
    #
    # board_str = cm.pretty_print_board(test_board)
    # print('')
    # print(board_str)
    # test_board_cp = test_board.copy()
    # assert agm.alpha_beta(test_board_cp, cm.PLAYER1, True, max_depth,
    #                       -1000, 1000)[1] == 6
    #
    # test_board = cm.initialize_game_state()
    # moves = np.array([3, 1, 2, 3, 2, 4, 2, 2, 3, 3, 2, 3, 2, 0, 3, 0, 0,
    #                   5, 0, 6, 0, 0, 6, 5, 5, 5, 5, 5])
    # player = cm.PLAYER1
    # for mv in moves:
    #     cm.apply_player_action(test_board, mv, player, False)
    #     player = cm.switch_player(player)
    #
    # board_str = cm.pretty_print_board(test_board)
    # print('')
    # print(board_str)
    # test_board_cp = test_board.copy()
    # assert agm.alpha_beta(test_board_cp, cm.PLAYER1, True, max_depth,
    #                       -1000, 1000)[1] == 6
    #
    # test_board = cm.initialize_game_state()
    # moves = np.array([3, 3, 2, 4, 0, 1, 0, 3, 0, 0, 2, 3, 3, 2, 2, 2, 0, 6,
    #                   2, 5, 6, 5, 5, 5, 3, 5])
    # player = cm.PLAYER1
    # for mv in moves:
    #     cm.apply_player_action(test_board, mv, player, False)
    #     player = cm.switch_player(player)
    #
    # board_str = cm.pretty_print_board(test_board)
    # print('')
    # print(board_str)
    # test_board_cp = test_board.copy()
    # assert agm.alpha_beta(test_board_cp, cm.PLAYER1, True, max_depth,
    #                       -1000, 1000)[1] != 4

    test_board = cm.initialize_game_state()
    # test_board[0, 2:5] = cm.PLAYER1
    test_board[0, 2:4] = cm.PLAYER1
    test_board[0, 1] = cm.PLAYER2
    # test_board[0, 4] = cm.PLAYER2
    test_board[0, 5] = cm.PLAYER1
    # test_board[0, 5] = cm.PLAYER1
    # test_board[0, 2] = cm.PLAYER1
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    test_board_cp = test_board.copy()
    board_map, board_mask = cm.board_to_bitmap(test_board_cp, cm.PLAYER1)
    print(agm.heuristic_solver_bits(board_map, board_mask,
                                    test_board_cp.shape[0], True))
Пример #11
0
def test_connect_four_bits():
    # Test a diagonal
    test_board = cm.initialize_game_state()
    test_board[0, 3] = cm.PLAYER1
    test_board[1, 4] = cm.PLAYER1
    test_board[2, 5] = cm.PLAYER1
    test_board[3, 6] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    # Test the same diagonal for the other player
    test_board = cm.initialize_game_state()
    test_board[0, 3] = cm.PLAYER2
    test_board[1, 4] = cm.PLAYER2
    test_board[2, 5] = cm.PLAYER2
    test_board[3, 6] = cm.PLAYER2
    player_map, mask_board = cm.board_to_bitmap(test_board, cm.PLAYER1)
    player_map2 = player_map ^ mask_board
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map2, test_board.shape[0])

    # Rotate the diagonal
    test_board = cm.initialize_game_state()
    test_board[0, 6] = cm.PLAYER1
    test_board[1, 5] = cm.PLAYER1
    test_board[2, 4] = cm.PLAYER1
    test_board[3, 3] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    # Test a row at the top
    test_board = cm.initialize_game_state()
    test_board[5, 3] = cm.PLAYER1
    test_board[5, 4] = cm.PLAYER1
    test_board[5, 5] = cm.PLAYER1
    test_board[5, 6] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    # Test a row at the bottom
    test_board = cm.initialize_game_state()
    test_board[0, 0] = cm.PLAYER1
    test_board[0, 1] = cm.PLAYER1
    test_board[0, 2] = cm.PLAYER1
    test_board[0, 3] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    # Test a column on the left
    test_board = cm.initialize_game_state()
    test_board[2, 0] = cm.PLAYER1
    test_board[3, 0] = cm.PLAYER1
    test_board[4, 0] = cm.PLAYER1
    test_board[5, 0] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    # Test a column on the right
    test_board = cm.initialize_game_state()
    test_board[0, 6] = cm.PLAYER1
    test_board[1, 6] = cm.PLAYER1
    test_board[2, 6] = cm.PLAYER1
    test_board[3, 6] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    # Test some things in the middle
    test_board = cm.initialize_game_state()
    test_board[2, 5] = cm.PLAYER1
    test_board[3, 4] = cm.PLAYER1
    test_board[4, 3] = cm.PLAYER1
    test_board[5, 2] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    test_board = cm.initialize_game_state()
    test_board[0, 2] = cm.PLAYER1
    test_board[1, 3] = cm.PLAYER1
    test_board[2, 4] = cm.PLAYER1
    test_board[3, 5] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    # Test the last_action option for column 6
    test_board = cm.initialize_game_state()
    test_board[0, 3] = cm.PLAYER1
    test_board[1, 4] = cm.PLAYER1
    test_board[2, 5] = cm.PLAYER1
    test_board[3, 6] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    # Test the last_action option for column 5
    test_board = cm.initialize_game_state()
    test_board[0, 2] = cm.PLAYER1
    test_board[1, 3] = cm.PLAYER1
    test_board[2, 4] = cm.PLAYER1
    test_board[3, 5] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    # Test the last_action option for column 1
    test_board = cm.initialize_game_state()
    test_board[0, 1] = cm.PLAYER1
    test_board[1, 1] = cm.PLAYER1
    test_board[2, 1] = cm.PLAYER1
    test_board[3, 1] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    # Test the last_action option for column 2 diagonal pattern
    test_board = cm.initialize_game_state()
    test_board[0, 1] = cm.PLAYER1
    test_board[1, 2] = cm.PLAYER1
    test_board[2, 3] = cm.PLAYER1
    test_board[3, 4] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert cm.connect_four(player_map, test_board.shape[0])

    # Test for overflow issues - horizontal
    test_board = cm.initialize_game_state()
    test_board[0, 4] = cm.PLAYER1
    test_board[0, 5] = cm.PLAYER1
    test_board[0, 6] = cm.PLAYER1
    test_board[1, 0] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert not cm.connect_four(player_map, test_board.shape[0])

    # Test for overflow issues - diagonal
    test_board = cm.initialize_game_state()
    test_board[0, 2] = cm.PLAYER1
    test_board[1, 1] = cm.PLAYER1
    test_board[2, 0] = cm.PLAYER1
    test_board[2, 6] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert not cm.connect_four(player_map, test_board.shape[0])

    # Test for overflow issues - vertical
    test_board = cm.initialize_game_state()
    test_board[3, 1] = cm.PLAYER1
    test_board[3, 2] = cm.PLAYER1
    test_board[3, 3] = cm.PLAYER1
    test_board[4, 0] = cm.PLAYER1
    test_board[2, 0] = cm.PLAYER1
    player_map = cm.board_to_bitmap(test_board, cm.PLAYER1)[0]
    board_str = cm.pretty_print_board(test_board)
    print('')
    print(board_str)
    assert not cm.connect_four(player_map, test_board.shape[0])