Exemple #1
0
def test_connected_four_oracle(empty_board: np.ndarray):
    from connectn.game import check_end_state, other_player, apply_player_action
    from connectn.game import PlayerAction, NO_PLAYER
    from connectn.game import connected_four_convolve, connected_four, connected_four_generators

    for game_n in range(1000):
        board = empty_board.copy()
        curr_player = PLAYER1
        end_state = check_end_state(board, curr_player)
        move_n = 0
        while end_state == GameStatus.STILL_PLAYING:
            move_n += 1
            curr_player = other_player(curr_player)
            moves = np.array([
                col for col in np.arange(PlayerAction(board.shape[1]))
                if board[PlayerAction(-1), col] == NO_PLAYER
            ])
            move = np.random.choice(moves, 1)[0]
            apply_player_action(board, move, curr_player)
            end_state = check_end_state(board, curr_player)

            conn_4_a = connected_four_convolve(board, curr_player)
            conn_4_b = connected_four(board, curr_player, move, True)
            conn_4_b2 = connected_four(board, curr_player, move, False)
            conn_4_c = connected_four(board, curr_player, None, True)
            conn_4_c2 = connected_four(board, curr_player, None, False)
            conn_4_u = connected_four_generators(board, curr_player, move)
            assert conn_4_a == conn_4_b
            assert conn_4_b == conn_4_b2
            assert conn_4_b2 == conn_4_c
            assert conn_4_c == conn_4_c2
            assert conn_4_c2 == conn_4_u
Exemple #2
0
def test_check_end_state_cols(empty_board):
    from connectn.game import check_end_state, other_player

    for i in range(CONNECT_N - 1):
        for col in range(empty_board.shape[1]):
            for row in range(empty_board.shape[0] - CONNECT_N + i + 1):
                for player in (PLAYER1, PLAYER2):
                    b0 = empty_board.copy()
                    b0[row:row + CONNECT_N - i, col] = player
                    b0[:row, col] = other_player(player)
                    if i == 0:
                        assert check_end_state(b0, player) == GameStatus.IS_WIN
                    else:
                        assert check_end_state(
                            b0, player) == GameStatus.STILL_PLAYING
Exemple #3
0
def test_check_end_state_diagonal(empty_board: np.ndarray):
    from connectn.game import check_end_state

    for i in range(CONNECT_N):
        n_diagonal = np.diag(np.ones(CONNECT_N - i, dtype=empty_board.dtype))
        for n_conn in (n_diagonal, n_diagonal[:, ::-1]):
            for row in range(empty_board.shape[0] - CONNECT_N + i + 1):
                for col in range(empty_board.shape[1] - CONNECT_N + i + 1):
                    for player in (PLAYER1, PLAYER2):
                        b0 = empty_board.copy()
                        b0[row:row + CONNECT_N - i,
                           col:col + CONNECT_N - i] = (player * n_conn)
                        if i == 0:
                            assert check_end_state(b0,
                                                   player) == GameStatus.IS_WIN
                        else:
                            assert check_end_state(
                                b0, player) == GameStatus.STILL_PLAYING
Exemple #4
0
def test_check_end_state_straight(empty_board: np.ndarray):
    from connectn.game import check_end_state
    from itertools import product

    dim = len(empty_board.shape)
    for player in (PLAYER1, PLAYER2):
        for n in range(CONNECT_N):
            for d in range(dim):
                for i in range(empty_board.shape[d] - CONNECT_N + n + 1):
                    remaining_indices = list(
                        range(empty_board.shape[k]) for k in range(dim)
                        if k != d)
                    for jk in product(*remaining_indices):
                        b0 = empty_board.copy()
                        indices = jk[:d] + (slice(
                            i, i + CONNECT_N - n), ) + jk[d:]
                        b0[indices] = player
                        if n == 0:
                            assert check_end_state(b0,
                                                   player) == GameStatus.IS_WIN
                        else:
                            assert check_end_state(
                                b0, player) == GameStatus.STILL_PLAYING
Exemple #5
0
def run_single_game(agent_1: str,
                    agent_2: str,
                    game_seed: Optional[int] = None) -> GameResult:
    """
    Likely has to be replaced by separate function runnable via the GridEngine
    """
    from connectn import IS_DEBUGGING
    from queue import Empty as EmptyQueue
    from connectn.game import initialize_game_state, other_player
    from connectn.game import valid_player_action, apply_player_action
    from connectn.game import check_end_state, pretty_print_board
    from connectn.game import PLAYER1, PLAYER2, GameStatus

    logger = logging.getLogger(__name__)
    logger.debug(f"Entered run_single_game for {agent_1} vs {agent_2}")
    rs = np.random.RandomState(game_seed)

    agent_modules = import_agents({})
    agent_names = (agent_1, agent_2)

    def get_name(_player: BoardPiece) -> str:
        return agent_names[_player - 1]

    states = {agent_name: None for agent_name in agent_names}

    winner = player = NO_PLAYER
    agent_name = agent_1
    results = {PLAYER1: AgentResult(agent_1), PLAYER2: AgentResult(agent_2)}
    gr = GameResult(results[PLAYER1], results[PLAYER2])

    gen_move = {}
    for player, agent_name in zip((PLAYER1, PLAYER2), agent_names):
        try:
            gen_move[agent_name]: cu.GenMove = getattr(
                agent_modules[agent_name], "generate_move")
        except AttributeError:
            results[player].stderr.append(
                "\nYou did not define generate_move at the package level")
            gr.winner = other_player(player)
            results[player].outcome = "FAIL"
            results[gr.winner].outcome = "WIN"
            return gr
        except KeyError as e:
            # If this occurs and it isn't for agent_fail, then something has gone terribly wrong.
            # Presumably one of the agents is not defined in users.py
            logger.exception("Something has gone terribly wrong")
            raise e

    game_state = initialize_game_state()
    for player, agent_name in zip((PLAYER1, PLAYER2), agent_names):
        try:
            init = getattr(agent_modules[agent_name], "initialize")
            init(game_state.copy(), player)
        except (Exception, AttributeError):
            pass

    loser_result = "LOSS"
    try:
        logger.info(f"Playing game between {agent_1} and {agent_2}")
        moves_q = mp.Manager().Queue()

        end_state = GameStatus.STILL_PLAYING
        playing = True
        action = PlayerAction(0)
        while playing:
            for player, agent_name in zip((PLAYER1, PLAYER2), agent_names):
                move_seed = rs.randint(2**32)
                results[player].seeds.append(move_seed)

                gma = GenMoveArgs(move_seed, game_state.copy(), player,
                                  states[agent_name])
                moves_q.put(gma)
                if IS_DEBUGGING:
                    generate_move_process(gen_move[agent_name], moves_q)
                else:
                    ap = mp.Process(
                        target=generate_move_process,
                        args=(gen_move[agent_name], moves_q),
                    )
                    t0 = time.time()
                    ap.start()
                    ap.join(cu.MOVE_TIME_MAX)
                    move_time = time.time() - t0
                    if ap.is_alive():
                        ap.terminate()
                        loser_result = "TIMEOUT"
                        msg = f"Agent {agent_name} timed out after {cu.MOVE_TIME_MAX} seconds ({move_time:.1f}s)."
                        raise AgentFailed(msg)

                try:
                    ret: Union[GenMoveSuccess,
                               GenMoveFailure] = moves_q.get(block=True,
                                                             timeout=60.0)
                except EmptyQueue:
                    logger.exception(
                        "Timed out waiting to get move result from queue")
                    raise

                results[player].stdout.append(ret.stdout)
                results[player].stderr.append(ret.stderr)

                if isinstance(ret, GenMoveFailure):
                    loser_result = "EXCEPTION"
                    error_msg = ret.error_msg
                    msg = f"Agent {agent_name} threw an exception:\n {error_msg}"
                    raise AgentFailed(msg)

                assert isinstance(ret, GenMoveSuccess)
                action = ret.action
                state_size = cu.get_size(ret.state)

                results[player].move_times.append(ret.move_time)
                results[player].state_size.append(state_size)

                if state_size > cu.STATE_MEMORY_MAX:
                    loser_result = "MAX_STATE_MEM"
                    msg = f"Agent {agent_name} used {cu.mib(state_size):.2f} MiB > {cu.mib(cu.STATE_MEMORY_MAX)} MiB"
                    raise AgentFailed(msg)

                if not np.issubdtype(type(action), np.integer):
                    loser_result = "NONINT_ACTION"
                    msg = f"Agent {agent_name} returned an invalid type of action {type(action)}"
                    raise AgentFailed(msg)

                action = PlayerAction(action)
                results[player].moves.append(action)
                if not valid_player_action(game_state, action):
                    loser_result = "INVALID_ACTION"
                    msg = f"Agent {agent_name} returned an invalid action {action}"
                    raise AgentFailed(msg)

                apply_player_action(game_state, action, player)
                end_state = check_end_state(game_state, player)
                playing = end_state == GameStatus.STILL_PLAYING
                states[agent_name] = ret.state
                if not playing:
                    break

        if end_state == GameStatus.IS_WIN:
            winner = player
            logger.info(
                f"Game finished, {get_name(player)} beat {get_name(other_player(player))} by playing column {action}."
            )
        elif end_state == GameStatus.IS_DRAW:
            winner = NO_PLAYER
            logger.info("Game finished, no winner")
        else:
            logger.info(
                "Something went wrong, game-play stopped before the end state."
            )

    except AgentFailed as err:
        logger.info(pretty_print_board(game_state))
        logger.info(f"Agent failed: {agent_name}")
        logger.info(err)
        winner = other_player(player)
        results[player].stderr.append(str(err))

    # fig = plt.figure()
    # fig.suptitle('Odds of win')
    # for i, (agent, saved_state) in enumerate(states.items()):
    #     ax = fig.add_subplot(2, 1, i+1)
    #     for odds in saved_state.odds.values():
    #         ax.plot(odds, ('r', 'b')[i])
    #     ax.set_title(agent)
    #     ax.set_ylim(0, 1)
    #
    # fig = plt.figure()
    # fig.suptitle('Odds of draw')
    # for i, (agent, saved_state) in enumerate(states.items()):
    #     ax = fig.add_subplot(2, 1, i+1)
    #     for odds in saved_state.draw.values():
    #         ax.plot(odds, ('r', 'b')[i])
    #     ax.set_title(agent)
    #     ax.set_ylim(0, 1)
    #
    # fig = plt.figure()
    # for i, (agent, saved_state) in enumerate(states.items()):
    #     ax = fig.add_subplot(2, 1, i+1)
    #     ax.plot(saved_state.nodes, label='Nodes')
    #     ax.plot(saved_state.visits, label='Visits')
    #     ax.set_title(agent)
    #     ax.legend()
    #
    # fig = plt.figure()
    # fig.suptitle('Time')
    # for i, (agent, saved_state) in enumerate(states.items()):
    #     ax = fig.add_subplot(2, 1, i+1)
    #     ax.plot(saved_state.time, label=f'{np.mean(saved_state.time):.2f}')
    #     ax.set_title(agent)
    #     ax.legend()
    #
    # plt.show()

    # for i, (agent, saved_state) in enumerate(states.items()):
    #     print(
    #     f'TIME {agent} mu:{np.mean(saved_state.time):.2f},
    #     med:{np.median(saved_state.time):.2f}, max:{np.max(saved_state.time):.2f}'
    #     )

    gr.winner = winner
    if winner == NO_PLAYER:
        results[PLAYER1].outcome = results[PLAYER2].outcome = "DRAW"
    else:
        results[PLAYER1 if winner == PLAYER1 else PLAYER2].outcome = "WIN"
        results[PLAYER2 if winner ==
                PLAYER1 else PLAYER1].outcome = loser_result

    logger.debug(f"Finished run_single_game for {agent_1} vs {agent_2}")

    return gr