From ade798744cb3880bbf6605f0b849ca0115ec17b0 Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Wed, 26 Jun 2024 23:09:36 -0400 Subject: [PATCH] implemented quantopedia command Fixes #177 --- examples/quantum_rpg/main_loop.py | 56 +++++++++++++++++++++----- examples/quantum_rpg/main_loop_test.py | 4 +- examples/quantum_rpg/npcs.py | 21 ++++++---- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/examples/quantum_rpg/main_loop.py b/examples/quantum_rpg/main_loop.py index 18fc87fc..186433a3 100644 --- a/examples/quantum_rpg/main_loop.py +++ b/examples/quantum_rpg/main_loop.py @@ -12,10 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Optional, Sequence import enum import io import sys +import textwrap +from typing import List, Optional, Sequence from . import ascii_art from . import battle @@ -23,38 +24,59 @@ from . import exceptions from . import game_state from . import input_helpers +from . import npcs from . import world from . import xp_utils from .final_state_preparation import final_state_world -class Command(enum.Enum): - """Command parsing utility. +class Error(Exception): + """Base class for locally defined exceptions.""" + pass - Currently only supports 'quit' but future expansion planned. - """ + +class AmbiguousCommandError(Error): + """Raised when entered command is ambiguous.""" + pass + + +class Command(enum.Enum): + """Enumeration of available commands.""" LOAD = "load" LOOK = "look" STATUS = "status" SAVE = "save" HELP = "help" + QUANTOPEDIA = "quantopedia" QUIT = "quit" @classmethod def parse(cls, s: str) -> Optional["Command"]: - """Parses a string as a Direction. + """Parses a string as a command. Allows prefixes, like 'e' to be parsed as EAST. """ if not s: return None lower_s = s.lower() - for cmd in Command: + candidates = [] + for cmd in cls: if cmd.value.startswith(lower_s): - return cmd + candidates.append(cmd) + if len(candidates) == 1: + return candidates[0] + if len(candidates) > 1: + raise AmbiguousCommandError return None + @classmethod + def help(cls) -> str: + cmds = ["Available commands:"] + for cmd in Command: + cmds.append(f" {cmd.value}") + return "\n".join(cmds) + class MainLoop: def __init__(self, world: world.World, state: game_state.GameState): @@ -135,7 +157,14 @@ def loop(self, user_input: Optional[Sequence[str]] = None) -> None: print(msg, file=self.file) print_room_description = False continue - input_cmd = Command.parse(current_input) + try: + input_cmd = Command.parse(current_input) + except AmbiguousCommandError: + print(f"Ambiguous command '{current_input}'.", + Command.help(), + file=self.file) + print_room_description = False + continue if input_cmd == Command.QUIT: return elif input_cmd == Command.STATUS: @@ -144,6 +173,15 @@ def loop(self, user_input: Optional[Sequence[str]] = None) -> None: elif input_cmd == Command.HELP: print(ascii_art.HELP, file=self.file) print_room_description = False + elif input_cmd == Command.QUANTOPEDIA: + print(file=self.file) + for npc_class in npcs.Npc.__subclasses__(): + print(npc_class.__name__, file=self.file) + print( + textwrap.indent(npc_class.quantopedia_entry(), " "), + file=self.file) + print(file=self.file) + print_room_description = False elif input_cmd == Command.LOAD: print( "Paste the save file here to load the game from that point.", diff --git a/examples/quantum_rpg/main_loop_test.py b/examples/quantum_rpg/main_loop_test.py index 4ef57daa..93c9e620 100644 --- a/examples/quantum_rpg/main_loop_test.py +++ b/examples/quantum_rpg/main_loop_test.py @@ -105,8 +105,8 @@ def example_world(): def test_parse_commands() -> None: assert main_loop.Command.parse("x") is None - assert main_loop.Command.parse("q") is main_loop.Command.QUIT - assert main_loop.Command.parse("Q") is main_loop.Command.QUIT + assert main_loop.Command.parse("qui") is main_loop.Command.QUIT + assert main_loop.Command.parse("Qui") is main_loop.Command.QUIT assert main_loop.Command.parse("Quit") is main_loop.Command.QUIT assert main_loop.Command.parse("quit") is main_loop.Command.QUIT diff --git a/examples/quantum_rpg/npcs.py b/examples/quantum_rpg/npcs.py index 740bc2d5..ac0c2701 100644 --- a/examples/quantum_rpg/npcs.py +++ b/examples/quantum_rpg/npcs.py @@ -91,7 +91,8 @@ def quantopedia_index(self) -> int: """ return 0 - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: """Explanatory text to the players about the NPC.""" return "Nothing known about this NPC." @@ -112,7 +113,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _FOAM_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Observers are known to frequent quantum events.\n" "They will measure qubits in order to find out their values." @@ -137,7 +139,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _FOAM_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Blue foam are the simplest kind of quantum errors. Blue foam\n" "are usually found in the |0> state and can be measured.\n" @@ -165,7 +168,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _FOAM_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Green foam are a simple kind of quantum error. Green foam\n" "are usually found in the |0> state and can be measured immediately.\n" @@ -195,7 +199,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _FOAM_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Red foam are a slightly more dangerous type of quantum error.\n" "They are usually found in the |1> state and must be flipped\n" @@ -224,7 +229,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _FOAM_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Purple foam are a combination of red and blue form.\n" "They are found in a |+> state which is a combination of\n" @@ -268,7 +274,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _HILLS_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Schrödinger's cat are found in a superposition of zero and one.\n" "This cat contains multiple qubits that are entangled so that all\n"