gameboy_worlds.interface.bomberman.actions
1from typing import Any, Dict, List, Type 2 3import numpy as np 4from gymnasium.spaces import Discrete 5 6from gameboy_worlds.emulation import LowLevelActions 7from gameboy_worlds.emulation.bomberman.parsers import ( 8 BombermanMaxParser, 9 BombermanPocketParser, 10 BombermanQuestParser, 11) 12from gameboy_worlds.emulation.bomberman.trackers import ( 13 BombermanMaxTracker, 14 BombermanPocketTracker, 15 BombermanQuestTracker, 16) 17from gameboy_worlds.interface.action import HighLevelAction, SingleHighLevelAction 18 19HARD_MAX_STEPS = 10 20 21MAX_MENU_METRIC = ("bomberman_max_core", "is_in_menu") 22MAX_BATTLE_METRIC = ("bomberman_max_core", "is_in_battle") 23POCKET_MENU_METRIC = ("bomberman_pocket_core", "is_in_menu") 24QUEST_MENU_METRIC = ("bomberman_quest_core", "is_in_menu") 25QUEST_BATTLE_METRIC = ("bomberman_quest_core", "is_in_battle") 26 27 28def frame_changed(previous: np.ndarray, current: np.ndarray, epsilon: float = 0.01) -> bool: 29 return np.abs(previous - current).mean() > epsilon 30 31 32class _MoveAction(HighLevelAction): 33 _DIRECTION_TO_ACTION = {} 34 _BLOCKING_METRICS: List[tuple[str, str]] = [] 35 36 def get_action_space(self): 37 return Discrete(len(self._DIRECTION_TO_ACTION) * HARD_MAX_STEPS) 38 39 def space_to_parameters(self, space_action): 40 directions = list(self._DIRECTION_TO_ACTION) 41 if space_action < 0 or space_action >= len(directions) * HARD_MAX_STEPS: 42 return None 43 direction = directions[space_action // HARD_MAX_STEPS] 44 steps = (space_action % HARD_MAX_STEPS) + 1 45 return {"direction": direction, "steps": steps} 46 47 def parameters_to_space(self, direction: str, steps: int): 48 directions = list(self._DIRECTION_TO_ACTION) 49 if direction not in directions or steps <= 0 or steps > HARD_MAX_STEPS: 50 return None 51 return directions.index(direction) * HARD_MAX_STEPS + steps - 1 52 53 def is_valid(self, **kwargs): 54 direction = kwargs.get("direction") 55 steps = kwargs.get("steps") 56 if direction is not None and direction not in self._DIRECTION_TO_ACTION: 57 return False 58 if steps is not None and (not isinstance(steps, int) or steps <= 0): 59 return False 60 return not any( 61 self._state_tracker.get_episode_metric(metric_key) 62 for metric_key in self._BLOCKING_METRICS 63 ) 64 65 def _execute(self, direction: str, steps: int): 66 action = self._DIRECTION_TO_ACTION[direction] 67 transition_state_dicts: List[Dict[str, Any]] = [] 68 n_steps_taken = 0 69 previous_frame = self._emulator.get_current_frame() 70 action_success = -1 71 for _ in range(steps): 72 frames, done = self._emulator.step(action) 73 report = self._state_tracker.report() 74 transition_state_dicts.append(report) 75 current_frame = frames[-1] 76 if not frame_changed(previous_frame, current_frame): 77 action_success = 1 if n_steps_taken > 0 else -1 78 break 79 n_steps_taken += 1 80 if any( 81 self._state_tracker.get_episode_metric(metric_key) 82 for metric_key in self._BLOCKING_METRICS 83 ): 84 action_success = 2 85 break 86 if done: 87 action_success = 0 88 break 89 previous_frame = current_frame 90 else: 91 action_success = 0 92 if transition_state_dicts: 93 transition_state_dicts[-1]["core"]["action_return"] = { 94 "n_steps_taken": n_steps_taken 95 } 96 return transition_state_dicts, action_success 97 98 @staticmethod 99 def get_action_name(direction: str, steps: int) -> str: 100 return f"Move {direction} {steps}" 101 102 103class _MetricGatedSingleAction(SingleHighLevelAction): 104 _BUTTON = None 105 _REQUIRED_METRICS_FALSE: List[tuple[str, str]] = [] 106 _REQUIRED_METRICS_TRUE: List[tuple[str, str]] = [] 107 108 def is_valid(self, **kwargs): 109 return all( 110 not self._state_tracker.get_episode_metric(metric_key) 111 for metric_key in self._REQUIRED_METRICS_FALSE 112 ) and all( 113 self._state_tracker.get_episode_metric(metric_key) 114 for metric_key in self._REQUIRED_METRICS_TRUE 115 ) 116 117 def _execute(self): 118 previous = self._emulator.get_current_frame() 119 frames, _ = self._emulator.step(self._BUTTON) 120 report = self._state_tracker.report() 121 return [report], 0 if frame_changed(previous, frames[-1]) else -1 122 123 124class BombermanMaxMoveAction(_MoveAction): 125 REQUIRED_STATE_TRACKER = BombermanMaxTracker 126 REQUIRED_STATE_PARSER = BombermanMaxParser 127 _DIRECTION_TO_ACTION = { 128 "up": LowLevelActions.PRESS_ARROW_UP, 129 "down": LowLevelActions.PRESS_ARROW_DOWN, 130 "left": LowLevelActions.PRESS_ARROW_LEFT, 131 "right": LowLevelActions.PRESS_ARROW_RIGHT, 132 } 133 _BLOCKING_METRICS = [MAX_MENU_METRIC, MAX_BATTLE_METRIC] 134 135 136class BombermanMaxPlaceBombAction(_MetricGatedSingleAction): 137 REQUIRED_STATE_TRACKER = BombermanMaxTracker 138 REQUIRED_STATE_PARSER = BombermanMaxParser 139 _BUTTON = LowLevelActions.PRESS_BUTTON_A 140 _REQUIRED_METRICS_FALSE = [MAX_MENU_METRIC, MAX_BATTLE_METRIC] 141 142 @staticmethod 143 def get_action_name() -> str: 144 return "PlaceBomb" 145 146 147class BombermanMaxKickBombAction(_MetricGatedSingleAction): 148 REQUIRED_STATE_TRACKER = BombermanMaxTracker 149 REQUIRED_STATE_PARSER = BombermanMaxParser 150 _BUTTON = LowLevelActions.PRESS_BUTTON_B 151 _REQUIRED_METRICS_FALSE = [MAX_MENU_METRIC, MAX_BATTLE_METRIC] 152 153 @staticmethod 154 def get_action_name() -> str: 155 return "KickBomb" 156 157 158class BombermanMaxOpenMenuAction(_MetricGatedSingleAction): 159 REQUIRED_STATE_TRACKER = BombermanMaxTracker 160 REQUIRED_STATE_PARSER = BombermanMaxParser 161 _BUTTON = LowLevelActions.PRESS_BUTTON_START 162 _REQUIRED_METRICS_FALSE = [MAX_MENU_METRIC, MAX_BATTLE_METRIC] 163 164 def _execute(self): 165 self._emulator.step(self._BUTTON) 166 report = self._state_tracker.report() 167 return [report], 0 if self._state_tracker.get_episode_metric(MAX_MENU_METRIC) else -1 168 169 @staticmethod 170 def get_action_name() -> str: 171 return "OpenMenu" 172 173 174class BombermanMaxCloseMenuAction(_MetricGatedSingleAction): 175 REQUIRED_STATE_TRACKER = BombermanMaxTracker 176 REQUIRED_STATE_PARSER = BombermanMaxParser 177 _BUTTON = LowLevelActions.PRESS_BUTTON_START 178 _REQUIRED_METRICS_TRUE = [MAX_MENU_METRIC] 179 180 def _execute(self): 181 self._emulator.step(self._BUTTON) 182 report = self._state_tracker.report() 183 return [report], 0 if not self._state_tracker.get_episode_metric(MAX_MENU_METRIC) else -1 184 185 @staticmethod 186 def get_action_name() -> str: 187 return "CloseMenu" 188 189 190class BombermanMaxNavigateMenuAction(SingleHighLevelAction): 191 REQUIRED_STATE_TRACKER = BombermanMaxTracker 192 REQUIRED_STATE_PARSER = BombermanMaxParser 193 _ACTION_MAP = { 194 "up": LowLevelActions.PRESS_ARROW_UP, 195 "down": LowLevelActions.PRESS_ARROW_DOWN, 196 "left": LowLevelActions.PRESS_ARROW_LEFT, 197 "right": LowLevelActions.PRESS_ARROW_RIGHT, 198 "confirm": LowLevelActions.PRESS_BUTTON_A, 199 "back": LowLevelActions.PRESS_BUTTON_B, 200 } 201 202 def get_action_space(self): 203 return Discrete(len(self._ACTION_MAP)) 204 205 def space_to_parameters(self, space_action): 206 keys = list(self._ACTION_MAP) 207 if space_action < 0 or space_action >= len(keys): 208 return None 209 return {"menu_action": keys[space_action]} 210 211 def parameters_to_space(self, menu_action: str): 212 keys = list(self._ACTION_MAP) 213 return keys.index(menu_action) if menu_action in keys else None 214 215 def is_valid(self, **kwargs): 216 menu_action = kwargs.get("menu_action") 217 return ( 218 menu_action in self._ACTION_MAP if menu_action is not None else True 219 ) and self._state_tracker.get_episode_metric(MAX_MENU_METRIC) 220 221 def _execute(self, menu_action: str): 222 previous = self._emulator.get_current_frame() 223 frames, _ = self._emulator.step(self._ACTION_MAP[menu_action]) 224 report = self._state_tracker.report() 225 return [report], 0 if frame_changed(previous, frames[-1]) else -1 226 227 @staticmethod 228 def get_action_name(menu_action: str) -> str: 229 return f"NavigateMenu {menu_action}" 230 231 232class BombermanMaxBattleAction(SingleHighLevelAction): 233 REQUIRED_STATE_TRACKER = BombermanMaxTracker 234 REQUIRED_STATE_PARSER = BombermanMaxParser 235 _ACTION_MAP = { 236 "bomb": LowLevelActions.PRESS_BUTTON_A, 237 "up": LowLevelActions.PRESS_ARROW_UP, 238 "down": LowLevelActions.PRESS_ARROW_DOWN, 239 "left": LowLevelActions.PRESS_ARROW_LEFT, 240 "right": LowLevelActions.PRESS_ARROW_RIGHT, 241 } 242 243 def get_action_space(self): 244 return Discrete(len(self._ACTION_MAP)) 245 246 def space_to_parameters(self, space_action): 247 keys = list(self._ACTION_MAP) 248 if space_action < 0 or space_action >= len(keys): 249 return None 250 return {"battle_action": keys[space_action]} 251 252 def parameters_to_space(self, battle_action: str): 253 keys = list(self._ACTION_MAP) 254 return keys.index(battle_action) if battle_action in keys else None 255 256 def is_valid(self, **kwargs): 257 battle_action = kwargs.get("battle_action") 258 return ( 259 battle_action in self._ACTION_MAP if battle_action is not None else True 260 ) and self._state_tracker.get_episode_metric(MAX_BATTLE_METRIC) 261 262 def _execute(self, battle_action: str): 263 previous = self._emulator.get_current_frame() 264 frames, _ = self._emulator.step(self._ACTION_MAP[battle_action]) 265 report = self._state_tracker.report() 266 return [report], 0 if frame_changed(previous, frames[-1]) else -1 267 268 @staticmethod 269 def get_action_name(battle_action: str) -> str: 270 return f"Battle {battle_action}" 271 272 273class BombermanPocketMoveAction(_MoveAction): 274 REQUIRED_STATE_TRACKER = BombermanPocketTracker 275 REQUIRED_STATE_PARSER = BombermanPocketParser 276 _DIRECTION_TO_ACTION = { 277 "left": LowLevelActions.PRESS_ARROW_LEFT, 278 "right": LowLevelActions.PRESS_ARROW_RIGHT, 279 } 280 _BLOCKING_METRICS = [POCKET_MENU_METRIC] 281 282 283class BombermanPocketJumpAction(_MetricGatedSingleAction): 284 REQUIRED_STATE_TRACKER = BombermanPocketTracker 285 REQUIRED_STATE_PARSER = BombermanPocketParser 286 _BUTTON = LowLevelActions.PRESS_BUTTON_B 287 _REQUIRED_METRICS_FALSE = [POCKET_MENU_METRIC] 288 289 @staticmethod 290 def get_action_name() -> str: 291 return "Jump" 292 293 294class BombermanPocketPlaceBombAction(_MetricGatedSingleAction): 295 REQUIRED_STATE_TRACKER = BombermanPocketTracker 296 REQUIRED_STATE_PARSER = BombermanPocketParser 297 _BUTTON = LowLevelActions.PRESS_BUTTON_A 298 _REQUIRED_METRICS_FALSE = [POCKET_MENU_METRIC] 299 300 @staticmethod 301 def get_action_name() -> str: 302 return "PlaceBomb" 303 304 305class BombermanPocketOpenPauseMenuAction(_MetricGatedSingleAction): 306 REQUIRED_STATE_TRACKER = BombermanPocketTracker 307 REQUIRED_STATE_PARSER = BombermanPocketParser 308 _BUTTON = LowLevelActions.PRESS_BUTTON_START 309 _REQUIRED_METRICS_FALSE = [POCKET_MENU_METRIC] 310 311 def _execute(self): 312 self._emulator.step(self._BUTTON) 313 report = self._state_tracker.report() 314 return [report], 0 if self._state_tracker.get_episode_metric(POCKET_MENU_METRIC) else -1 315 316 @staticmethod 317 def get_action_name() -> str: 318 return "OpenPauseMenu" 319 320 321class BombermanPocketClosePauseMenuAction(_MetricGatedSingleAction): 322 REQUIRED_STATE_TRACKER = BombermanPocketTracker 323 REQUIRED_STATE_PARSER = BombermanPocketParser 324 _BUTTON = LowLevelActions.PRESS_BUTTON_START 325 _REQUIRED_METRICS_TRUE = [POCKET_MENU_METRIC] 326 327 def _execute(self): 328 self._emulator.step(self._BUTTON) 329 report = self._state_tracker.report() 330 return [report], 0 if not self._state_tracker.get_episode_metric(POCKET_MENU_METRIC) else -1 331 332 @staticmethod 333 def get_action_name() -> str: 334 return "ClosePauseMenu" 335 336 337class BombermanQuestMoveAction(_MoveAction): 338 REQUIRED_STATE_TRACKER = BombermanQuestTracker 339 REQUIRED_STATE_PARSER = BombermanQuestParser 340 _DIRECTION_TO_ACTION = { 341 "up": LowLevelActions.PRESS_ARROW_UP, 342 "down": LowLevelActions.PRESS_ARROW_DOWN, 343 "left": LowLevelActions.PRESS_ARROW_LEFT, 344 "right": LowLevelActions.PRESS_ARROW_RIGHT, 345 } 346 _BLOCKING_METRICS = [QUEST_MENU_METRIC, QUEST_BATTLE_METRIC] 347 348 349class BombermanQuestPlaceBombAction(_MetricGatedSingleAction): 350 REQUIRED_STATE_TRACKER = BombermanQuestTracker 351 REQUIRED_STATE_PARSER = BombermanQuestParser 352 _BUTTON = LowLevelActions.PRESS_BUTTON_A 353 _REQUIRED_METRICS_FALSE = [QUEST_MENU_METRIC, QUEST_BATTLE_METRIC] 354 355 @staticmethod 356 def get_action_name() -> str: 357 return "PlaceBomb" 358 359 360class BombermanQuestUseBButtonItemAction(_MetricGatedSingleAction): 361 REQUIRED_STATE_TRACKER = BombermanQuestTracker 362 REQUIRED_STATE_PARSER = BombermanQuestParser 363 _BUTTON = LowLevelActions.PRESS_BUTTON_B 364 _REQUIRED_METRICS_FALSE = [QUEST_MENU_METRIC, QUEST_BATTLE_METRIC] 365 366 @staticmethod 367 def get_action_name() -> str: 368 return "UseBButtonItem" 369 370 371class BombermanQuestOpenMenuAction(_MetricGatedSingleAction): 372 REQUIRED_STATE_TRACKER = BombermanQuestTracker 373 REQUIRED_STATE_PARSER = BombermanQuestParser 374 _BUTTON = LowLevelActions.PRESS_BUTTON_START 375 _REQUIRED_METRICS_FALSE = [QUEST_MENU_METRIC, QUEST_BATTLE_METRIC] 376 377 def _execute(self): 378 self._emulator.step(self._BUTTON) 379 report = self._state_tracker.report() 380 return [report], 0 if self._state_tracker.get_episode_metric(QUEST_MENU_METRIC) else -1 381 382 @staticmethod 383 def get_action_name() -> str: 384 return "OpenMenu" 385 386 387class BombermanQuestCloseMenuAction(_MetricGatedSingleAction): 388 REQUIRED_STATE_TRACKER = BombermanQuestTracker 389 REQUIRED_STATE_PARSER = BombermanQuestParser 390 _BUTTON = LowLevelActions.PRESS_BUTTON_START 391 _REQUIRED_METRICS_TRUE = [QUEST_MENU_METRIC] 392 393 def _execute(self): 394 self._emulator.step(self._BUTTON) 395 report = self._state_tracker.report() 396 return [report], 0 if not self._state_tracker.get_episode_metric(QUEST_MENU_METRIC) else -1 397 398 @staticmethod 399 def get_action_name() -> str: 400 return "CloseMenu" 401 402 403class BombermanQuestNavigateMenuAction(BombermanMaxNavigateMenuAction): 404 REQUIRED_STATE_TRACKER = BombermanQuestTracker 405 REQUIRED_STATE_PARSER = BombermanQuestParser 406 407 def is_valid(self, **kwargs): 408 menu_action = kwargs.get("menu_action") 409 return ( 410 menu_action in self._ACTION_MAP if menu_action is not None else True 411 ) and self._state_tracker.get_episode_metric(QUEST_MENU_METRIC) 412 413 414class BombermanQuestBattleAction(BombermanMaxBattleAction): 415 REQUIRED_STATE_TRACKER = BombermanQuestTracker 416 REQUIRED_STATE_PARSER = BombermanQuestParser 417 _ACTION_MAP = { 418 "bomb": LowLevelActions.PRESS_BUTTON_A, 419 "item": LowLevelActions.PRESS_BUTTON_B, 420 "up": LowLevelActions.PRESS_ARROW_UP, 421 "down": LowLevelActions.PRESS_ARROW_DOWN, 422 "left": LowLevelActions.PRESS_ARROW_LEFT, 423 "right": LowLevelActions.PRESS_ARROW_RIGHT, 424 } 425 426 def is_valid(self, **kwargs): 427 battle_action = kwargs.get("battle_action") 428 return ( 429 battle_action in self._ACTION_MAP if battle_action is not None else True 430 ) and self._state_tracker.get_episode_metric(QUEST_BATTLE_METRIC)
125class BombermanMaxMoveAction(_MoveAction): 126 REQUIRED_STATE_TRACKER = BombermanMaxTracker 127 REQUIRED_STATE_PARSER = BombermanMaxParser 128 _DIRECTION_TO_ACTION = { 129 "up": LowLevelActions.PRESS_ARROW_UP, 130 "down": LowLevelActions.PRESS_ARROW_DOWN, 131 "left": LowLevelActions.PRESS_ARROW_LEFT, 132 "right": LowLevelActions.PRESS_ARROW_RIGHT, 133 } 134 _BLOCKING_METRICS = [MAX_MENU_METRIC, MAX_BATTLE_METRIC]
Abstract base class for high level actions.
The execute() method performs the high level action and returns a list of state tracker reports after each low level action executed, along with an action success status.
The docstring of each subclass should specify the interpretation of the action success status.
The execute() method may also return some additional information in the final state tracker report under the "core" key, "action_return" subkey (usually as a dict itself)
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
137class BombermanMaxPlaceBombAction(_MetricGatedSingleAction): 138 REQUIRED_STATE_TRACKER = BombermanMaxTracker 139 REQUIRED_STATE_PARSER = BombermanMaxParser 140 _BUTTON = LowLevelActions.PRESS_BUTTON_A 141 _REQUIRED_METRICS_FALSE = [MAX_MENU_METRIC, MAX_BATTLE_METRIC] 142 143 @staticmethod 144 def get_action_name() -> str: 145 return "PlaceBomb"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
148class BombermanMaxKickBombAction(_MetricGatedSingleAction): 149 REQUIRED_STATE_TRACKER = BombermanMaxTracker 150 REQUIRED_STATE_PARSER = BombermanMaxParser 151 _BUTTON = LowLevelActions.PRESS_BUTTON_B 152 _REQUIRED_METRICS_FALSE = [MAX_MENU_METRIC, MAX_BATTLE_METRIC] 153 154 @staticmethod 155 def get_action_name() -> str: 156 return "KickBomb"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
159class BombermanMaxOpenMenuAction(_MetricGatedSingleAction): 160 REQUIRED_STATE_TRACKER = BombermanMaxTracker 161 REQUIRED_STATE_PARSER = BombermanMaxParser 162 _BUTTON = LowLevelActions.PRESS_BUTTON_START 163 _REQUIRED_METRICS_FALSE = [MAX_MENU_METRIC, MAX_BATTLE_METRIC] 164 165 def _execute(self): 166 self._emulator.step(self._BUTTON) 167 report = self._state_tracker.report() 168 return [report], 0 if self._state_tracker.get_episode_metric(MAX_MENU_METRIC) else -1 169 170 @staticmethod 171 def get_action_name() -> str: 172 return "OpenMenu"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
175class BombermanMaxCloseMenuAction(_MetricGatedSingleAction): 176 REQUIRED_STATE_TRACKER = BombermanMaxTracker 177 REQUIRED_STATE_PARSER = BombermanMaxParser 178 _BUTTON = LowLevelActions.PRESS_BUTTON_START 179 _REQUIRED_METRICS_TRUE = [MAX_MENU_METRIC] 180 181 def _execute(self): 182 self._emulator.step(self._BUTTON) 183 report = self._state_tracker.report() 184 return [report], 0 if not self._state_tracker.get_episode_metric(MAX_MENU_METRIC) else -1 185 186 @staticmethod 187 def get_action_name() -> str: 188 return "CloseMenu"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
233class BombermanMaxBattleAction(SingleHighLevelAction): 234 REQUIRED_STATE_TRACKER = BombermanMaxTracker 235 REQUIRED_STATE_PARSER = BombermanMaxParser 236 _ACTION_MAP = { 237 "bomb": LowLevelActions.PRESS_BUTTON_A, 238 "up": LowLevelActions.PRESS_ARROW_UP, 239 "down": LowLevelActions.PRESS_ARROW_DOWN, 240 "left": LowLevelActions.PRESS_ARROW_LEFT, 241 "right": LowLevelActions.PRESS_ARROW_RIGHT, 242 } 243 244 def get_action_space(self): 245 return Discrete(len(self._ACTION_MAP)) 246 247 def space_to_parameters(self, space_action): 248 keys = list(self._ACTION_MAP) 249 if space_action < 0 or space_action >= len(keys): 250 return None 251 return {"battle_action": keys[space_action]} 252 253 def parameters_to_space(self, battle_action: str): 254 keys = list(self._ACTION_MAP) 255 return keys.index(battle_action) if battle_action in keys else None 256 257 def is_valid(self, **kwargs): 258 battle_action = kwargs.get("battle_action") 259 return ( 260 battle_action in self._ACTION_MAP if battle_action is not None else True 261 ) and self._state_tracker.get_episode_metric(MAX_BATTLE_METRIC) 262 263 def _execute(self, battle_action: str): 264 previous = self._emulator.get_current_frame() 265 frames, _ = self._emulator.step(self._ACTION_MAP[battle_action]) 266 report = self._state_tracker.report() 267 return [report], 0 if frame_changed(previous, frames[-1]) else -1 268 269 @staticmethod 270 def get_action_name(battle_action: str) -> str: 271 return f"Battle {battle_action}"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns the Gym defined Space that characterizes the high level action's parameter space.
You can use this API to get a Space for sampling high level actions of this type.
Returns:
Space: The Gym space that characterizes the high level action's parameter space.
247 def space_to_parameters(self, space_action): 248 keys = list(self._ACTION_MAP) 249 if space_action < 0 or space_action >= len(keys): 250 return None 251 return {"battle_action": keys[space_action]}
Converts a Gym space action into high level action parameters. If the provided space action is invalid, return None.
Parameters
- space_action: The action in the high level action's parameter space.
Returns
The high level action's parameters corresponding to the space action.
253 def parameters_to_space(self, battle_action: str): 254 keys = list(self._ACTION_MAP) 255 return keys.index(battle_action) if battle_action in keys else None
Converts high level action parameters into a Gym space action. If the provided parameters are invalid, return None.
Parameters
- kwargs: The high level action's parameters.
Returns
The action in the high level action's parameter space.
257 def is_valid(self, **kwargs): 258 battle_action = kwargs.get("battle_action") 259 return ( 260 battle_action in self._ACTION_MAP if battle_action is not None else True 261 ) and self._state_tracker.get_episode_metric(MAX_BATTLE_METRIC)
Checks if the high level action can be performed in the current state. If kwargs is empty, then must check whether there exists any valid way to perform the action.
Arguments:
- **kwargs: Additional arguments required for the specific high level action.
Returns:
bool: Whether the action is valid in the current state.
269 @staticmethod 270 def get_action_name(battle_action: str) -> str: 271 return f"Battle {battle_action}"
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
274class BombermanPocketMoveAction(_MoveAction): 275 REQUIRED_STATE_TRACKER = BombermanPocketTracker 276 REQUIRED_STATE_PARSER = BombermanPocketParser 277 _DIRECTION_TO_ACTION = { 278 "left": LowLevelActions.PRESS_ARROW_LEFT, 279 "right": LowLevelActions.PRESS_ARROW_RIGHT, 280 } 281 _BLOCKING_METRICS = [POCKET_MENU_METRIC]
Abstract base class for high level actions.
The execute() method performs the high level action and returns a list of state tracker reports after each low level action executed, along with an action success status.
The docstring of each subclass should specify the interpretation of the action success status.
The execute() method may also return some additional information in the final state tracker report under the "core" key, "action_return" subkey (usually as a dict itself)
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
284class BombermanPocketJumpAction(_MetricGatedSingleAction): 285 REQUIRED_STATE_TRACKER = BombermanPocketTracker 286 REQUIRED_STATE_PARSER = BombermanPocketParser 287 _BUTTON = LowLevelActions.PRESS_BUTTON_B 288 _REQUIRED_METRICS_FALSE = [POCKET_MENU_METRIC] 289 290 @staticmethod 291 def get_action_name() -> str: 292 return "Jump"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
295class BombermanPocketPlaceBombAction(_MetricGatedSingleAction): 296 REQUIRED_STATE_TRACKER = BombermanPocketTracker 297 REQUIRED_STATE_PARSER = BombermanPocketParser 298 _BUTTON = LowLevelActions.PRESS_BUTTON_A 299 _REQUIRED_METRICS_FALSE = [POCKET_MENU_METRIC] 300 301 @staticmethod 302 def get_action_name() -> str: 303 return "PlaceBomb"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
306class BombermanPocketOpenPauseMenuAction(_MetricGatedSingleAction): 307 REQUIRED_STATE_TRACKER = BombermanPocketTracker 308 REQUIRED_STATE_PARSER = BombermanPocketParser 309 _BUTTON = LowLevelActions.PRESS_BUTTON_START 310 _REQUIRED_METRICS_FALSE = [POCKET_MENU_METRIC] 311 312 def _execute(self): 313 self._emulator.step(self._BUTTON) 314 report = self._state_tracker.report() 315 return [report], 0 if self._state_tracker.get_episode_metric(POCKET_MENU_METRIC) else -1 316 317 @staticmethod 318 def get_action_name() -> str: 319 return "OpenPauseMenu"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
322class BombermanPocketClosePauseMenuAction(_MetricGatedSingleAction): 323 REQUIRED_STATE_TRACKER = BombermanPocketTracker 324 REQUIRED_STATE_PARSER = BombermanPocketParser 325 _BUTTON = LowLevelActions.PRESS_BUTTON_START 326 _REQUIRED_METRICS_TRUE = [POCKET_MENU_METRIC] 327 328 def _execute(self): 329 self._emulator.step(self._BUTTON) 330 report = self._state_tracker.report() 331 return [report], 0 if not self._state_tracker.get_episode_metric(POCKET_MENU_METRIC) else -1 332 333 @staticmethod 334 def get_action_name() -> str: 335 return "ClosePauseMenu"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
338class BombermanQuestMoveAction(_MoveAction): 339 REQUIRED_STATE_TRACKER = BombermanQuestTracker 340 REQUIRED_STATE_PARSER = BombermanQuestParser 341 _DIRECTION_TO_ACTION = { 342 "up": LowLevelActions.PRESS_ARROW_UP, 343 "down": LowLevelActions.PRESS_ARROW_DOWN, 344 "left": LowLevelActions.PRESS_ARROW_LEFT, 345 "right": LowLevelActions.PRESS_ARROW_RIGHT, 346 } 347 _BLOCKING_METRICS = [QUEST_MENU_METRIC, QUEST_BATTLE_METRIC]
Abstract base class for high level actions.
The execute() method performs the high level action and returns a list of state tracker reports after each low level action executed, along with an action success status.
The docstring of each subclass should specify the interpretation of the action success status.
The execute() method may also return some additional information in the final state tracker report under the "core" key, "action_return" subkey (usually as a dict itself)
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
350class BombermanQuestPlaceBombAction(_MetricGatedSingleAction): 351 REQUIRED_STATE_TRACKER = BombermanQuestTracker 352 REQUIRED_STATE_PARSER = BombermanQuestParser 353 _BUTTON = LowLevelActions.PRESS_BUTTON_A 354 _REQUIRED_METRICS_FALSE = [QUEST_MENU_METRIC, QUEST_BATTLE_METRIC] 355 356 @staticmethod 357 def get_action_name() -> str: 358 return "PlaceBomb"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
361class BombermanQuestUseBButtonItemAction(_MetricGatedSingleAction): 362 REQUIRED_STATE_TRACKER = BombermanQuestTracker 363 REQUIRED_STATE_PARSER = BombermanQuestParser 364 _BUTTON = LowLevelActions.PRESS_BUTTON_B 365 _REQUIRED_METRICS_FALSE = [QUEST_MENU_METRIC, QUEST_BATTLE_METRIC] 366 367 @staticmethod 368 def get_action_name() -> str: 369 return "UseBButtonItem"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
372class BombermanQuestOpenMenuAction(_MetricGatedSingleAction): 373 REQUIRED_STATE_TRACKER = BombermanQuestTracker 374 REQUIRED_STATE_PARSER = BombermanQuestParser 375 _BUTTON = LowLevelActions.PRESS_BUTTON_START 376 _REQUIRED_METRICS_FALSE = [QUEST_MENU_METRIC, QUEST_BATTLE_METRIC] 377 378 def _execute(self): 379 self._emulator.step(self._BUTTON) 380 report = self._state_tracker.report() 381 return [report], 0 if self._state_tracker.get_episode_metric(QUEST_MENU_METRIC) else -1 382 383 @staticmethod 384 def get_action_name() -> str: 385 return "OpenMenu"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
388class BombermanQuestCloseMenuAction(_MetricGatedSingleAction): 389 REQUIRED_STATE_TRACKER = BombermanQuestTracker 390 REQUIRED_STATE_PARSER = BombermanQuestParser 391 _BUTTON = LowLevelActions.PRESS_BUTTON_START 392 _REQUIRED_METRICS_TRUE = [QUEST_MENU_METRIC] 393 394 def _execute(self): 395 self._emulator.step(self._BUTTON) 396 report = self._state_tracker.report() 397 return [report], 0 if not self._state_tracker.get_episode_metric(QUEST_MENU_METRIC) else -1 398 399 @staticmethod 400 def get_action_name() -> str: 401 return "CloseMenu"
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
Returns a human readable name for the high level action with the given parameters.
Parameters
- kwargs: The high level action's parameters.
Returns
A human readable name for the high level action.
Inherited Members
415class BombermanQuestBattleAction(BombermanMaxBattleAction): 416 REQUIRED_STATE_TRACKER = BombermanQuestTracker 417 REQUIRED_STATE_PARSER = BombermanQuestParser 418 _ACTION_MAP = { 419 "bomb": LowLevelActions.PRESS_BUTTON_A, 420 "item": LowLevelActions.PRESS_BUTTON_B, 421 "up": LowLevelActions.PRESS_ARROW_UP, 422 "down": LowLevelActions.PRESS_ARROW_DOWN, 423 "left": LowLevelActions.PRESS_ARROW_LEFT, 424 "right": LowLevelActions.PRESS_ARROW_RIGHT, 425 } 426 427 def is_valid(self, **kwargs): 428 battle_action = kwargs.get("battle_action") 429 return ( 430 battle_action in self._ACTION_MAP if battle_action is not None else True 431 ) and self._state_tracker.get_episode_metric(QUEST_BATTLE_METRIC)
An abstract class for a high level action that has only one possible parameterization.
The state tracker that tracks the minimal state information required for the action to function.
The state parser that parses the minimal state information required for the action to function.
427 def is_valid(self, **kwargs): 428 battle_action = kwargs.get("battle_action") 429 return ( 430 battle_action in self._ACTION_MAP if battle_action is not None else True 431 ) and self._state_tracker.get_episode_metric(QUEST_BATTLE_METRIC)
Checks if the high level action can be performed in the current state. If kwargs is empty, then must check whether there exists any valid way to perform the action.
Arguments:
- **kwargs: Additional arguments required for the specific high level action.
Returns:
bool: Whether the action is valid in the current state.