gameboy_worlds.emulation.legend_of_zelda.parsers

  1from gameboy_worlds.utils import verify_parameters, log_error
  2from gameboy_worlds.emulation.parser import StateParser, NamedScreenRegion
  3import os
  4from typing import List, Tuple, Optional
  5
  6
  7def _get_proper_regions(
  8    override_regions: List[Tuple[str, int, int, int, int]],
  9    base_regions: List[Tuple[str, int, int, int, int]],
 10) -> List[Tuple[str, int, int, int, int]]:
 11    if len(override_regions) == 0:
 12        return base_regions
 13    proper_regions = override_regions.copy()
 14    override_names = [region[0] for region in override_regions]
 15    for region in base_regions:
 16        if region[0] not in override_names:
 17            proper_regions.append(region)
 18    return proper_regions
 19
 20
 21class BaseLegendOfZeldaParser(StateParser):
 22    """
 23    Minimal state parser for Legend of Zelda GameBoy variants.
 24
 25    This parser only provides the ROM data path required by the base StateParser.
 26    No custom screen regions or metrics are defined.
 27    """
 28
 29    COMMON_REGIONS = [
 30        ("health_bar", 102, 128, 36, 8),
 31        ("playable_area", 0, 0, 159, 143),
 32        ("dialogue_top", 8, 8, 143, 5),
 33        ("dialogue_bottom", 8, 80, 143, 5),
 34        ("dialogue_top_ocr", 9, 9, 141, 36),
 35        ("dialogue_bottom_ocr", 9, 81, 141, 36),
 36    ]
 37
 38    """ List of common named screen regions for Pokémon games.
 39
 40    - health_bar_top: When the health bar appears at the top, it signifies that the inventory is open.
 41
 42    - playable_area: The playable region turns white when doors are used.
 43
 44    - dialogue_top: Dialogues may sometimes appear at the top.
 45
 46    - dialogue_bottom: Dialogues may sometimes appear at the bottom.
 47
 48    - dialogue_top_ocr/dialogue_bottom_ocr: Larger dialogue crops used for OCR.
 49    """
 50
 51    def __init__(
 52        self,
 53        variant: str,
 54        pyboy,
 55        parameters,
 56        override_regions: Optional[List[Tuple[str, int, int, int, int]]] = None,
 57    ):
 58        """
 59        Args:
 60            variant: Zelda variant string (e.g., legend_of_zelda_links_awakening).
 61            pyboy: PyBoy emulator instance.
 62            parameters: Project parameters loaded from configs.
 63        """
 64        verify_parameters(parameters)
 65        if f"{variant}_rom_data_path" not in parameters:
 66            log_error(
 67                f"ROM data path not found for variant: {variant}. Add {variant}_rom_data_path to the config files.",
 68                parameters,
 69            )
 70        self.variant = variant
 71        self.rom_data_path = parameters[f"{variant}_rom_data_path"]
 72        captures_dir = self.rom_data_path + "/captures/"
 73        named_screen_regions = []
 74
 75        if override_regions is None:
 76            override_regions = []
 77        regions = _get_proper_regions(
 78            override_regions=override_regions,
 79            base_regions=self.COMMON_REGIONS,
 80        )
 81
 82        # for region_name, x, y, w, h in self.COMMON_REGIONS:
 83        for region_name, x, y, w, h in regions:
 84            region = NamedScreenRegion(
 85                region_name,
 86                x,
 87                y,
 88                w,
 89                h,
 90                parameters=parameters,
 91                target_path=os.path.join(captures_dir, region_name),
 92            )
 93            named_screen_regions.append(region)
 94
 95        super().__init__(pyboy, parameters, named_screen_regions)
 96
 97    def __repr__(self) -> str:
 98        return f"<BaseLegendOfZeldaParser(variant={self.variant})>"
 99
100    def is_in_dialogue(self, current_screen) -> bool:
101        """
102        Returns True if the game is in dialogue
103        """
104        in_dialogue_top = self.named_region_matches_target(
105            current_screen, "dialogue_top"
106        )
107        in_dialogue_bottom = self.named_region_matches_target(
108            current_screen, "dialogue_bottom"
109        )
110        return in_dialogue_top or in_dialogue_bottom
111
112    def is_scene_transition(self, current_screen) -> bool:
113        """
114        Returns True if the game is in dialogue
115        """
116        transition = self.named_region_matches_target(current_screen, "playable_area")
117        return transition
118
119    def is_in_inventory(self, current_screen) -> bool:
120        return False
121
122    def is_in_cutscene(self, current_screen) -> bool:
123        return False
124
125    def get_agent_state(self, current_screen) -> str:
126        if self.is_scene_transition(current_screen):
127            return "scene_transition"
128        if self.is_in_dialogue(current_screen):
129            return "in_dialogue"
130        if self.is_in_inventory(current_screen):
131            return "in_inventory"
132        #if self.is_in_cutscene(current_screen):
133        #    return "in_cutscene" 
134        #need to fix this as it is giving in cutscene by checking the specfic health bar at that point
135        return "free_roam"
136
137
138class LegendOfZeldaLinksAwakeningParser(BaseLegendOfZeldaParser):
139    def __init__(self, pyboy, parameters):
140        override_regions = [
141            ("health_bar_top", 102, 0, 36, 8),
142            ("equipped_action_1", 7, 130, 8, 10),
143            ("equipped_action_2", 46, 130, 8, 10),
144            ("owl_tracker", 70, 30, 13, 16),
145            ("shield_tracker", 106, 54, 7, 8),
146            ("outside_tarinhouse_tracker", 122, 20, 5, 10),
147            ("kid_screen_tracker", 154, 49, 5, 8),
148            ("library", 33, 17, 12, 13),
149            ("signboard", 32, 17, 14, 10),
150            ("cash_counter_tracker", 94, 84, 15, 10),
151            ("shop_signboard_tracker", 57, 73, 30, 5),
152            ("call_booth", 76, 50, 20, 12),
153            ("telephone_tracker", 81, 76, 13, 9),
154            ("telephone_speech_tracker", 25, 16, 53, 7),
155            ("bush_outside_forest", 97, 33, 13, 11),
156            ("brave_keyword_tracker", 102, 15, 38, 8),
157            ("open_chest_tracker", 64, 49, 14, 7),
158            ("stone_break_tracker", 49, 35, 12, 40),
159            ("bring_keyword_tracker", 24, 86, 56, 10),
160            ("skeleton_tracker", 63, 78, 18, 19),
161            ("skeleton2_tracker", 63, 32, 18, 14),
162            ("no_grass", 79, 36, 14, 8),
163            ("diamond_tracker", 47, 65, 17, 10),
164            ("house_right_window", 106, 63, 25, 8),
165            ("stool", 80, 81, 14, 10),
166            ("char_onstairs", 46, 60, 18, 18),
167            ("pot", 80, 33, 15, 12),
168            ("pond", 55, 39, 51, 31),
169            ("signboard_tracker", 28, 30, 21, 18),
170            ("witch_tracker", 62, 52, 18, 26),
171            ("pots_tracker", 16, 80, 15, 32),
172            ("gemstone", 79, 64, 14, 13),
173            ("no_weapon", 45, 0, 27, 16),
174            ("yes_weapon", 46, 0, 26, 15),
175            ("girl", 30, 62, 17, 15),
176            ("piece", 71, 54, 17, 28),
177            ("shroom_taker", 26, 24, 20, 24),
178            ("shroom_sword", 40, 0, 36, 16),
179            ("shroom_shield", 2, 3, 31, 9),
180            ("signboard2", 44, 13, 25, 19),
181            ("empty_land", 0, 0, 160, 80),
182            ("pineapple", 46, 64, 17, 16),
183            ("granny", 113, 81, 15, 13),
184            ("empty_carpet", 48, 80, 61, 31),
185            ("chimney", 64, 49, 15, 13),
186            ("empty_track", 95, 96, 16, 31),
187            ("wood", 96, 50, 15, 12),
188            ("wood2", 28, 14, 116, 16),
189            ("block", 129, 16, 15, 111),
190            ("purplestone", 47, 67, 17, 11),
191            ("too_heavy", 9, 9, 89, 35),
192            ("boy2nd", 12, 83, 116, 32),
193            ("dirt", 14, 93, 19, 19),
194            ("dirt2", 15, 61, 20, 18),
195            ("tree", 100, 51, 24, 16),
196            ("boysay", 12, 13, 108, 29),
197            ("railing", 10, 46, 69, 7),
198            ("palmt", 76, 53, 11, 9),
199            ("gameover", 46, 38, 67, 19),
200            ("tileslong", 128, 18, 15, 108),
201            ("boardsign", 45, 14, 19, 18),
202            ("potsbelow", 14, 94, 35, 19),
203            ("go_out_dialogue", 8, 10, 136, 34),
204            ("twopurple", 78, 14, 19, 35),
205            ("treerighthouse", 109, 32, 36, 26),
206            ("treestopr", 64, 2, 61, 37),
207            ("onewood", 69, 65, 13, 13),
208            ("chunkgrass", 31, 32, 64, 47),
209        ]
210
211        """
212        - health_bar: Health bar appears at the bottom in legends of zelda link's awakening.
213        - equipped_action_1: Action shown on the bottom-left side of the screen (triggered by A).
214        - equipped_action_2: Action shown to the right of equipped_action_1 (triggered by S).
215        - outside_tarinhouse_tracker: fence outside the house
216        - kid_screen_tracker: represents the tree on the right when we can see kids playing
217        """
218        super().__init__(
219            variant="legend_of_zelda_links_awakening",
220            pyboy=pyboy,
221            parameters=parameters,
222            override_regions=override_regions,
223        )
224
225    def is_in_cutscene(self, current_screen) -> bool:
226        """
227        Returns True if the game is in cutscene
228        """
229        in_cutscence_1 = self.named_region_matches_target(current_screen, "health_bar")
230        in_cutscence_2 = self.named_region_matches_target(
231            current_screen, "health_bar_top"
232        )
233        return not (in_cutscence_1 or in_cutscence_2)
234
235    def is_in_inventory(self, current_screen) -> bool:
236        """
237        Returns True if the game is in inventory
238        """
239        in_inventory = self.named_region_matches_target(
240            current_screen, "health_bar_top"
241        )
242        return in_inventory
243
244
245class LegendOfZeldaTheOracleOfSeasonsParser(BaseLegendOfZeldaParser):
246    def __init__(self, pyboy, parameters):
247        override_regions = [
248            ("health_bar", 102, 0, 36, 8),
249            # ("dialogue_top", 8, 25, 143, 38),
250            # ("dialogue_bottom", 8, 96, 143, 38),
251            ("dialogue_top", 8, 25, 8, 37),
252            ("dialogue_bottom", 8, 97, 8, 37),
253            ("dialogue_top_ocr", 8, 25, 143, 38),
254            ("dialogue_bottom_ocr", 8, 96, 143, 38),
255            ("bricks", 152, 24, 7, 100),
256            ("beer_guy_tracker", 15, 48, 18, 16),
257            ("red_edges", 106, 76, 15, 12),
258            ("after_jump", 79, 67, 16, 14),
259            ("flowers", 112, 31, 16, 17),
260            ("books", 128, 17, 15, 21),
261            ("door", 65, 130, 28, 12),
262            ("bottom_right_shore", 90, 73, 18, 16),
263            ("edge_character", 140, 25, 16, 19),
264            ("char_onstairs", 0, 16, 159, 78),
265            ("bush", 97, 33, 13, 13),
266            ("clocks", 81, 17, 11, 10),
267            ("fireplace", 16, 32, 32, 16),
268            ("oof_its_heavy", 9, 25, 136, 17),
269            ("green_rock_tracker", 79, 47, 16, 14),
270            ("rock", 97, 97, 14, 13),
271            ("almirah", 80, 17, 31, 12),
272            ("signboard_entry", 27, 29, 19, 16),
273            ("grass_right", 118, 79, 18, 14),
274            ("open_gate", 70, 51, 16, 18),
275            ("sign_dialogue", 0, 80, 122, 38),
276            ("left_screen", 0, 16, 108, 128),
277            ("right_screent", 107, 46, 49, 47),
278            ("right_screenb", 110, 30, 48, 18),
279            ("stool", 32, 5, 17, 13),
280            ("bush_of_pier", 47, 71, 40, 18),
281            ("empty_walk", 109, 17, 47, 126),
282            ("cat", 67, 95, 22, 20),
283            ("meow", 64, 96, 62, 30),
284            ("look_no_matter", 16, 79, 78, 39),
285            ("chest", 14, 32, 19, 15),
286            ("dog", 28, 66, 22, 13),
287            ("mickey", 31, 64, 15, 15),
288            ("empty_block", 111, 64, 16, 15),
289            ("shopsign", 72, 73, 30, 6),
290            ("joystick", 81, 66, 13, 12),
291            ("redbook", 113, 80, 13, 12),
292            ("redsnake", 98, 64, 11, 13),
293            ("bluesnake", 46, 64, 17, 12),
294            ("bluesnaketalk", 9, 96, 136, 22),
295            ("redsnaketalk", 10, 97, 124, 17),
296            ("bluetext", 6, 96, 95, 37),
297            ("redtext", 9, 96, 118, 37),
298            ("guyonlava", 0, 111, 159, 30),
299            ("trackempty", 0, 49, 159, 11),
300            ("boundaryred", 0, 129, 159, 12),
301            ("gameover", 41, 7, 77, 16),
302            ("greencarpet", 16, 72, 46, 57),
303            ("holes", 111, 62, 34, 16),
304            ("trunk", 56, 31, 30, 26),
305            ("4cy", 79, 17, 16, 29),
306            ("gobridge", 66, 0, 93, 143),
307            ("emptybeforehole", 1, 18, 158, 27),
308            ("alleytunnel", 32, 18, 45, 27),
309            ("mickeynoddy", 29, 62, 22, 18)
310        ]
311        """
312        - bricks: bricks on the right side of the screen, signifying that the inventory is open.
313        """
314        super().__init__(
315            variant="legend_of_zelda_the_oracle_of_seasons",
316            pyboy=pyboy,
317            parameters=parameters,
318            override_regions=override_regions,
319        )
320
321    def is_in_cutscene(self, current_screen) -> bool:
322        """
323        Returns True if the game is in cutscene
324        """
325        is_in_cutscene = self.named_region_matches_target(current_screen, "health_bar")
326        return not is_in_cutscene
327
328    def is_in_inventory(self, current_screen) -> bool:
329        """
330        Returns True if the game is in inventory
331        """
332        is_in_inventory = self.named_region_matches_target(current_screen, "bricks")
333        return is_in_inventory
334
335
336class LegendOfZeldaParser(LegendOfZeldaLinksAwakeningParser):
337    pass
class BaseLegendOfZeldaParser(gameboy_worlds.emulation.parser.StateParser):
 22class BaseLegendOfZeldaParser(StateParser):
 23    """
 24    Minimal state parser for Legend of Zelda GameBoy variants.
 25
 26    This parser only provides the ROM data path required by the base StateParser.
 27    No custom screen regions or metrics are defined.
 28    """
 29
 30    COMMON_REGIONS = [
 31        ("health_bar", 102, 128, 36, 8),
 32        ("playable_area", 0, 0, 159, 143),
 33        ("dialogue_top", 8, 8, 143, 5),
 34        ("dialogue_bottom", 8, 80, 143, 5),
 35        ("dialogue_top_ocr", 9, 9, 141, 36),
 36        ("dialogue_bottom_ocr", 9, 81, 141, 36),
 37    ]
 38
 39    """ List of common named screen regions for Pokémon games.
 40
 41    - health_bar_top: When the health bar appears at the top, it signifies that the inventory is open.
 42
 43    - playable_area: The playable region turns white when doors are used.
 44
 45    - dialogue_top: Dialogues may sometimes appear at the top.
 46
 47    - dialogue_bottom: Dialogues may sometimes appear at the bottom.
 48
 49    - dialogue_top_ocr/dialogue_bottom_ocr: Larger dialogue crops used for OCR.
 50    """
 51
 52    def __init__(
 53        self,
 54        variant: str,
 55        pyboy,
 56        parameters,
 57        override_regions: Optional[List[Tuple[str, int, int, int, int]]] = None,
 58    ):
 59        """
 60        Args:
 61            variant: Zelda variant string (e.g., legend_of_zelda_links_awakening).
 62            pyboy: PyBoy emulator instance.
 63            parameters: Project parameters loaded from configs.
 64        """
 65        verify_parameters(parameters)
 66        if f"{variant}_rom_data_path" not in parameters:
 67            log_error(
 68                f"ROM data path not found for variant: {variant}. Add {variant}_rom_data_path to the config files.",
 69                parameters,
 70            )
 71        self.variant = variant
 72        self.rom_data_path = parameters[f"{variant}_rom_data_path"]
 73        captures_dir = self.rom_data_path + "/captures/"
 74        named_screen_regions = []
 75
 76        if override_regions is None:
 77            override_regions = []
 78        regions = _get_proper_regions(
 79            override_regions=override_regions,
 80            base_regions=self.COMMON_REGIONS,
 81        )
 82
 83        # for region_name, x, y, w, h in self.COMMON_REGIONS:
 84        for region_name, x, y, w, h in regions:
 85            region = NamedScreenRegion(
 86                region_name,
 87                x,
 88                y,
 89                w,
 90                h,
 91                parameters=parameters,
 92                target_path=os.path.join(captures_dir, region_name),
 93            )
 94            named_screen_regions.append(region)
 95
 96        super().__init__(pyboy, parameters, named_screen_regions)
 97
 98    def __repr__(self) -> str:
 99        return f"<BaseLegendOfZeldaParser(variant={self.variant})>"
100
101    def is_in_dialogue(self, current_screen) -> bool:
102        """
103        Returns True if the game is in dialogue
104        """
105        in_dialogue_top = self.named_region_matches_target(
106            current_screen, "dialogue_top"
107        )
108        in_dialogue_bottom = self.named_region_matches_target(
109            current_screen, "dialogue_bottom"
110        )
111        return in_dialogue_top or in_dialogue_bottom
112
113    def is_scene_transition(self, current_screen) -> bool:
114        """
115        Returns True if the game is in dialogue
116        """
117        transition = self.named_region_matches_target(current_screen, "playable_area")
118        return transition
119
120    def is_in_inventory(self, current_screen) -> bool:
121        return False
122
123    def is_in_cutscene(self, current_screen) -> bool:
124        return False
125
126    def get_agent_state(self, current_screen) -> str:
127        if self.is_scene_transition(current_screen):
128            return "scene_transition"
129        if self.is_in_dialogue(current_screen):
130            return "in_dialogue"
131        if self.is_in_inventory(current_screen):
132            return "in_inventory"
133        #if self.is_in_cutscene(current_screen):
134        #    return "in_cutscene" 
135        #need to fix this as it is giving in cutscene by checking the specfic health bar at that point
136        return "free_roam"

Minimal state parser for Legend of Zelda GameBoy variants.

This parser only provides the ROM data path required by the base StateParser. No custom screen regions or metrics are defined.

BaseLegendOfZeldaParser( variant: str, pyboy, parameters, override_regions: Optional[List[Tuple[str, int, int, int, int]]] = None)
52    def __init__(
53        self,
54        variant: str,
55        pyboy,
56        parameters,
57        override_regions: Optional[List[Tuple[str, int, int, int, int]]] = None,
58    ):
59        """
60        Args:
61            variant: Zelda variant string (e.g., legend_of_zelda_links_awakening).
62            pyboy: PyBoy emulator instance.
63            parameters: Project parameters loaded from configs.
64        """
65        verify_parameters(parameters)
66        if f"{variant}_rom_data_path" not in parameters:
67            log_error(
68                f"ROM data path not found for variant: {variant}. Add {variant}_rom_data_path to the config files.",
69                parameters,
70            )
71        self.variant = variant
72        self.rom_data_path = parameters[f"{variant}_rom_data_path"]
73        captures_dir = self.rom_data_path + "/captures/"
74        named_screen_regions = []
75
76        if override_regions is None:
77            override_regions = []
78        regions = _get_proper_regions(
79            override_regions=override_regions,
80            base_regions=self.COMMON_REGIONS,
81        )
82
83        # for region_name, x, y, w, h in self.COMMON_REGIONS:
84        for region_name, x, y, w, h in regions:
85            region = NamedScreenRegion(
86                region_name,
87                x,
88                y,
89                w,
90                h,
91                parameters=parameters,
92                target_path=os.path.join(captures_dir, region_name),
93            )
94            named_screen_regions.append(region)
95
96        super().__init__(pyboy, parameters, named_screen_regions)
Arguments:
  • variant: Zelda variant string (e.g., legend_of_zelda_links_awakening).
  • pyboy: PyBoy emulator instance.
  • parameters: Project parameters loaded from configs.
COMMON_REGIONS = [('health_bar', 102, 128, 36, 8), ('playable_area', 0, 0, 159, 143), ('dialogue_top', 8, 8, 143, 5), ('dialogue_bottom', 8, 80, 143, 5), ('dialogue_top_ocr', 9, 9, 141, 36), ('dialogue_bottom_ocr', 9, 81, 141, 36)]

List of common named screen regions for Pokémon games.

  • health_bar_top: When the health bar appears at the top, it signifies that the inventory is open.

  • playable_area: The playable region turns white when doors are used.

  • dialogue_top: Dialogues may sometimes appear at the top.

  • dialogue_bottom: Dialogues may sometimes appear at the bottom.

  • dialogue_top_ocr/dialogue_bottom_ocr: Larger dialogue crops used for OCR.

variant
rom_data_path

Path to the rom data directory for the game variant.

def is_in_dialogue(self, current_screen) -> bool:
101    def is_in_dialogue(self, current_screen) -> bool:
102        """
103        Returns True if the game is in dialogue
104        """
105        in_dialogue_top = self.named_region_matches_target(
106            current_screen, "dialogue_top"
107        )
108        in_dialogue_bottom = self.named_region_matches_target(
109            current_screen, "dialogue_bottom"
110        )
111        return in_dialogue_top or in_dialogue_bottom

Returns True if the game is in dialogue

def is_scene_transition(self, current_screen) -> bool:
113    def is_scene_transition(self, current_screen) -> bool:
114        """
115        Returns True if the game is in dialogue
116        """
117        transition = self.named_region_matches_target(current_screen, "playable_area")
118        return transition

Returns True if the game is in dialogue

def is_in_inventory(self, current_screen) -> bool:
120    def is_in_inventory(self, current_screen) -> bool:
121        return False
def is_in_cutscene(self, current_screen) -> bool:
123    def is_in_cutscene(self, current_screen) -> bool:
124        return False
def get_agent_state(self, current_screen) -> str:
126    def get_agent_state(self, current_screen) -> str:
127        if self.is_scene_transition(current_screen):
128            return "scene_transition"
129        if self.is_in_dialogue(current_screen):
130            return "in_dialogue"
131        if self.is_in_inventory(current_screen):
132            return "in_inventory"
133        #if self.is_in_cutscene(current_screen):
134        #    return "in_cutscene" 
135        #need to fix this as it is giving in cutscene by checking the specfic health bar at that point
136        return "free_roam"
class LegendOfZeldaLinksAwakeningParser(BaseLegendOfZeldaParser):
139class LegendOfZeldaLinksAwakeningParser(BaseLegendOfZeldaParser):
140    def __init__(self, pyboy, parameters):
141        override_regions = [
142            ("health_bar_top", 102, 0, 36, 8),
143            ("equipped_action_1", 7, 130, 8, 10),
144            ("equipped_action_2", 46, 130, 8, 10),
145            ("owl_tracker", 70, 30, 13, 16),
146            ("shield_tracker", 106, 54, 7, 8),
147            ("outside_tarinhouse_tracker", 122, 20, 5, 10),
148            ("kid_screen_tracker", 154, 49, 5, 8),
149            ("library", 33, 17, 12, 13),
150            ("signboard", 32, 17, 14, 10),
151            ("cash_counter_tracker", 94, 84, 15, 10),
152            ("shop_signboard_tracker", 57, 73, 30, 5),
153            ("call_booth", 76, 50, 20, 12),
154            ("telephone_tracker", 81, 76, 13, 9),
155            ("telephone_speech_tracker", 25, 16, 53, 7),
156            ("bush_outside_forest", 97, 33, 13, 11),
157            ("brave_keyword_tracker", 102, 15, 38, 8),
158            ("open_chest_tracker", 64, 49, 14, 7),
159            ("stone_break_tracker", 49, 35, 12, 40),
160            ("bring_keyword_tracker", 24, 86, 56, 10),
161            ("skeleton_tracker", 63, 78, 18, 19),
162            ("skeleton2_tracker", 63, 32, 18, 14),
163            ("no_grass", 79, 36, 14, 8),
164            ("diamond_tracker", 47, 65, 17, 10),
165            ("house_right_window", 106, 63, 25, 8),
166            ("stool", 80, 81, 14, 10),
167            ("char_onstairs", 46, 60, 18, 18),
168            ("pot", 80, 33, 15, 12),
169            ("pond", 55, 39, 51, 31),
170            ("signboard_tracker", 28, 30, 21, 18),
171            ("witch_tracker", 62, 52, 18, 26),
172            ("pots_tracker", 16, 80, 15, 32),
173            ("gemstone", 79, 64, 14, 13),
174            ("no_weapon", 45, 0, 27, 16),
175            ("yes_weapon", 46, 0, 26, 15),
176            ("girl", 30, 62, 17, 15),
177            ("piece", 71, 54, 17, 28),
178            ("shroom_taker", 26, 24, 20, 24),
179            ("shroom_sword", 40, 0, 36, 16),
180            ("shroom_shield", 2, 3, 31, 9),
181            ("signboard2", 44, 13, 25, 19),
182            ("empty_land", 0, 0, 160, 80),
183            ("pineapple", 46, 64, 17, 16),
184            ("granny", 113, 81, 15, 13),
185            ("empty_carpet", 48, 80, 61, 31),
186            ("chimney", 64, 49, 15, 13),
187            ("empty_track", 95, 96, 16, 31),
188            ("wood", 96, 50, 15, 12),
189            ("wood2", 28, 14, 116, 16),
190            ("block", 129, 16, 15, 111),
191            ("purplestone", 47, 67, 17, 11),
192            ("too_heavy", 9, 9, 89, 35),
193            ("boy2nd", 12, 83, 116, 32),
194            ("dirt", 14, 93, 19, 19),
195            ("dirt2", 15, 61, 20, 18),
196            ("tree", 100, 51, 24, 16),
197            ("boysay", 12, 13, 108, 29),
198            ("railing", 10, 46, 69, 7),
199            ("palmt", 76, 53, 11, 9),
200            ("gameover", 46, 38, 67, 19),
201            ("tileslong", 128, 18, 15, 108),
202            ("boardsign", 45, 14, 19, 18),
203            ("potsbelow", 14, 94, 35, 19),
204            ("go_out_dialogue", 8, 10, 136, 34),
205            ("twopurple", 78, 14, 19, 35),
206            ("treerighthouse", 109, 32, 36, 26),
207            ("treestopr", 64, 2, 61, 37),
208            ("onewood", 69, 65, 13, 13),
209            ("chunkgrass", 31, 32, 64, 47),
210        ]
211
212        """
213        - health_bar: Health bar appears at the bottom in legends of zelda link's awakening.
214        - equipped_action_1: Action shown on the bottom-left side of the screen (triggered by A).
215        - equipped_action_2: Action shown to the right of equipped_action_1 (triggered by S).
216        - outside_tarinhouse_tracker: fence outside the house
217        - kid_screen_tracker: represents the tree on the right when we can see kids playing
218        """
219        super().__init__(
220            variant="legend_of_zelda_links_awakening",
221            pyboy=pyboy,
222            parameters=parameters,
223            override_regions=override_regions,
224        )
225
226    def is_in_cutscene(self, current_screen) -> bool:
227        """
228        Returns True if the game is in cutscene
229        """
230        in_cutscence_1 = self.named_region_matches_target(current_screen, "health_bar")
231        in_cutscence_2 = self.named_region_matches_target(
232            current_screen, "health_bar_top"
233        )
234        return not (in_cutscence_1 or in_cutscence_2)
235
236    def is_in_inventory(self, current_screen) -> bool:
237        """
238        Returns True if the game is in inventory
239        """
240        in_inventory = self.named_region_matches_target(
241            current_screen, "health_bar_top"
242        )
243        return in_inventory

Minimal state parser for Legend of Zelda GameBoy variants.

This parser only provides the ROM data path required by the base StateParser. No custom screen regions or metrics are defined.

LegendOfZeldaLinksAwakeningParser(pyboy, parameters)
140    def __init__(self, pyboy, parameters):
141        override_regions = [
142            ("health_bar_top", 102, 0, 36, 8),
143            ("equipped_action_1", 7, 130, 8, 10),
144            ("equipped_action_2", 46, 130, 8, 10),
145            ("owl_tracker", 70, 30, 13, 16),
146            ("shield_tracker", 106, 54, 7, 8),
147            ("outside_tarinhouse_tracker", 122, 20, 5, 10),
148            ("kid_screen_tracker", 154, 49, 5, 8),
149            ("library", 33, 17, 12, 13),
150            ("signboard", 32, 17, 14, 10),
151            ("cash_counter_tracker", 94, 84, 15, 10),
152            ("shop_signboard_tracker", 57, 73, 30, 5),
153            ("call_booth", 76, 50, 20, 12),
154            ("telephone_tracker", 81, 76, 13, 9),
155            ("telephone_speech_tracker", 25, 16, 53, 7),
156            ("bush_outside_forest", 97, 33, 13, 11),
157            ("brave_keyword_tracker", 102, 15, 38, 8),
158            ("open_chest_tracker", 64, 49, 14, 7),
159            ("stone_break_tracker", 49, 35, 12, 40),
160            ("bring_keyword_tracker", 24, 86, 56, 10),
161            ("skeleton_tracker", 63, 78, 18, 19),
162            ("skeleton2_tracker", 63, 32, 18, 14),
163            ("no_grass", 79, 36, 14, 8),
164            ("diamond_tracker", 47, 65, 17, 10),
165            ("house_right_window", 106, 63, 25, 8),
166            ("stool", 80, 81, 14, 10),
167            ("char_onstairs", 46, 60, 18, 18),
168            ("pot", 80, 33, 15, 12),
169            ("pond", 55, 39, 51, 31),
170            ("signboard_tracker", 28, 30, 21, 18),
171            ("witch_tracker", 62, 52, 18, 26),
172            ("pots_tracker", 16, 80, 15, 32),
173            ("gemstone", 79, 64, 14, 13),
174            ("no_weapon", 45, 0, 27, 16),
175            ("yes_weapon", 46, 0, 26, 15),
176            ("girl", 30, 62, 17, 15),
177            ("piece", 71, 54, 17, 28),
178            ("shroom_taker", 26, 24, 20, 24),
179            ("shroom_sword", 40, 0, 36, 16),
180            ("shroom_shield", 2, 3, 31, 9),
181            ("signboard2", 44, 13, 25, 19),
182            ("empty_land", 0, 0, 160, 80),
183            ("pineapple", 46, 64, 17, 16),
184            ("granny", 113, 81, 15, 13),
185            ("empty_carpet", 48, 80, 61, 31),
186            ("chimney", 64, 49, 15, 13),
187            ("empty_track", 95, 96, 16, 31),
188            ("wood", 96, 50, 15, 12),
189            ("wood2", 28, 14, 116, 16),
190            ("block", 129, 16, 15, 111),
191            ("purplestone", 47, 67, 17, 11),
192            ("too_heavy", 9, 9, 89, 35),
193            ("boy2nd", 12, 83, 116, 32),
194            ("dirt", 14, 93, 19, 19),
195            ("dirt2", 15, 61, 20, 18),
196            ("tree", 100, 51, 24, 16),
197            ("boysay", 12, 13, 108, 29),
198            ("railing", 10, 46, 69, 7),
199            ("palmt", 76, 53, 11, 9),
200            ("gameover", 46, 38, 67, 19),
201            ("tileslong", 128, 18, 15, 108),
202            ("boardsign", 45, 14, 19, 18),
203            ("potsbelow", 14, 94, 35, 19),
204            ("go_out_dialogue", 8, 10, 136, 34),
205            ("twopurple", 78, 14, 19, 35),
206            ("treerighthouse", 109, 32, 36, 26),
207            ("treestopr", 64, 2, 61, 37),
208            ("onewood", 69, 65, 13, 13),
209            ("chunkgrass", 31, 32, 64, 47),
210        ]
211
212        """
213        - health_bar: Health bar appears at the bottom in legends of zelda link's awakening.
214        - equipped_action_1: Action shown on the bottom-left side of the screen (triggered by A).
215        - equipped_action_2: Action shown to the right of equipped_action_1 (triggered by S).
216        - outside_tarinhouse_tracker: fence outside the house
217        - kid_screen_tracker: represents the tree on the right when we can see kids playing
218        """
219        super().__init__(
220            variant="legend_of_zelda_links_awakening",
221            pyboy=pyboy,
222            parameters=parameters,
223            override_regions=override_regions,
224        )
Arguments:
  • variant: Zelda variant string (e.g., legend_of_zelda_links_awakening).
  • pyboy: PyBoy emulator instance.
  • parameters: Project parameters loaded from configs.
def is_in_cutscene(self, current_screen) -> bool:
226    def is_in_cutscene(self, current_screen) -> bool:
227        """
228        Returns True if the game is in cutscene
229        """
230        in_cutscence_1 = self.named_region_matches_target(current_screen, "health_bar")
231        in_cutscence_2 = self.named_region_matches_target(
232            current_screen, "health_bar_top"
233        )
234        return not (in_cutscence_1 or in_cutscence_2)

Returns True if the game is in cutscene

def is_in_inventory(self, current_screen) -> bool:
236    def is_in_inventory(self, current_screen) -> bool:
237        """
238        Returns True if the game is in inventory
239        """
240        in_inventory = self.named_region_matches_target(
241            current_screen, "health_bar_top"
242        )
243        return in_inventory

Returns True if the game is in inventory

class LegendOfZeldaTheOracleOfSeasonsParser(BaseLegendOfZeldaParser):
246class LegendOfZeldaTheOracleOfSeasonsParser(BaseLegendOfZeldaParser):
247    def __init__(self, pyboy, parameters):
248        override_regions = [
249            ("health_bar", 102, 0, 36, 8),
250            # ("dialogue_top", 8, 25, 143, 38),
251            # ("dialogue_bottom", 8, 96, 143, 38),
252            ("dialogue_top", 8, 25, 8, 37),
253            ("dialogue_bottom", 8, 97, 8, 37),
254            ("dialogue_top_ocr", 8, 25, 143, 38),
255            ("dialogue_bottom_ocr", 8, 96, 143, 38),
256            ("bricks", 152, 24, 7, 100),
257            ("beer_guy_tracker", 15, 48, 18, 16),
258            ("red_edges", 106, 76, 15, 12),
259            ("after_jump", 79, 67, 16, 14),
260            ("flowers", 112, 31, 16, 17),
261            ("books", 128, 17, 15, 21),
262            ("door", 65, 130, 28, 12),
263            ("bottom_right_shore", 90, 73, 18, 16),
264            ("edge_character", 140, 25, 16, 19),
265            ("char_onstairs", 0, 16, 159, 78),
266            ("bush", 97, 33, 13, 13),
267            ("clocks", 81, 17, 11, 10),
268            ("fireplace", 16, 32, 32, 16),
269            ("oof_its_heavy", 9, 25, 136, 17),
270            ("green_rock_tracker", 79, 47, 16, 14),
271            ("rock", 97, 97, 14, 13),
272            ("almirah", 80, 17, 31, 12),
273            ("signboard_entry", 27, 29, 19, 16),
274            ("grass_right", 118, 79, 18, 14),
275            ("open_gate", 70, 51, 16, 18),
276            ("sign_dialogue", 0, 80, 122, 38),
277            ("left_screen", 0, 16, 108, 128),
278            ("right_screent", 107, 46, 49, 47),
279            ("right_screenb", 110, 30, 48, 18),
280            ("stool", 32, 5, 17, 13),
281            ("bush_of_pier", 47, 71, 40, 18),
282            ("empty_walk", 109, 17, 47, 126),
283            ("cat", 67, 95, 22, 20),
284            ("meow", 64, 96, 62, 30),
285            ("look_no_matter", 16, 79, 78, 39),
286            ("chest", 14, 32, 19, 15),
287            ("dog", 28, 66, 22, 13),
288            ("mickey", 31, 64, 15, 15),
289            ("empty_block", 111, 64, 16, 15),
290            ("shopsign", 72, 73, 30, 6),
291            ("joystick", 81, 66, 13, 12),
292            ("redbook", 113, 80, 13, 12),
293            ("redsnake", 98, 64, 11, 13),
294            ("bluesnake", 46, 64, 17, 12),
295            ("bluesnaketalk", 9, 96, 136, 22),
296            ("redsnaketalk", 10, 97, 124, 17),
297            ("bluetext", 6, 96, 95, 37),
298            ("redtext", 9, 96, 118, 37),
299            ("guyonlava", 0, 111, 159, 30),
300            ("trackempty", 0, 49, 159, 11),
301            ("boundaryred", 0, 129, 159, 12),
302            ("gameover", 41, 7, 77, 16),
303            ("greencarpet", 16, 72, 46, 57),
304            ("holes", 111, 62, 34, 16),
305            ("trunk", 56, 31, 30, 26),
306            ("4cy", 79, 17, 16, 29),
307            ("gobridge", 66, 0, 93, 143),
308            ("emptybeforehole", 1, 18, 158, 27),
309            ("alleytunnel", 32, 18, 45, 27),
310            ("mickeynoddy", 29, 62, 22, 18)
311        ]
312        """
313        - bricks: bricks on the right side of the screen, signifying that the inventory is open.
314        """
315        super().__init__(
316            variant="legend_of_zelda_the_oracle_of_seasons",
317            pyboy=pyboy,
318            parameters=parameters,
319            override_regions=override_regions,
320        )
321
322    def is_in_cutscene(self, current_screen) -> bool:
323        """
324        Returns True if the game is in cutscene
325        """
326        is_in_cutscene = self.named_region_matches_target(current_screen, "health_bar")
327        return not is_in_cutscene
328
329    def is_in_inventory(self, current_screen) -> bool:
330        """
331        Returns True if the game is in inventory
332        """
333        is_in_inventory = self.named_region_matches_target(current_screen, "bricks")
334        return is_in_inventory

Minimal state parser for Legend of Zelda GameBoy variants.

This parser only provides the ROM data path required by the base StateParser. No custom screen regions or metrics are defined.

LegendOfZeldaTheOracleOfSeasonsParser(pyboy, parameters)
247    def __init__(self, pyboy, parameters):
248        override_regions = [
249            ("health_bar", 102, 0, 36, 8),
250            # ("dialogue_top", 8, 25, 143, 38),
251            # ("dialogue_bottom", 8, 96, 143, 38),
252            ("dialogue_top", 8, 25, 8, 37),
253            ("dialogue_bottom", 8, 97, 8, 37),
254            ("dialogue_top_ocr", 8, 25, 143, 38),
255            ("dialogue_bottom_ocr", 8, 96, 143, 38),
256            ("bricks", 152, 24, 7, 100),
257            ("beer_guy_tracker", 15, 48, 18, 16),
258            ("red_edges", 106, 76, 15, 12),
259            ("after_jump", 79, 67, 16, 14),
260            ("flowers", 112, 31, 16, 17),
261            ("books", 128, 17, 15, 21),
262            ("door", 65, 130, 28, 12),
263            ("bottom_right_shore", 90, 73, 18, 16),
264            ("edge_character", 140, 25, 16, 19),
265            ("char_onstairs", 0, 16, 159, 78),
266            ("bush", 97, 33, 13, 13),
267            ("clocks", 81, 17, 11, 10),
268            ("fireplace", 16, 32, 32, 16),
269            ("oof_its_heavy", 9, 25, 136, 17),
270            ("green_rock_tracker", 79, 47, 16, 14),
271            ("rock", 97, 97, 14, 13),
272            ("almirah", 80, 17, 31, 12),
273            ("signboard_entry", 27, 29, 19, 16),
274            ("grass_right", 118, 79, 18, 14),
275            ("open_gate", 70, 51, 16, 18),
276            ("sign_dialogue", 0, 80, 122, 38),
277            ("left_screen", 0, 16, 108, 128),
278            ("right_screent", 107, 46, 49, 47),
279            ("right_screenb", 110, 30, 48, 18),
280            ("stool", 32, 5, 17, 13),
281            ("bush_of_pier", 47, 71, 40, 18),
282            ("empty_walk", 109, 17, 47, 126),
283            ("cat", 67, 95, 22, 20),
284            ("meow", 64, 96, 62, 30),
285            ("look_no_matter", 16, 79, 78, 39),
286            ("chest", 14, 32, 19, 15),
287            ("dog", 28, 66, 22, 13),
288            ("mickey", 31, 64, 15, 15),
289            ("empty_block", 111, 64, 16, 15),
290            ("shopsign", 72, 73, 30, 6),
291            ("joystick", 81, 66, 13, 12),
292            ("redbook", 113, 80, 13, 12),
293            ("redsnake", 98, 64, 11, 13),
294            ("bluesnake", 46, 64, 17, 12),
295            ("bluesnaketalk", 9, 96, 136, 22),
296            ("redsnaketalk", 10, 97, 124, 17),
297            ("bluetext", 6, 96, 95, 37),
298            ("redtext", 9, 96, 118, 37),
299            ("guyonlava", 0, 111, 159, 30),
300            ("trackempty", 0, 49, 159, 11),
301            ("boundaryred", 0, 129, 159, 12),
302            ("gameover", 41, 7, 77, 16),
303            ("greencarpet", 16, 72, 46, 57),
304            ("holes", 111, 62, 34, 16),
305            ("trunk", 56, 31, 30, 26),
306            ("4cy", 79, 17, 16, 29),
307            ("gobridge", 66, 0, 93, 143),
308            ("emptybeforehole", 1, 18, 158, 27),
309            ("alleytunnel", 32, 18, 45, 27),
310            ("mickeynoddy", 29, 62, 22, 18)
311        ]
312        """
313        - bricks: bricks on the right side of the screen, signifying that the inventory is open.
314        """
315        super().__init__(
316            variant="legend_of_zelda_the_oracle_of_seasons",
317            pyboy=pyboy,
318            parameters=parameters,
319            override_regions=override_regions,
320        )
Arguments:
  • variant: Zelda variant string (e.g., legend_of_zelda_links_awakening).
  • pyboy: PyBoy emulator instance.
  • parameters: Project parameters loaded from configs.
def is_in_cutscene(self, current_screen) -> bool:
322    def is_in_cutscene(self, current_screen) -> bool:
323        """
324        Returns True if the game is in cutscene
325        """
326        is_in_cutscene = self.named_region_matches_target(current_screen, "health_bar")
327        return not is_in_cutscene

Returns True if the game is in cutscene

def is_in_inventory(self, current_screen) -> bool:
329    def is_in_inventory(self, current_screen) -> bool:
330        """
331        Returns True if the game is in inventory
332        """
333        is_in_inventory = self.named_region_matches_target(current_screen, "bricks")
334        return is_in_inventory

Returns True if the game is in inventory

class LegendOfZeldaParser(LegendOfZeldaLinksAwakeningParser):
337class LegendOfZeldaParser(LegendOfZeldaLinksAwakeningParser):
338    pass

Minimal state parser for Legend of Zelda GameBoy variants.

This parser only provides the ROM data path required by the base StateParser. No custom screen regions or metrics are defined.