gameboy_worlds.interface.legend_of_zelda.controllers

 1from typing import Dict, Type
 2
 3from gameboy_worlds.interface.controller import Controller
 4from gameboy_worlds.interface.action import HighLevelAction
 5from gameboy_worlds.interface.legend_of_zelda.actions import (
 6    MoveAction,
 7    OpenInventoryAction,
 8    CloseInventoryAction,
 9    SkipDialogueAction,
10    InteractAction,
11    UseOtherInventoryItemAction,
12)
13
14
15class LegendOfZeldaStateWiseController(Controller):
16    ACTIONS = [
17        MoveAction,
18        OpenInventoryAction,
19        CloseInventoryAction,
20        SkipDialogueAction,
21        InteractAction,
22        UseOtherInventoryItemAction,
23    ]
24
25    def string_to_high_level_action(self, input_str):
26        text = input_str.lower().strip()
27        if "(" not in text or ")" not in text:
28            return None, None
29        action_name = text.split("(")[0].strip()
30        action_args_str = text.split("(")[1].split(")")[0].strip()
31        if action_name == "openinventory":
32            return OpenInventoryAction, {}
33        if action_name == "closeinventory":
34            return CloseInventoryAction, {}
35        if action_name == "skipdialogue":
36            return SkipDialogueAction, {}
37        if action_name == "interact":
38            return InteractAction, {}
39        if action_name == "useotherinventoryitem":
40            return UseOtherInventoryItemAction, {}
41        if action_name == "move":
42            parts = [
43                x.strip()
44                for x in action_args_str.replace(",", " ").split(" ")
45                if x.strip()
46            ]
47            if len(parts) != 2:
48                return None, None
49            direction, steps_str = parts
50            if (
51                direction not in ["up", "down", "left", "right"]
52                or not steps_str.isdigit()
53            ):
54                return None, None
55            return MoveAction, {"direction": direction, "steps": int(steps_str)}
56        return None, None
57
58    def get_action_strings(
59        self, return_all: bool = False
60    ) -> Dict[Type[HighLevelAction], str]:
61        free_roam_action_strings = {
62            MoveAction: "move(<up/down/left/right> <steps>): Move in a direction for N steps.",
63            OpenInventoryAction: "openinventory(): Open inventory.",
64            InteractAction: "interact(): Interact using A button.",
65            UseOtherInventoryItemAction: "useotherinventoryitem(): Use the secondary equipped item (B button).",
66        }
67        inventory_action_strings = {
68            CloseInventoryAction: "closeinventory(): Close inventory.",
69        }
70        dialogue_action_strings = {
71            SkipDialogueAction: "skipdialogue(): Progress dialogue.",
72        }
73        if return_all:
74            return {
75                **free_roam_action_strings,
76                **inventory_action_strings,
77                **dialogue_action_strings,
78            }
79        current_state = self._emulator.state_parser.get_agent_state(
80            self._emulator.get_current_frame()
81        )
82        if current_state == "free_roam":
83            return free_roam_action_strings
84        if current_state == "in_inventory":
85            return inventory_action_strings
86        if current_state == "in_dialogue":
87            return dialogue_action_strings
88        if current_state in ["scene_transition", "in_cutscene"]:
89            return {}
90        return {
91            **free_roam_action_strings,
92            **inventory_action_strings,
93            **dialogue_action_strings,
94        }
class LegendOfZeldaStateWiseController(gameboy_worlds.interface.controller.Controller):
16class LegendOfZeldaStateWiseController(Controller):
17    ACTIONS = [
18        MoveAction,
19        OpenInventoryAction,
20        CloseInventoryAction,
21        SkipDialogueAction,
22        InteractAction,
23        UseOtherInventoryItemAction,
24    ]
25
26    def string_to_high_level_action(self, input_str):
27        text = input_str.lower().strip()
28        if "(" not in text or ")" not in text:
29            return None, None
30        action_name = text.split("(")[0].strip()
31        action_args_str = text.split("(")[1].split(")")[0].strip()
32        if action_name == "openinventory":
33            return OpenInventoryAction, {}
34        if action_name == "closeinventory":
35            return CloseInventoryAction, {}
36        if action_name == "skipdialogue":
37            return SkipDialogueAction, {}
38        if action_name == "interact":
39            return InteractAction, {}
40        if action_name == "useotherinventoryitem":
41            return UseOtherInventoryItemAction, {}
42        if action_name == "move":
43            parts = [
44                x.strip()
45                for x in action_args_str.replace(",", " ").split(" ")
46                if x.strip()
47            ]
48            if len(parts) != 2:
49                return None, None
50            direction, steps_str = parts
51            if (
52                direction not in ["up", "down", "left", "right"]
53                or not steps_str.isdigit()
54            ):
55                return None, None
56            return MoveAction, {"direction": direction, "steps": int(steps_str)}
57        return None, None
58
59    def get_action_strings(
60        self, return_all: bool = False
61    ) -> Dict[Type[HighLevelAction], str]:
62        free_roam_action_strings = {
63            MoveAction: "move(<up/down/left/right> <steps>): Move in a direction for N steps.",
64            OpenInventoryAction: "openinventory(): Open inventory.",
65            InteractAction: "interact(): Interact using A button.",
66            UseOtherInventoryItemAction: "useotherinventoryitem(): Use the secondary equipped item (B button).",
67        }
68        inventory_action_strings = {
69            CloseInventoryAction: "closeinventory(): Close inventory.",
70        }
71        dialogue_action_strings = {
72            SkipDialogueAction: "skipdialogue(): Progress dialogue.",
73        }
74        if return_all:
75            return {
76                **free_roam_action_strings,
77                **inventory_action_strings,
78                **dialogue_action_strings,
79            }
80        current_state = self._emulator.state_parser.get_agent_state(
81            self._emulator.get_current_frame()
82        )
83        if current_state == "free_roam":
84            return free_roam_action_strings
85        if current_state == "in_inventory":
86            return inventory_action_strings
87        if current_state == "in_dialogue":
88            return dialogue_action_strings
89        if current_state in ["scene_transition", "in_cutscene"]:
90            return {}
91        return {
92            **free_roam_action_strings,
93            **inventory_action_strings,
94            **dialogue_action_strings,
95        }

Abstract base class for controllers interfacing with the emulator. Handles conversion between high level actions and Gym action spaces.

def string_to_high_level_action(self, input_str):
26    def string_to_high_level_action(self, input_str):
27        text = input_str.lower().strip()
28        if "(" not in text or ")" not in text:
29            return None, None
30        action_name = text.split("(")[0].strip()
31        action_args_str = text.split("(")[1].split(")")[0].strip()
32        if action_name == "openinventory":
33            return OpenInventoryAction, {}
34        if action_name == "closeinventory":
35            return CloseInventoryAction, {}
36        if action_name == "skipdialogue":
37            return SkipDialogueAction, {}
38        if action_name == "interact":
39            return InteractAction, {}
40        if action_name == "useotherinventoryitem":
41            return UseOtherInventoryItemAction, {}
42        if action_name == "move":
43            parts = [
44                x.strip()
45                for x in action_args_str.replace(",", " ").split(" ")
46                if x.strip()
47            ]
48            if len(parts) != 2:
49                return None, None
50            direction, steps_str = parts
51            if (
52                direction not in ["up", "down", "left", "right"]
53                or not steps_str.isdigit()
54            ):
55                return None, None
56            return MoveAction, {"direction": direction, "steps": int(steps_str)}
57        return None, None

Provide a way to map a string input to a HighLevelAction and parameters.

Implement if you want to use the human_step_play method, or if you want to allow a LM based agent to give its actions in text. Must return None, None if the input_str does not map to an action.

def get_action_strings( self, return_all: bool = False) -> Dict[Type[gameboy_worlds.interface.action.HighLevelAction], str]:
59    def get_action_strings(
60        self, return_all: bool = False
61    ) -> Dict[Type[HighLevelAction], str]:
62        free_roam_action_strings = {
63            MoveAction: "move(<up/down/left/right> <steps>): Move in a direction for N steps.",
64            OpenInventoryAction: "openinventory(): Open inventory.",
65            InteractAction: "interact(): Interact using A button.",
66            UseOtherInventoryItemAction: "useotherinventoryitem(): Use the secondary equipped item (B button).",
67        }
68        inventory_action_strings = {
69            CloseInventoryAction: "closeinventory(): Close inventory.",
70        }
71        dialogue_action_strings = {
72            SkipDialogueAction: "skipdialogue(): Progress dialogue.",
73        }
74        if return_all:
75            return {
76                **free_roam_action_strings,
77                **inventory_action_strings,
78                **dialogue_action_strings,
79            }
80        current_state = self._emulator.state_parser.get_agent_state(
81            self._emulator.get_current_frame()
82        )
83        if current_state == "free_roam":
84            return free_roam_action_strings
85        if current_state == "in_inventory":
86            return inventory_action_strings
87        if current_state == "in_dialogue":
88            return dialogue_action_strings
89        if current_state in ["scene_transition", "in_cutscene"]:
90            return {}
91        return {
92            **free_roam_action_strings,
93            **inventory_action_strings,
94            **dialogue_action_strings,
95        }

Provide a way to verbalize the allowed high level actions, along with the format of the input parameters. Useful for prompting a VLM to choose an action.

This should match the mapping in string_to_high_level_action

Parameters
  • return_all: If True, returns all possible actions and parameter formats. If False, returns only the actions that are valid in the current state.
Returns

A dictionary mapping high level actions to their verbalizations and input formats.