106 lines
3.3 KiB
Python
106 lines
3.3 KiB
Python
"""
|
|
This module hosts the PyClient class. You can use this class to simulate
|
|
games among mulltiple agents.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from agents import Agent
|
|
from .games import Game
|
|
from .replay import GameReplay, Turn
|
|
from dataclasses import dataclass
|
|
from typing import Any, Generator
|
|
|
|
@dataclass(frozen=True)
|
|
class PyClient:
|
|
"""
|
|
Host games among multiple agents.
|
|
"""
|
|
|
|
debug : bool
|
|
|
|
def gen_game(
|
|
self,
|
|
players : list[Agent],
|
|
start : Game,
|
|
max_turns : int | None = None,
|
|
) -> Generator[Turn, None, None]:
|
|
"""
|
|
Generate a game by polling the players.
|
|
|
|
:param players: All players that wish to participate.
|
|
:type players: list[Agent]
|
|
:param start: The start state of the game.
|
|
:type start: Game
|
|
:param max_turns: The maximum number of turns a game may take
|
|
:type max_turns: int
|
|
:return: A generator that yields turns.
|
|
:rtype: Generator[Turn, None, None]
|
|
:raises ValueError: The game is taking too long.
|
|
"""
|
|
current_state = start
|
|
turns_taken = 0
|
|
|
|
while current_state.winner() is None:
|
|
if max_turns is not None and turns_taken >= max_turns:
|
|
raise ValueError(
|
|
f"Turn limit exceeded: played for {turns_taken} turns and still needed more."
|
|
)
|
|
|
|
turns_taken += 1
|
|
player = current_state.player_to_move()
|
|
|
|
if len(players) < player:
|
|
# Player not found! Make a default move.
|
|
current_state = current_state.move_default()
|
|
|
|
yield Turn(action={}, player=player, state=current_state)
|
|
|
|
else:
|
|
agent = players[player - 1]
|
|
on_move = agent.get_action(
|
|
game=current_state.game_name(),
|
|
action=current_state.action_name(),
|
|
)
|
|
|
|
# Calculate move
|
|
if on_move is None:
|
|
payload = {}
|
|
else:
|
|
try:
|
|
payload = on_move(current_state.as_seen_by(player=player))
|
|
except Exception:
|
|
payload = {}
|
|
|
|
current_state = current_state.move(payload=payload)
|
|
|
|
yield Turn(action=payload, player=player, state=current_state)
|
|
|
|
def play_game(
|
|
self,
|
|
players : list[Agent],
|
|
start : Game,
|
|
max_turns : int | None = None,
|
|
) -> GameReplay:
|
|
"""
|
|
Generate a game by polling the players. Collect all moves in a
|
|
summary.
|
|
|
|
:param players: All players that wish to participate.
|
|
:type players: list[Agent]
|
|
:param start: The start state of the game.
|
|
:type start: Game
|
|
:param max_turns: The maximum number of turns a game may take
|
|
:type max_turns: int
|
|
:return: Summary describing how the game went.
|
|
:rtype: GameReplay
|
|
:raises ValueError: The game is taking too long.
|
|
"""
|
|
return GameReplay(
|
|
game_name=start.game_name(),
|
|
start=start,
|
|
turns=list(self.gen_game(
|
|
players=players, start=start, max_turns=max_turns
|
|
)),
|
|
)
|