pacai.core.isolation.none

  1import logging
  2import random
  3import typing
  4
  5import edq.util.time
  6
  7import pacai.core.action
  8import pacai.core.agent
  9import pacai.core.agentaction
 10import pacai.core.agentinfo
 11import pacai.core.gamestate
 12import pacai.core.isolation.isolator
 13
 14class NoneIsolator(pacai.core.isolation.isolator.AgentIsolator):
 15    """
 16    An isolator that does not do any isolation between the engine and agents.
 17    All agents will be run in the same thread (and therefore processes space).
 18    This is the simplest and fastest of all isolators, but offers the least control and protection.
 19    Agents cannot be timed out (since they run on the same thread).
 20    Agents can also access any memory, disk, or permissions that the core engine has access to.
 21    """
 22
 23    def __init__(self) -> None:
 24        self._agents: dict[int, pacai.core.agent.Agent] = {}
 25        """
 26        The agents that this isolator manages.
 27        These agents are held and ran in this thread's memory space.
 28        """
 29
 30    def init_agents(self, agent_infos: dict[int, pacai.core.agentinfo.AgentInfo]) -> None:
 31        self._agents = {}
 32        for (agent_index, agent_info) in agent_infos.items():
 33            self._agents[agent_index] = pacai.core.agent.load(agent_info)
 34
 35    def game_start(self,
 36            rng: random.Random,
 37            initial_state: pacai.core.gamestate.GameState,
 38            timeout: float,
 39            ) -> dict[int, pacai.core.agentaction.AgentActionRecord]:
 40        results = {}
 41        for (agent_index, agent) in self._agents.items():
 42            data = {
 43                'agent_index': agent_index,
 44                'suggested_seed': rng.randint(0, 2**64),
 45                'initial_state': initial_state,
 46            }
 47
 48            results[agent_index] = _call_agent_method(agent_index, agent.game_start_full, data)
 49
 50        return results
 51
 52    def game_complete(self,
 53            final_state: pacai.core.gamestate.GameState,
 54            timeout: float,
 55            ) -> dict[int, pacai.core.agentaction.AgentActionRecord]:
 56        results = {}
 57        for (agent_index, agent) in self._agents.items():
 58            data = {
 59                'final_state': final_state,
 60            }
 61
 62            results[agent_index] = _call_agent_method(agent_index, agent.game_complete_full, data)
 63
 64        return results
 65
 66    def get_action(self,
 67            state: pacai.core.gamestate.GameState,
 68            user_inputs: list[pacai.core.action.Action],
 69            timeout: float,
 70            ) -> pacai.core.agentaction.AgentActionRecord:
 71        agent = self._agents[state.agent_index]
 72        data = {
 73            'state': state,
 74            'user_inputs': user_inputs,
 75        }
 76
 77        return _call_agent_method(state.agent_index, agent.get_action_full, data)
 78
 79    def close(self) -> None:
 80        self._agents.clear()
 81
 82def _call_agent_method(
 83        agent_index: int,
 84        agent_method: typing.Callable[..., pacai.core.agentaction.AgentAction],
 85        agent_method_kwargs: dict[str, typing.Any],
 86        ) -> pacai.core.agentaction.AgentActionRecord:
 87    """ Call a method on the agent and do all the proper bookkeeping. """
 88
 89    crashed = False
 90    agent_action: pacai.core.agentaction.AgentAction | None = None
 91
 92    start_time = edq.util.time.Timestamp.now()
 93
 94    try:
 95        agent_action = agent_method(**agent_method_kwargs)
 96    except Exception as ex:
 97        crashed = True
 98        logging.warning("Agent %d crashed.", agent_index, exc_info = ex)
 99
100    end_time = edq.util.time.Timestamp.now()
101
102    return pacai.core.agentaction.AgentActionRecord(
103            agent_index = agent_index,
104            agent_action = agent_action,
105            duration = end_time.sub(start_time),
106            crashed = crashed)
class NoneIsolator(pacai.core.isolation.isolator.AgentIsolator):
15class NoneIsolator(pacai.core.isolation.isolator.AgentIsolator):
16    """
17    An isolator that does not do any isolation between the engine and agents.
18    All agents will be run in the same thread (and therefore processes space).
19    This is the simplest and fastest of all isolators, but offers the least control and protection.
20    Agents cannot be timed out (since they run on the same thread).
21    Agents can also access any memory, disk, or permissions that the core engine has access to.
22    """
23
24    def __init__(self) -> None:
25        self._agents: dict[int, pacai.core.agent.Agent] = {}
26        """
27        The agents that this isolator manages.
28        These agents are held and ran in this thread's memory space.
29        """
30
31    def init_agents(self, agent_infos: dict[int, pacai.core.agentinfo.AgentInfo]) -> None:
32        self._agents = {}
33        for (agent_index, agent_info) in agent_infos.items():
34            self._agents[agent_index] = pacai.core.agent.load(agent_info)
35
36    def game_start(self,
37            rng: random.Random,
38            initial_state: pacai.core.gamestate.GameState,
39            timeout: float,
40            ) -> dict[int, pacai.core.agentaction.AgentActionRecord]:
41        results = {}
42        for (agent_index, agent) in self._agents.items():
43            data = {
44                'agent_index': agent_index,
45                'suggested_seed': rng.randint(0, 2**64),
46                'initial_state': initial_state,
47            }
48
49            results[agent_index] = _call_agent_method(agent_index, agent.game_start_full, data)
50
51        return results
52
53    def game_complete(self,
54            final_state: pacai.core.gamestate.GameState,
55            timeout: float,
56            ) -> dict[int, pacai.core.agentaction.AgentActionRecord]:
57        results = {}
58        for (agent_index, agent) in self._agents.items():
59            data = {
60                'final_state': final_state,
61            }
62
63            results[agent_index] = _call_agent_method(agent_index, agent.game_complete_full, data)
64
65        return results
66
67    def get_action(self,
68            state: pacai.core.gamestate.GameState,
69            user_inputs: list[pacai.core.action.Action],
70            timeout: float,
71            ) -> pacai.core.agentaction.AgentActionRecord:
72        agent = self._agents[state.agent_index]
73        data = {
74            'state': state,
75            'user_inputs': user_inputs,
76        }
77
78        return _call_agent_method(state.agent_index, agent.get_action_full, data)
79
80    def close(self) -> None:
81        self._agents.clear()

An isolator that does not do any isolation between the engine and agents. All agents will be run in the same thread (and therefore processes space). This is the simplest and fastest of all isolators, but offers the least control and protection. Agents cannot be timed out (since they run on the same thread). Agents can also access any memory, disk, or permissions that the core engine has access to.

def init_agents(self, agent_infos: dict[int, pacai.core.agentinfo.AgentInfo]) -> None:
31    def init_agents(self, agent_infos: dict[int, pacai.core.agentinfo.AgentInfo]) -> None:
32        self._agents = {}
33        for (agent_index, agent_info) in agent_infos.items():
34            self._agents[agent_index] = pacai.core.agent.load(agent_info)

Initialize the agents this isolator will be responsible for.

def game_start( self, rng: random.Random, initial_state: pacai.core.gamestate.GameState, timeout: float) -> dict[int, pacai.core.agentaction.AgentActionRecord]:
36    def game_start(self,
37            rng: random.Random,
38            initial_state: pacai.core.gamestate.GameState,
39            timeout: float,
40            ) -> dict[int, pacai.core.agentaction.AgentActionRecord]:
41        results = {}
42        for (agent_index, agent) in self._agents.items():
43            data = {
44                'agent_index': agent_index,
45                'suggested_seed': rng.randint(0, 2**64),
46                'initial_state': initial_state,
47            }
48
49            results[agent_index] = _call_agent_method(agent_index, agent.game_start_full, data)
50
51        return results

Pass along the initial game state to each agent and all them the allotted time to start.

def game_complete( self, final_state: pacai.core.gamestate.GameState, timeout: float) -> dict[int, pacai.core.agentaction.AgentActionRecord]:
53    def game_complete(self,
54            final_state: pacai.core.gamestate.GameState,
55            timeout: float,
56            ) -> dict[int, pacai.core.agentaction.AgentActionRecord]:
57        results = {}
58        for (agent_index, agent) in self._agents.items():
59            data = {
60                'final_state': final_state,
61            }
62
63            results[agent_index] = _call_agent_method(agent_index, agent.game_complete_full, data)
64
65        return results

Notify all agents that the game is over.

def get_action( self, state: pacai.core.gamestate.GameState, user_inputs: list[pacai.core.action.Action], timeout: float) -> pacai.core.agentaction.AgentActionRecord:
67    def get_action(self,
68            state: pacai.core.gamestate.GameState,
69            user_inputs: list[pacai.core.action.Action],
70            timeout: float,
71            ) -> pacai.core.agentaction.AgentActionRecord:
72        agent = self._agents[state.agent_index]
73        data = {
74            'state': state,
75            'user_inputs': user_inputs,
76        }
77
78        return _call_agent_method(state.agent_index, agent.get_action_full, data)

Get the current agent's next action. User inputs may be provided by the UI if available.

def close(self) -> None:
80    def close(self) -> None:
81        self._agents.clear()

Close the isolator and release all owned resources. This call should not communicate with agents and forcibly release any agent resources.