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
class CoreRunesOfVirtueMetrics(gameboy_worlds.emulation.tracker.MetricGroup):
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
NAME = 'runes_of_virtue_core'

Name of the MetricGroup.

The StateParser which implements the minimum required functionality for this MetricGroup to work.

def reset(self, first: bool = False):
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.
def close(self):
31    def close(self):
32        self.reset()

Called when environment closes. Good for computing summary stats.

Step will not be called after this.

def step( self, current_frame: numpy.ndarray, recent_frames: Optional[numpy.ndarray]):
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.
def report(self) -> dict:
38    def report(self) -> dict:
39        return {"agent_state": self.current_state}

Return metrics as dictionary for instantaneous variable tracking.

Returns

Dictionary of metrics

def report_final(self) -> dict:
41    def report_final(self) -> dict:
42        return {}

Return metrics as dictionary for logging. Called at end of environment (before close). Will never be called before self.close.

Returns

Dictionary of metrics

class RunesOfVirtueOCRMetric(gameboy_worlds.emulation.tracker.OCRegionMetric):
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.

def start(self):
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.

def can_read_kind(self, current_frame: numpy.ndarray, kind: str) -> bool:
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.