11import abc
22import enum
3+ import logging
4+ import random
35
46import pacai .core .action
57import pacai .core .agent
68import pacai .core .gamestate
9+ import pacai .core .time
710
8- class Isolator (abc .ABC ):
11+ class AgentIsolator (abc .ABC ):
912 """
1013 An isolator isolates an agent instance from the game being played.
1114 This "isolation" allows the game to hide or protect state from a agent.
@@ -16,16 +19,15 @@ class Isolator(abc.ABC):
1619 """
1720
1821 @abc .abstractmethod
19- def game_init (self , agent_args : list [pacai .core .agent .AgentArguments ]) -> None :
22+ def init_agents (self , agent_args : list [pacai .core .agent .AgentArguments ]) -> None :
2023 """
21- Initialize the isolator with the given agent arguments.
22- Called when a game is just preparing to start.
24+ Initialize the agents this isolator will be responsible for.
2325 """
2426
2527 pass
2628
2729 @abc .abstractmethod
28- def game_start (self , initial_state : pacai .core .gamestate .GameState ) -> None :
30+ def game_start (self , rng : random . Random , initial_state : pacai .core .gamestate .GameState ) -> None :
2931 """
3032 Pass along the initial game state to each agent and all them the allotted time to start.
3133 """
@@ -41,9 +43,17 @@ def game_complete(self, final_state: pacai.core.gamestate.GameState) -> None:
4143 pass
4244
4345 @abc .abstractmethod
44- def get_action (self , agent_index : int , state : pacai .core .gamestate .GameState ) -> pacai .core .action . Action :
46+ def get_action (self , agent_index : int , state : pacai .core .gamestate .GameState ) -> pacai .core .agent . ActionRecord :
4547 """
46- Get an agent's next action.
48+ Get an agent's next action and how long it took to decide on that action.
49+ """
50+
51+ pass
52+
53+ @abc .abstractmethod
54+ def close (self ) -> None :
55+ """
56+ Close the isolator and release all owned resources.
4757 """
4858
4959 pass
@@ -53,26 +63,81 @@ class Level(enum.Enum):
5363 PROCESS = 1
5464 TCP = 2
5565
56- def get_isolator (self ) -> Isolator :
66+ def get_isolator (self , ** kwargs ) -> AgentIsolator :
5767 """ Get an isolator matching the given level. """
5868
5969 if (self .value == Level .NONE ):
60- return NoneIsolator ()
70+ return NoneIsolator (** kwargs )
6171 if (self .value == Level .PROCESS ):
62- return ProcessIsolator ()
72+ return ProcessIsolator (** kwargs )
6373 if (self .value == Level .TCP ):
64- return TCPIsolator ()
74+ return TCPIsolator (** kwargs )
6575 else :
6676 raise ValueError (f"Unknown isolation level '{ self .value } '." )
6777
68- # TEST
69- class NoneIsolator (abc .ABC ):
70- pass
78+ class NoneIsolator (AgentIsolator ):
79+ """
80+ An isolator that does not do any isolation between the engine and agents.
81+ All agents will be run in the same thread (and therefore processes space).
82+ This is the simplest and fastest of all isolators, but offers the least control and protection.
83+ Agents cannot be timed out (since they run on the same thread).
84+ Agents can also access any memory or disk that the core engine has access to.
85+ """
86+
87+ def __init__ (self , ** kwargs ) -> None :
88+ self ._agents : list [pacai .core .agent .Agent ] | None = None
89+
90+ def init_agents (self , agent_args : list [pacai .core .agent .AgentArguments ]) -> None :
91+ self ._agents = [pacai .core .agent .load (args ) for args in agent_args ]
92+
93+ def game_start (self , rng : random .Random , initial_state : pacai .core .gamestate .GameState ) -> None :
94+ if (self ._agents is None ):
95+ raise ValueError ("Isolator agents has not been initialized." )
96+
97+ for i in range (len (self ._agents )):
98+ suggested_seed = rng .randint (0 , 2 ** 64 )
99+ self ._agents [i ].game_start (i , suggested_seed , initial_state )
100+
101+ def game_complete (self , final_state : pacai .core .gamestate .GameState ) -> None :
102+ if (self ._agents is None ):
103+ raise ValueError ("Isolator agents has not been initialized." )
104+
105+ for agent in self ._agents :
106+ agent .game_complete (final_state )
107+
108+ def get_action (self , agent_index : int , state : pacai .core .gamestate .GameState ) -> pacai .core .agent .ActionRecord :
109+ if (self ._agents is None ):
110+ raise ValueError ("Isolator agents has not been initialized." )
111+
112+ agent = self ._agents [agent_index ]
113+ crashed = False
114+
115+ start_time = pacai .core .time .now ()
116+
117+ try :
118+ action = agent .get_action (state )
119+ except Exception as ex :
120+ logging .warning ("Agent '%s' (%d) crashed." , agent .name , agent_index , exc_info = ex )
121+
122+ crashed = True
123+ action = pacai .core .action .STOP
124+
125+ end_time = pacai .core .time .now ()
126+
127+ return pacai .core .agent .ActionRecord (
128+ agent_index = agent_index ,
129+ action = action ,
130+ duration = end_time .sub (start_time ),
131+ crashed = crashed )
132+
133+
134+ def close (self ) -> None :
135+ self ._agents = None
71136
72137# TEST
73- class ProcessIsolator (abc . ABC ):
138+ class ProcessIsolator (AgentIsolator ):
74139 pass
75140
76141# TEST
77- class TCPIsolator (abc . ABC ):
142+ class TCPIsolator (AgentIsolator ):
78143 pass
0 commit comments