Skip to content

Commit

Permalink
Merge pull request #182 from solodov/main
Browse files Browse the repository at this point in the history
[Quantum RPG] allow action selections to be modified
  • Loading branch information
solodov authored Mar 20, 2024
2 parents 5f21ed2 + 4aed4e1 commit 97bcb1e
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 29 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ docs/generated

# Notebook formatting script.
nbformat

build
19 changes: 11 additions & 8 deletions unitary/examples/quantum_rpg/battle.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,19 +143,22 @@ def take_player_turn(self):
else:
break
if action in current_player.actions():
monster = (
input_helpers.get_user_input_number(
monster, qubit = input_helpers.get_multiple_user_inputs(
self.get_user_input,
lambda: input_helpers.get_user_input_number(
self.get_user_input,
"Which enemy number: ",
max_number=len(self.enemy_side),
file=self.file,
)
- 1
)
selected_monster = self.enemy_side[monster]
qubit = input_helpers.get_user_input_number(
self.get_user_input, "Which enemy qubit number: ", file=self.file
),
lambda: input_helpers.get_user_input_number(
self.get_user_input,
"Which enemy qubit number: ",
file=self.file,
),
file=self.file,
)
selected_monster = self.enemy_side[monster - 1]
qubit_name = selected_monster.quantum_object_name(qubit)
if qubit_name in selected_monster.active_qubits():
res = actions[action](selected_monster, qubit)
Expand Down
30 changes: 23 additions & 7 deletions unitary/examples/quantum_rpg/battle_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_battle():
c = classes.Analyst("Aaronson")
e = npcs.Observer("watcher")
state = game_state.GameState(
party=[c], user_input=["m", "1", "1"], file=io.StringIO()
party=[c], user_input=["m", "1", "1", ""], file=io.StringIO()
)
b = battle.Battle(state, [e])
b.take_player_turn()
Expand All @@ -39,6 +39,8 @@ def test_battle():
m) Measure enemy qubit.
q) Read Quantopedia.
?) Help.
[enter]) Confirm selection.
r) Redo selection.
""".strip()
)

Expand All @@ -47,7 +49,7 @@ def test_bad_monster():
c = classes.Analyst("Aaronson")
e = npcs.Observer("watcher")
state = game_state.GameState(
party=[c], user_input=["m", "2", "1", "1"], file=io.StringIO()
party=[c], user_input=["m", "2", "1", "1", ""], file=io.StringIO()
)
b = battle.Battle(state, [e])
b.take_player_turn()
Expand All @@ -63,6 +65,8 @@ def test_bad_monster():
q) Read Quantopedia.
?) Help.
Invalid number selected.
[enter]) Confirm selection.
r) Redo selection.
""".strip()
)

Expand All @@ -78,7 +82,7 @@ def test_higher_level_players():
w = npcs.Observer("watcher")
w2 = npcs.Observer("watcher")
state = game_state.GameState(
[c, e], user_input=["m", "1", "1", "h", "2", "1"], file=io.StringIO()
[c, e], user_input=["m", "1", "1", "", "h", "2", "1", ""], file=io.StringIO()
)
b = battle.Battle(state, [w, w2])
b.take_player_turn()
Expand All @@ -96,6 +100,8 @@ def test_higher_level_players():
s) Sample enemy qubit.
q) Read Quantopedia.
?) Help.
[enter]) Confirm selection.
r) Redo selection.
------------------------------------------------------------
Aaronson Analyst 1) watcher Observer
3QP (0|1> 0|0> 3?) 1QP (0|1> 1|0> 0?) *DOWN*
Expand All @@ -107,6 +113,8 @@ def test_higher_level_players():
x) Attack with X gate.
q) Read Quantopedia.
?) Help.
[enter]) Confirm selection.
r) Redo selection.
""".strip()
)

Expand Down Expand Up @@ -138,7 +146,7 @@ def test_battle_loop():
c = classes.Analyst("Aaronson")
e = npcs.Observer("watcher")
state = game_state.GameState(
party=[c], user_input=["m", "1", "1"], file=io.StringIO()
party=[c], user_input=["m", "1", "1", ""], file=io.StringIO()
)
b = battle.Battle(state, [e])
assert b.loop() == battle.BattleResult.PLAYERS_WON
Expand All @@ -153,6 +161,8 @@ def test_battle_loop():
m) Measure enemy qubit.
q) Read Quantopedia.
?) Help.
[enter]) Confirm selection.
r) Redo selection.
------------------------------------------------------------
Battle Summary
Expand All @@ -167,7 +177,7 @@ def test_battle_help():
c = classes.Analyst("Aaronson")
e = npcs.Observer("watcher")
state = game_state.GameState(
party=[c], user_input=["?", "m", "1", "1"], file=io.StringIO()
party=[c], user_input=["?", "m", "1", "1", ""], file=io.StringIO()
)
b = battle.Battle(state, [e])
assert b.loop() == battle.BattleResult.PLAYERS_WON
Expand All @@ -186,6 +196,8 @@ def test_battle_help():
into the |0> state or |1> state with a probability based on its
amplitude. Try to measure the enemy qubits as |0> to defeat them.
[enter]) Confirm selection.
r) Redo selection.
------------------------------------------------------------
Battle Summary
Expand All @@ -200,7 +212,7 @@ def test_read_quantopedia_not_known():
c = classes.Analyst("Aaronson")
e = npcs.Observer("watcher")
state = game_state.GameState(
party=[c], user_input=["q", "m", "1", "1"], file=io.StringIO()
party=[c], user_input=["q", "m", "1", "1", ""], file=io.StringIO()
)
b = battle.Battle(state, [e])
assert b.loop() == battle.BattleResult.PLAYERS_WON
Expand All @@ -216,6 +228,8 @@ def test_read_quantopedia_not_known():
q) Read Quantopedia.
?) Help.
You do not have information on Observer yet.
[enter]) Confirm selection.
r) Redo selection.
------------------------------------------------------------
Battle Summary
Expand All @@ -230,7 +244,7 @@ def test_read_quantopedia():
c = classes.Analyst("Aaronson")
e = npcs.Observer("watcher")
state = game_state.GameState(
party=[c], user_input=["q", "m", "1", "1"], file=io.StringIO()
party=[c], user_input=["q", "m", "1", "1", ""], file=io.StringIO()
)
state.set_quantopedia(1)
b = battle.Battle(state, [e])
Expand All @@ -248,6 +262,8 @@ def test_read_quantopedia():
?) Help.
Observers are known to frequent quantum events.
They will measure qubits in order to find out their values.
[enter]) Confirm selection.
r) Redo selection.
------------------------------------------------------------
Battle Summary
Expand Down
7 changes: 5 additions & 2 deletions unitary/examples/quantum_rpg/encounter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
# limitations under the License.

import io

import unitary.alpha as alpha
import unitary.examples.quantum_rpg.battle as battle
import unitary.examples.quantum_rpg.classes as classes
import unitary.examples.quantum_rpg.game_state as game_state
import unitary.examples.quantum_rpg.encounter as encounter
import unitary.examples.quantum_rpg.game_state as game_state
import unitary.examples.quantum_rpg.npcs as npcs
import unitary.examples.quantum_rpg.xp_utils as xp_utils

Expand All @@ -38,7 +39,7 @@ def test_encounter():
c = classes.Analyst("Aaronson")
o = npcs.Observer("watcher")
state = game_state.GameState(
party=[c], user_input=["m", "1", "1"], file=io.StringIO()
party=[c], user_input=["m", "1", "1", ""], file=io.StringIO()
)
e = encounter.Encounter([o])

Expand All @@ -56,6 +57,8 @@ def test_encounter():
m) Measure enemy qubit.
q) Read Quantopedia.
?) Help.
[enter]) Confirm selection.
r) Redo selection.
""".strip()
)

Expand Down
34 changes: 29 additions & 5 deletions unitary/examples/quantum_rpg/input_helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""Functions for safe and user-friendly input."""

from typing import Callable, Optional, Sequence, TextIO
from typing import Callable, Optional, Sequence, TextIO, Union

import sys
import unitary.examples.quantum_rpg.qaracter as qaracter

from unitary.examples.quantum_rpg import qaracter

_USER_INPUT = Callable[[str], str]
_INVALID_MESSAGE = "Invalid number selected."
Expand All @@ -27,9 +28,9 @@ def get_user_input_number(
get_user_input: _USER_INPUT,
message: str = "",
max_number: Optional[int] = None,
invalid_message: Optional[str] = _INVALID_MESSAGE,
invalid_message: str = _INVALID_MESSAGE,
file: TextIO = sys.stdout,
):
) -> int:
"""Helper to get a valid number from the user.
This will only accept valid numbers from the user from 1 to max_number.
Expand All @@ -54,9 +55,32 @@ def get_user_input_number(
print("number out of range", file=file)


def get_multiple_user_inputs(
get_user_input: _USER_INPUT,
*prompts: Sequence[Union[Callable[[], int], Callable[[], str]]],
file: TextIO = sys.stdout,
) -> Sequence[Union[int, str]]:
"""Runs multiple number or string prompts and returns their results.
After all inputs have been provided the last prompt asks for confirmation if
user happy with inputs, and allows to restart the sequence and change
inputs.
"""
while True:
inputs = [p() for p in prompts]
print("[enter]) Confirm selection.", file=file)
print("r) Redo selection.", file=file)
while True:
a = get_user_input("Choose your action: ")
if a == "r":
break
if a == "":
return inputs


def get_user_input_qaracter_name(
get_user_input: _USER_INPUT,
qaracter_type: Optional[str] = "a new qaracter",
qaracter_type: str = "a new qaracter",
file: TextIO = sys.stdout,
):
while True:
Expand Down
33 changes: 31 additions & 2 deletions unitary/examples/quantum_rpg/input_helpers_test.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import pytest
import io

import unitary.examples.quantum_rpg.input_helpers as input_helpers
import pytest

from unitary.examples.quantum_rpg import input_helpers


def test_get_user_input_function():
Expand Down Expand Up @@ -43,3 +44,31 @@ def test_get_user_input_number_max():
assert (
input_helpers.get_user_input_number(get_input, file=output, max_number=4) == 3
)


def test_get_multiple_user_inputs():
get_input = input_helpers.get_user_input_function(
["1", "2", "r", "3", "1", ""],
)
output = io.StringIO()

def p1():
return input_helpers.get_user_input_number(
get_input,
max_number=4,
file=output,
)

def p2():
return input_helpers.get_user_input_number(
get_input,
max_number=4,
file=output,
)

assert input_helpers.get_multiple_user_inputs(
get_input,
p1,
p2,
file=output,
) == [3, 1]
22 changes: 17 additions & 5 deletions unitary/examples/quantum_rpg/main_loop_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import cast

import copy
import io
from typing import cast

import unitary.alpha as alpha
import unitary.examples.quantum_rpg.ascii_art as ascii_art
import unitary.examples.quantum_rpg.classes as classes
Expand Down Expand Up @@ -328,7 +328,9 @@ def test_bad_load() -> None:
def test_battle() -> None:
c = classes.Analyst("Mensing")
state = game_state.GameState(
party=[c], user_input=["e", "south", "m", "1", "1", "quit"], file=io.StringIO()
party=[c],
user_input=["e", "south", "m", "1", "1", "", "quit"],
file=io.StringIO(),
)
loop = main_loop.MainLoop(state=state, world=world.World(example_world()))
loop.loop()
Expand Down Expand Up @@ -366,6 +368,8 @@ def test_battle() -> None:
m) Measure enemy qubit.
q) Read Quantopedia.
?) Help.
[enter]) Confirm selection.
r) Redo selection.
------------------------------------------------------------
Battle Summary
Expand All @@ -385,7 +389,9 @@ def test_battle() -> None:
def test_lost_battle() -> None:
c = classes.Engineer("Mensing")
state = game_state.GameState(
party=[c], user_input=["e", "south", "x", "1", "1", "quit"], file=io.StringIO()
party=[c],
user_input=["e", "south", "x", "1", "1", "", "quit"],
file=io.StringIO(),
)
assert state.party[0].name == "Mensing"
assert len(state.party[0].active_qubits()) == 1
Expand Down Expand Up @@ -427,6 +433,8 @@ def test_lost_battle() -> None:
x) Attack with X gate.
q) Read Quantopedia.
?) Help.
[enter]) Confirm selection.
r) Redo selection.
Observer watcher measures Mensing_1 as HURT.
------------------------------------------------------------
Battle Summary
Expand All @@ -450,7 +458,9 @@ def test_escaped_battle():
c = classes.Engineer("Mensing")
c.add_quantum_effect(alpha.Flip(), 1)
state = game_state.GameState(
party=[c], user_input=["e", "south", "x", "1", "1", "quit"], file=io.StringIO()
party=[c],
user_input=["e", "south", "x", "1", "1", "", "quit"],
file=io.StringIO(),
)
assert state.party[0].name == "Mensing"
assert len(state.party[0].active_qubits()) == 1
Expand Down Expand Up @@ -492,6 +502,8 @@ def test_escaped_battle():
x) Attack with X gate.
q) Read Quantopedia.
?) Help.
[enter]) Confirm selection.
r) Redo selection.
Observer watcher measures Mensing_1 as HEALTHY.
------------------------------------------------------------
Battle Summary
Expand Down

0 comments on commit 97bcb1e

Please sign in to comment.