def verbose_callback(game_state, action, active_player, active_idx, match_id, time_taken):
    if game_state.ply_count % 2 == 0 or game_state.terminal_test():  # print every other move, plus endgame
        summary = "\nmatch: {} | move: {} | {:.2f}s | {}({}) => {}".format(
            match_id, game_state.ply_count, time_taken, active_player.__class__.__name__, active_idx, DebugState.ind2xy(action)
        )
        board = str(DebugState.from_state(game_state))
        print(summary); logger.info(summary)
        print(board);   logger.info(board)
Exemplo n.º 2
0
def log_results(agents, scores, match_id, winner, start_time, args={}):
    if args.get('progress'):
        print('+' if winner == agents[0] else '-', end='', flush=True)

    logging = args.get('logging', 100)
    if (logging != 0 and match_id % logging == 0
            or match_id != 0 and match_id == args.get('rounds')):
        total_average = 100 * sum(scores[agents[0]]) / len(scores[agents[0]])
        running_average = 100 * sum(
            2 * (i + 0.5) * score
            for i, score in enumerate(scores[agents[0]])) / len(
                scores[agents[0]])**2
        message = " match_id: {:4d} | {:3.0f}s | {:3.0f}% -> {:3.0f}% | {} vs {}".format(
            match_id,
            time.perf_counter() - start_time,
            total_average,
            running_average,
            agents[0].name,
            agents[1].name,
        )
        print(message)
        logger.info(message)
def play_sync(
        agents: Tuple[Agent, Agent],
        game_state=None,  # defaults to Isolation()
        time_limit=TIME_LIMIT,
        match_id=0,
        debug=False,  # disables the signal timeout
        logging=True,
        verbose=False,  # prints an ASCII copy of the board after each turn
        exceptions=False,
        max_moves=0,  # end the game early after a set number of turns
        callbacks: List[Callable] = None,
        **kwargs):

    gc.collect(
        1
    )  # reduce chance of TimeoutError in call_with_timeout_ms() | gc.collect(2) is an expensive function
    agents = tuple(
        Agent(agent, agent.__class__.name
              ) if not isinstance(agent, Agent) else agent for agent in agents)
    players = tuple(a.agent_class(player_id=i) for i, a in enumerate(agents))
    game_state = game_state or Isolation()
    initial_state = game_state
    active_idx = 0
    winner = None
    loser = None
    status = Status.NORMAL
    game_history = []
    callbacks = copy(callbacks) or []

    if logging: logger.info(GAME_INFO.format(initial_state, *agents))
    while not game_state.terminal_test():
        if max_moves and game_state.ply_count >= max_moves: break
        turn_start = time.perf_counter()
        active_idx = game_state.player()
        active_player = players[active_idx]
        winner, loser = agents[1 - active_idx], agents[
            active_idx]  # any problems during get_action means the active player loses

        action = None
        active_player.queue = LifoQueue()  # we don't need a TimeoutQueue here
        try:
            if time_limit == 0 or debug:
                active_player.get_action(game_state)
                action = active_player.queue.get(
                    block=False)  # raises Empty if agent did not respond
            else:
                # increment timeout 2x before throwing exception - MinimaxAgent occasionally takes longer than 150ms
                for i in [1, 2]:
                    exception = call_with_timeout_ms(i * time_limit,
                                                     active_player.get_action,
                                                     game_state)
                    if not active_player.queue.empty():
                        action = active_player.queue.get(
                            block=False
                        )  # raises Empty if agent did not respond
                        break  # accept answer generated after minimum timeout
                if exceptions and action is None and exception == TimeoutError:
                    print(active_player)
                    raise TimeoutError
        except KeyboardInterrupt:
            raise KeyboardInterrupt
        except Exception as err:
            status = Status.EXCEPTION
            if exceptions:
                logger.error(
                    ERR_INFO.format(err, initial_state, agents[0], agents[1],
                                    game_state, game_history))
                traceback.print_exception(type(err), err, err.__traceback__)
            break
        finally:
            if time_limit and not debug:
                signal.signal(signal.SIGPROF,
                              signal.SIG_IGN)  # Unregister the timeout signal

        if action not in game_state.actions():
            status = Status.INVALID_MOVE
            if exceptions:
                print(
                    ERR_INFO.format('INVALID_MOVE', initial_state, agents[0],
                                    agents[1], game_state, game_history))
                logger.error(
                    ERR_INFO.format('INVALID_MOVE', initial_state, agents[0],
                                    agents[1], game_state, game_history))
            break

        time_taken = time.perf_counter() - turn_start
        game_state = game_state.result(action)
        game_history.append(action)

        # Callbacks can be used to hook in additional functionality after each turn, such as verbose rendering
        # BUGFIX: don't modify callbacks, else the board position will be repeated multiple times per turn
        turn_callbacks = list(callbacks) if isinstance(callbacks,
                                                       (tuple, list,
                                                        set)) else [callbacks]
        if verbose: turn_callbacks = [verbose_callback] + callbacks
        for callback in turn_callbacks:
            if not callable(callback): continue
            callback(game_state=game_state,
                     action=action,
                     active_player=active_player,
                     active_idx=active_idx,
                     match_id=match_id,
                     time_taken=time_taken)
    else:
        status = Status.GAME_OVER
        if game_state.utility(active_idx) > 0:
            winner, loser = loser, winner  # swap winner/loser if active player won

    if logging:
        logger.info(
            RESULT_INFO.format(status, game_state, game_history, winner,
                               loser))
    return winner, game_history, match_id