gameboy_worlds.emulation.runes_of_virtue.base_metrics
1from typing import Optional 2 3import numpy as np 4 5from gameboy_worlds.emulation.runes_of_virtue.parsers import ( 6 AgentState, 7 RunesOfVirtueStateParser, 8) 9from gameboy_worlds.emulation.tracker import MetricGroup, OCRegionMetric 10 11 12class CoreRunesOfVirtueMetrics(MetricGroup): 13 """ 14 Runes of Virtue specific core metrics. 15 16 Reports: 17 - agent_state: The AgentState info. Is either FREE_ROAM or IN_MENU. 18 19 Final Reports: 20 - None 21 """ 22 23 NAME = "runes_of_virtue_core" 24 REQUIRED_PARSER = RunesOfVirtueStateParser 25 26 def reset(self, first: bool = False): 27 self.current_state: AgentState = AgentState.FREE_ROAM 28 self._previous_state = self.current_state 29 30 def close(self): 31 self.reset() 32 33 def step(self, current_frame: np.ndarray, recent_frames: Optional[np.ndarray]): 34 self._previous_state = self.current_state 35 self.current_state = self.state_parser.get_agent_state(current_frame) 36 37 def report(self) -> dict: 38 return {"agent_state": self.current_state} 39 40 def report_final(self) -> dict: 41 return {} 42 43 44class RunesOfVirtueOCRMetric(OCRegionMetric): 45 """ 46 Captures Runes of Virtue dialogue regions for downstream OCR. 47 """ 48 49 REQUIRED_PARSER = RunesOfVirtueStateParser 50 51 def start(self): 52 self.kinds = {"dialogue": self.state_parser.get_dialogue_ocr_region_name()} 53 super().start() 54 55 def can_read_kind(self, current_frame: np.ndarray, kind: str) -> bool: 56 self.state_parser: RunesOfVirtueStateParser 57 if kind == "dialogue": 58 in_dialogue = self.state_parser.dialogue_box_open( 59 current_screen=current_frame 60 ) 61 dialogue_empty = self.state_parser.dialogue_box_empty( 62 current_screen=current_frame 63 ) 64 in_menu = self.state_parser.is_in_menu(current_screen=current_frame) 65 return in_dialogue and not dialogue_empty and not in_menu 66 return False
13class CoreRunesOfVirtueMetrics(MetricGroup): 14 """ 15 Runes of Virtue specific core metrics. 16 17 Reports: 18 - agent_state: The AgentState info. Is either FREE_ROAM or IN_MENU. 19 20 Final Reports: 21 - None 22 """ 23 24 NAME = "runes_of_virtue_core" 25 REQUIRED_PARSER = RunesOfVirtueStateParser 26 27 def reset(self, first: bool = False): 28 self.current_state: AgentState = AgentState.FREE_ROAM 29 self._previous_state = self.current_state 30 31 def close(self): 32 self.reset() 33 34 def step(self, current_frame: np.ndarray, recent_frames: Optional[np.ndarray]): 35 self._previous_state = self.current_state 36 self.current_state = self.state_parser.get_agent_state(current_frame) 37 38 def report(self) -> dict: 39 return {"agent_state": self.current_state} 40 41 def report_final(self) -> dict: 42 return {}
Runes of Virtue specific core metrics.
Reports:
- agent_state: The AgentState info. Is either FREE_ROAM or IN_MENU.
Final Reports:
- None
The StateParser which implements the minimum required functionality for this MetricGroup to work.
27 def reset(self, first: bool = False): 28 self.current_state: AgentState = AgentState.FREE_ROAM 29 self._previous_state = self.current_state
Called when environment resets.
Arguments:
- first (bool): Whether this is the first reset of the environment. If True, might need to aggregate metrics into running final totals.
Called when environment closes. Good for computing summary stats.
Step will not be called after this.
34 def step(self, current_frame: np.ndarray, recent_frames: Optional[np.ndarray]): 35 self._previous_state = self.current_state 36 self.current_state = self.state_parser.get_agent_state(current_frame)
Called each environment step to update metrics.
Arguments:
- current_frame (np.ndarray): The current frame rendered by the emulator.
- recent_frames (Optional[np.ndarray]): The stack of frames that were rendered during the last action. Shape is [n_frames, height, width, channels]. Can be None if rendering is disabled.
Return metrics as dictionary for instantaneous variable tracking.
Returns
Dictionary of metrics
45class RunesOfVirtueOCRMetric(OCRegionMetric): 46 """ 47 Captures Runes of Virtue dialogue regions for downstream OCR. 48 """ 49 50 REQUIRED_PARSER = RunesOfVirtueStateParser 51 52 def start(self): 53 self.kinds = {"dialogue": self.state_parser.get_dialogue_ocr_region_name()} 54 super().start() 55 56 def can_read_kind(self, current_frame: np.ndarray, kind: str) -> bool: 57 self.state_parser: RunesOfVirtueStateParser 58 if kind == "dialogue": 59 in_dialogue = self.state_parser.dialogue_box_open( 60 current_screen=current_frame 61 ) 62 dialogue_empty = self.state_parser.dialogue_box_empty( 63 current_screen=current_frame 64 ) 65 in_menu = self.state_parser.is_in_menu(current_screen=current_frame) 66 return in_dialogue and not dialogue_empty and not in_menu 67 return False
Captures Runes of Virtue dialogue regions for downstream OCR.
The StateParser which implements the minimum required functionality for this MetricGroup to work.
52 def start(self): 53 self.kinds = {"dialogue": self.state_parser.get_dialogue_ocr_region_name()} 54 super().start()
Assumes the child has initialized a dict called self.kinds which tracks the various kinds of OCR that could be done. self.kinds should be in the form: {kind: region_name} where region_name is the name of the region to OCR for that kind. Will track ocr captured region results in form of list of dictionaries where these kinds are keys.
56 def can_read_kind(self, current_frame: np.ndarray, kind: str) -> bool: 57 self.state_parser: RunesOfVirtueStateParser 58 if kind == "dialogue": 59 in_dialogue = self.state_parser.dialogue_box_open( 60 current_screen=current_frame 61 ) 62 dialogue_empty = self.state_parser.dialogue_box_empty( 63 current_screen=current_frame 64 ) 65 in_menu = self.state_parser.is_in_menu(current_screen=current_frame) 66 return in_dialogue and not dialogue_empty and not in_menu 67 return False
Checks if the frame has text for the given kind.
Arguments:
- frame (np.ndarray): The frame to check.
- kind (str): The kind of text to check for.