pacai.core.agent
1import abc 2import logging 3import random 4import typing 5 6import pacai.core.agentaction 7import pacai.core.action 8import pacai.core.agentinfo 9import pacai.core.gamestate 10import pacai.util.alias 11import pacai.util.reflection 12 13class Agent(abc.ABC): 14 """ 15 The base for all agents in the pacai system. 16 17 Agents are called on by the game engine for three things: 18 1) `game_start_full()`/`game_start()` - a notification that the game has started. 19 2) `game_complete()` - a notification that the game has ended. 20 3) `get_action()` - a request for the agent to provide its next action. 21 22 For the three core agent methods: get_action(), game_start(), and game_complete(), 23 this class provides "full" versions of these methods (suffixed with "_full"). 24 These methods may have more information and allow the agent to provide more information, 25 but are a little more complex. 26 By default, this class will just call the simple methods from the "full" ones, 27 allowing children to just implement the simple methods. 28 29 Agents should avoid doing any heavy work in their constructors, 30 and instead do that work in game_start_full()/game_start() (where they will have access to the game state). 31 """ 32 33 def __init__(self, 34 name: pacai.util.reflection.Reference | str = pacai.util.alias.AGENT_DUMMY.long, 35 move_delay: int = pacai.core.agentinfo.DEFAULT_MOVE_DELAY, 36 state_eval_func: pacai.core.gamestate.AgentStateEvaluationFunction | pacai.util.reflection.Reference | str = 37 pacai.core.agentinfo.DEFAULT_STATE_EVAL, 38 training: bool = False, 39 training_epoch: int = 0, 40 **kwargs: typing.Any) -> None: 41 self.name: pacai.util.reflection.Reference = pacai.util.reflection.Reference(name) 42 """ The name of this agent. """ 43 44 self.move_delay: int = move_delay 45 """ 46 The delay between moves for this agent. 47 This value is abstract and has not real units, 48 i.e., it is not something like a number of seconds. 49 Instead, this is a relative "time" that is used to decide the next agent to move. 50 Lower values (relative to other agents) times means the agent will move more times and thus be "faster". 51 For example, an agent with a move delay of 50 will move twice as often as an agent with a move delay of 100. 52 """ 53 54 clean_state_eval_func = pacai.util.reflection.resolve_and_fetch(pacai.core.gamestate.AgentStateEvaluationFunction, state_eval_func) 55 self.evaluation_function: pacai.core.gamestate.AgentStateEvaluationFunction = clean_state_eval_func 56 """ The evaluation function that agent will use to assess game states. """ 57 58 self.rng: random.Random = random.Random(4) 59 """ 60 The RNG this agent should use whenever it wants randomness. 61 This object will be constructed right away, 62 but will be recreated with the suggested seed from the game engine during game_start_full(). 63 """ 64 65 self.agent_index: int = -1 66 """ 67 The index this agent has been assigned for this game. 68 It is initialized to -1 (before the game starts), but gets populated during game_start_full(). 69 """ 70 71 self.last_positions: list[pacai.core.board.Position | None] = [] 72 """ 73 Keep track of the last positions this agent was in. 74 This is updated in the beginning of get_action_full(). 75 This will include times when the agent was not on the board (a None position). 76 """ 77 78 self.training: bool = training 79 """ This instance of this agent has been created for training. """ 80 81 self.training_epoch: int = training_epoch 82 """ The training epoch (number of training games) the agent has completed. """ 83 84 self.extra_storage: dict[str, typing.Any] = {} 85 """ An extra place that can be used by and agent subcomponents for persistent storage. """ 86 87 logging.debug("Created agent '%s' with move delay %d and state evaluation function '%s'.", 88 pacai.util.reflection.get_qualified_name(self.name), 89 self.move_delay, 90 pacai.util.reflection.get_qualified_name(state_eval_func)) 91 92 def get_action_full(self, 93 state: pacai.core.gamestate.GameState, 94 user_inputs: list[pacai.core.action.Action], 95 ) -> pacai.core.agentaction.AgentAction: 96 """ 97 Get an action for this agent given the current state of the game. 98 Agents may keep internal state, but the given state should be considered the source of truth. 99 Calls to this method may be subject to a timeout (enforced by the isolator). 100 101 By default, this method just calls get_action(). 102 Agent classes should typically just implement get_action(), 103 and only implement this if they need additional functionality. 104 """ 105 106 self.last_positions.append(state.get_agent_position(self.agent_index)) 107 108 action = self.get_action(state) 109 110 return pacai.core.agentaction.AgentAction(action) 111 112 def get_action(self, state: pacai.core.gamestate.GameState) -> pacai.core.action.Action: 113 """ 114 Get an action for this agent given the current state of the game. 115 This is simplified version of get_action_full(), 116 see that method for full details. 117 """ 118 119 return pacai.core.action.STOP 120 121 def game_start_full(self, 122 agent_index: int, 123 suggested_seed: int, 124 initial_state: pacai.core.gamestate.GameState, 125 ) -> pacai.core.agentaction.AgentAction: 126 """ 127 Notify this agent that the game is about to start. 128 The provided agent index is the game's index/id for this agent. 129 The state represents the initial state of the game. 130 Any precomputation for this game should be done in this method. 131 Calls to this method may be subject to a timeout. 132 """ 133 134 self.agent_index = agent_index 135 self.rng = random.Random(suggested_seed) 136 137 self.game_start(initial_state) 138 139 return pacai.core.agentaction.AgentAction(pacai.core.action.STOP) 140 141 def game_start(self, 142 initial_state: pacai.core.gamestate.GameState, 143 ) -> None: 144 """ 145 Notify this agent that the game is about to start. 146 The provided agent index is the game's index/id for this agent. 147 The state represents the initial state of the game. 148 Any precomputation for this game should be done in this method. 149 Calls to this method may be subject to a timeout. 150 """ 151 152 def game_complete_full(self, 153 final_state: pacai.core.gamestate.GameState, 154 ) -> pacai.core.agentaction.AgentAction: 155 """ 156 Notify this agent that the game has concluded. 157 Agents should use this as an opportunity to make any final calculations and close any game-related resources. 158 """ 159 160 self.game_complete(final_state) 161 162 return pacai.core.agentaction.AgentAction(pacai.core.action.STOP) 163 164 def game_complete(self, 165 final_state: pacai.core.gamestate.GameState, 166 ) -> None: 167 """ 168 Notify this agent that the game has concluded. 169 Agents should use this as an opportunity to make any final calculations and close any game-related resources. 170 """ 171 172 def evaluate_state(self, 173 state: pacai.core.gamestate.GameState, 174 action: pacai.core.action.Action | None = None, 175 **kwargs: typing.Any) -> float: 176 """ 177 Evaluate the state to get a decide how good an action was. 178 The base implementation for this function just calls `self.evaluation_function`, 179 but child classes may override this method to easily implement their own evaluations. 180 """ 181 182 return self.evaluation_function(state, agent = self, action = action, **kwargs) 183 184def load(agent_info: pacai.core.agentinfo.AgentInfo) -> Agent: 185 """ 186 Construct a new agent object using the given agent info. 187 The name of the agent will be used as a reference to (e.g., name of) the agent's class. 188 """ 189 190 agent = pacai.util.reflection.new_object(agent_info.name, **agent_info.to_flat_dict()) 191 192 if (not isinstance(agent, Agent)): 193 raise ValueError(f"Loaded class is not an agent: '{agent_info.name}'.") 194 195 return agent
14class Agent(abc.ABC): 15 """ 16 The base for all agents in the pacai system. 17 18 Agents are called on by the game engine for three things: 19 1) `game_start_full()`/`game_start()` - a notification that the game has started. 20 2) `game_complete()` - a notification that the game has ended. 21 3) `get_action()` - a request for the agent to provide its next action. 22 23 For the three core agent methods: get_action(), game_start(), and game_complete(), 24 this class provides "full" versions of these methods (suffixed with "_full"). 25 These methods may have more information and allow the agent to provide more information, 26 but are a little more complex. 27 By default, this class will just call the simple methods from the "full" ones, 28 allowing children to just implement the simple methods. 29 30 Agents should avoid doing any heavy work in their constructors, 31 and instead do that work in game_start_full()/game_start() (where they will have access to the game state). 32 """ 33 34 def __init__(self, 35 name: pacai.util.reflection.Reference | str = pacai.util.alias.AGENT_DUMMY.long, 36 move_delay: int = pacai.core.agentinfo.DEFAULT_MOVE_DELAY, 37 state_eval_func: pacai.core.gamestate.AgentStateEvaluationFunction | pacai.util.reflection.Reference | str = 38 pacai.core.agentinfo.DEFAULT_STATE_EVAL, 39 training: bool = False, 40 training_epoch: int = 0, 41 **kwargs: typing.Any) -> None: 42 self.name: pacai.util.reflection.Reference = pacai.util.reflection.Reference(name) 43 """ The name of this agent. """ 44 45 self.move_delay: int = move_delay 46 """ 47 The delay between moves for this agent. 48 This value is abstract and has not real units, 49 i.e., it is not something like a number of seconds. 50 Instead, this is a relative "time" that is used to decide the next agent to move. 51 Lower values (relative to other agents) times means the agent will move more times and thus be "faster". 52 For example, an agent with a move delay of 50 will move twice as often as an agent with a move delay of 100. 53 """ 54 55 clean_state_eval_func = pacai.util.reflection.resolve_and_fetch(pacai.core.gamestate.AgentStateEvaluationFunction, state_eval_func) 56 self.evaluation_function: pacai.core.gamestate.AgentStateEvaluationFunction = clean_state_eval_func 57 """ The evaluation function that agent will use to assess game states. """ 58 59 self.rng: random.Random = random.Random(4) 60 """ 61 The RNG this agent should use whenever it wants randomness. 62 This object will be constructed right away, 63 but will be recreated with the suggested seed from the game engine during game_start_full(). 64 """ 65 66 self.agent_index: int = -1 67 """ 68 The index this agent has been assigned for this game. 69 It is initialized to -1 (before the game starts), but gets populated during game_start_full(). 70 """ 71 72 self.last_positions: list[pacai.core.board.Position | None] = [] 73 """ 74 Keep track of the last positions this agent was in. 75 This is updated in the beginning of get_action_full(). 76 This will include times when the agent was not on the board (a None position). 77 """ 78 79 self.training: bool = training 80 """ This instance of this agent has been created for training. """ 81 82 self.training_epoch: int = training_epoch 83 """ The training epoch (number of training games) the agent has completed. """ 84 85 self.extra_storage: dict[str, typing.Any] = {} 86 """ An extra place that can be used by and agent subcomponents for persistent storage. """ 87 88 logging.debug("Created agent '%s' with move delay %d and state evaluation function '%s'.", 89 pacai.util.reflection.get_qualified_name(self.name), 90 self.move_delay, 91 pacai.util.reflection.get_qualified_name(state_eval_func)) 92 93 def get_action_full(self, 94 state: pacai.core.gamestate.GameState, 95 user_inputs: list[pacai.core.action.Action], 96 ) -> pacai.core.agentaction.AgentAction: 97 """ 98 Get an action for this agent given the current state of the game. 99 Agents may keep internal state, but the given state should be considered the source of truth. 100 Calls to this method may be subject to a timeout (enforced by the isolator). 101 102 By default, this method just calls get_action(). 103 Agent classes should typically just implement get_action(), 104 and only implement this if they need additional functionality. 105 """ 106 107 self.last_positions.append(state.get_agent_position(self.agent_index)) 108 109 action = self.get_action(state) 110 111 return pacai.core.agentaction.AgentAction(action) 112 113 def get_action(self, state: pacai.core.gamestate.GameState) -> pacai.core.action.Action: 114 """ 115 Get an action for this agent given the current state of the game. 116 This is simplified version of get_action_full(), 117 see that method for full details. 118 """ 119 120 return pacai.core.action.STOP 121 122 def game_start_full(self, 123 agent_index: int, 124 suggested_seed: int, 125 initial_state: pacai.core.gamestate.GameState, 126 ) -> pacai.core.agentaction.AgentAction: 127 """ 128 Notify this agent that the game is about to start. 129 The provided agent index is the game's index/id for this agent. 130 The state represents the initial state of the game. 131 Any precomputation for this game should be done in this method. 132 Calls to this method may be subject to a timeout. 133 """ 134 135 self.agent_index = agent_index 136 self.rng = random.Random(suggested_seed) 137 138 self.game_start(initial_state) 139 140 return pacai.core.agentaction.AgentAction(pacai.core.action.STOP) 141 142 def game_start(self, 143 initial_state: pacai.core.gamestate.GameState, 144 ) -> None: 145 """ 146 Notify this agent that the game is about to start. 147 The provided agent index is the game's index/id for this agent. 148 The state represents the initial state of the game. 149 Any precomputation for this game should be done in this method. 150 Calls to this method may be subject to a timeout. 151 """ 152 153 def game_complete_full(self, 154 final_state: pacai.core.gamestate.GameState, 155 ) -> pacai.core.agentaction.AgentAction: 156 """ 157 Notify this agent that the game has concluded. 158 Agents should use this as an opportunity to make any final calculations and close any game-related resources. 159 """ 160 161 self.game_complete(final_state) 162 163 return pacai.core.agentaction.AgentAction(pacai.core.action.STOP) 164 165 def game_complete(self, 166 final_state: pacai.core.gamestate.GameState, 167 ) -> None: 168 """ 169 Notify this agent that the game has concluded. 170 Agents should use this as an opportunity to make any final calculations and close any game-related resources. 171 """ 172 173 def evaluate_state(self, 174 state: pacai.core.gamestate.GameState, 175 action: pacai.core.action.Action | None = None, 176 **kwargs: typing.Any) -> float: 177 """ 178 Evaluate the state to get a decide how good an action was. 179 The base implementation for this function just calls `self.evaluation_function`, 180 but child classes may override this method to easily implement their own evaluations. 181 """ 182 183 return self.evaluation_function(state, agent = self, action = action, **kwargs)
The base for all agents in the pacai system.
Agents are called on by the game engine for three things:
1) game_start_full()/game_start() - a notification that the game has started.
2) game_complete() - a notification that the game has ended.
3) get_action() - a request for the agent to provide its next action.
For the three core agent methods: get_action(), game_start(), and game_complete(), this class provides "full" versions of these methods (suffixed with "_full"). These methods may have more information and allow the agent to provide more information, but are a little more complex. By default, this class will just call the simple methods from the "full" ones, allowing children to just implement the simple methods.
Agents should avoid doing any heavy work in their constructors, and instead do that work in game_start_full()/game_start() (where they will have access to the game state).
34 def __init__(self, 35 name: pacai.util.reflection.Reference | str = pacai.util.alias.AGENT_DUMMY.long, 36 move_delay: int = pacai.core.agentinfo.DEFAULT_MOVE_DELAY, 37 state_eval_func: pacai.core.gamestate.AgentStateEvaluationFunction | pacai.util.reflection.Reference | str = 38 pacai.core.agentinfo.DEFAULT_STATE_EVAL, 39 training: bool = False, 40 training_epoch: int = 0, 41 **kwargs: typing.Any) -> None: 42 self.name: pacai.util.reflection.Reference = pacai.util.reflection.Reference(name) 43 """ The name of this agent. """ 44 45 self.move_delay: int = move_delay 46 """ 47 The delay between moves for this agent. 48 This value is abstract and has not real units, 49 i.e., it is not something like a number of seconds. 50 Instead, this is a relative "time" that is used to decide the next agent to move. 51 Lower values (relative to other agents) times means the agent will move more times and thus be "faster". 52 For example, an agent with a move delay of 50 will move twice as often as an agent with a move delay of 100. 53 """ 54 55 clean_state_eval_func = pacai.util.reflection.resolve_and_fetch(pacai.core.gamestate.AgentStateEvaluationFunction, state_eval_func) 56 self.evaluation_function: pacai.core.gamestate.AgentStateEvaluationFunction = clean_state_eval_func 57 """ The evaluation function that agent will use to assess game states. """ 58 59 self.rng: random.Random = random.Random(4) 60 """ 61 The RNG this agent should use whenever it wants randomness. 62 This object will be constructed right away, 63 but will be recreated with the suggested seed from the game engine during game_start_full(). 64 """ 65 66 self.agent_index: int = -1 67 """ 68 The index this agent has been assigned for this game. 69 It is initialized to -1 (before the game starts), but gets populated during game_start_full(). 70 """ 71 72 self.last_positions: list[pacai.core.board.Position | None] = [] 73 """ 74 Keep track of the last positions this agent was in. 75 This is updated in the beginning of get_action_full(). 76 This will include times when the agent was not on the board (a None position). 77 """ 78 79 self.training: bool = training 80 """ This instance of this agent has been created for training. """ 81 82 self.training_epoch: int = training_epoch 83 """ The training epoch (number of training games) the agent has completed. """ 84 85 self.extra_storage: dict[str, typing.Any] = {} 86 """ An extra place that can be used by and agent subcomponents for persistent storage. """ 87 88 logging.debug("Created agent '%s' with move delay %d and state evaluation function '%s'.", 89 pacai.util.reflection.get_qualified_name(self.name), 90 self.move_delay, 91 pacai.util.reflection.get_qualified_name(state_eval_func))
The delay between moves for this agent. This value is abstract and has not real units, i.e., it is not something like a number of seconds. Instead, this is a relative "time" that is used to decide the next agent to move. Lower values (relative to other agents) times means the agent will move more times and thus be "faster". For example, an agent with a move delay of 50 will move twice as often as an agent with a move delay of 100.
The evaluation function that agent will use to assess game states.
The RNG this agent should use whenever it wants randomness. This object will be constructed right away, but will be recreated with the suggested seed from the game engine during game_start_full().
The index this agent has been assigned for this game. It is initialized to -1 (before the game starts), but gets populated during game_start_full().
Keep track of the last positions this agent was in. This is updated in the beginning of get_action_full(). This will include times when the agent was not on the board (a None position).
An extra place that can be used by and agent subcomponents for persistent storage.
93 def get_action_full(self, 94 state: pacai.core.gamestate.GameState, 95 user_inputs: list[pacai.core.action.Action], 96 ) -> pacai.core.agentaction.AgentAction: 97 """ 98 Get an action for this agent given the current state of the game. 99 Agents may keep internal state, but the given state should be considered the source of truth. 100 Calls to this method may be subject to a timeout (enforced by the isolator). 101 102 By default, this method just calls get_action(). 103 Agent classes should typically just implement get_action(), 104 and only implement this if they need additional functionality. 105 """ 106 107 self.last_positions.append(state.get_agent_position(self.agent_index)) 108 109 action = self.get_action(state) 110 111 return pacai.core.agentaction.AgentAction(action)
Get an action for this agent given the current state of the game. Agents may keep internal state, but the given state should be considered the source of truth. Calls to this method may be subject to a timeout (enforced by the isolator).
By default, this method just calls get_action(). Agent classes should typically just implement get_action(), and only implement this if they need additional functionality.
113 def get_action(self, state: pacai.core.gamestate.GameState) -> pacai.core.action.Action: 114 """ 115 Get an action for this agent given the current state of the game. 116 This is simplified version of get_action_full(), 117 see that method for full details. 118 """ 119 120 return pacai.core.action.STOP
Get an action for this agent given the current state of the game. This is simplified version of get_action_full(), see that method for full details.
122 def game_start_full(self, 123 agent_index: int, 124 suggested_seed: int, 125 initial_state: pacai.core.gamestate.GameState, 126 ) -> pacai.core.agentaction.AgentAction: 127 """ 128 Notify this agent that the game is about to start. 129 The provided agent index is the game's index/id for this agent. 130 The state represents the initial state of the game. 131 Any precomputation for this game should be done in this method. 132 Calls to this method may be subject to a timeout. 133 """ 134 135 self.agent_index = agent_index 136 self.rng = random.Random(suggested_seed) 137 138 self.game_start(initial_state) 139 140 return pacai.core.agentaction.AgentAction(pacai.core.action.STOP)
Notify this agent that the game is about to start. The provided agent index is the game's index/id for this agent. The state represents the initial state of the game. Any precomputation for this game should be done in this method. Calls to this method may be subject to a timeout.
142 def game_start(self, 143 initial_state: pacai.core.gamestate.GameState, 144 ) -> None: 145 """ 146 Notify this agent that the game is about to start. 147 The provided agent index is the game's index/id for this agent. 148 The state represents the initial state of the game. 149 Any precomputation for this game should be done in this method. 150 Calls to this method may be subject to a timeout. 151 """
Notify this agent that the game is about to start. The provided agent index is the game's index/id for this agent. The state represents the initial state of the game. Any precomputation for this game should be done in this method. Calls to this method may be subject to a timeout.
153 def game_complete_full(self, 154 final_state: pacai.core.gamestate.GameState, 155 ) -> pacai.core.agentaction.AgentAction: 156 """ 157 Notify this agent that the game has concluded. 158 Agents should use this as an opportunity to make any final calculations and close any game-related resources. 159 """ 160 161 self.game_complete(final_state) 162 163 return pacai.core.agentaction.AgentAction(pacai.core.action.STOP)
Notify this agent that the game has concluded. Agents should use this as an opportunity to make any final calculations and close any game-related resources.
165 def game_complete(self, 166 final_state: pacai.core.gamestate.GameState, 167 ) -> None: 168 """ 169 Notify this agent that the game has concluded. 170 Agents should use this as an opportunity to make any final calculations and close any game-related resources. 171 """
Notify this agent that the game has concluded. Agents should use this as an opportunity to make any final calculations and close any game-related resources.
173 def evaluate_state(self, 174 state: pacai.core.gamestate.GameState, 175 action: pacai.core.action.Action | None = None, 176 **kwargs: typing.Any) -> float: 177 """ 178 Evaluate the state to get a decide how good an action was. 179 The base implementation for this function just calls `self.evaluation_function`, 180 but child classes may override this method to easily implement their own evaluations. 181 """ 182 183 return self.evaluation_function(state, agent = self, action = action, **kwargs)
Evaluate the state to get a decide how good an action was.
The base implementation for this function just calls self.evaluation_function,
but child classes may override this method to easily implement their own evaluations.
185def load(agent_info: pacai.core.agentinfo.AgentInfo) -> Agent: 186 """ 187 Construct a new agent object using the given agent info. 188 The name of the agent will be used as a reference to (e.g., name of) the agent's class. 189 """ 190 191 agent = pacai.util.reflection.new_object(agent_info.name, **agent_info.to_flat_dict()) 192 193 if (not isinstance(agent, Agent)): 194 raise ValueError(f"Loaded class is not an agent: '{agent_info.name}'.") 195 196 return agent
Construct a new agent object using the given agent info. The name of the agent will be used as a reference to (e.g., name of) the agent's class.