Spaces:
Sleeping
Sleeping
| """Parser + engine smoke tests. Run with: python -m tests.test_parser | |
| These use the mock backend, so no model weights or network are required.""" | |
| import sys | |
| import os | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from engine.game_state import GameState, Enemy | |
| from engine import parser | |
| from engine.engine import GameEngine | |
| from engine.llm import build_backend | |
| def check(name, cond): | |
| status = "ok " if cond else "FAIL" | |
| print(f"[{status}] {name}") | |
| if not cond: | |
| raise AssertionError(name) | |
| def test_parse_blocks(): | |
| raw = ( | |
| "<narrative>You enter a dim hall.</narrative>" | |
| "<state>\nHP: -5\nGOLD: +10\nITEM_ADD: Torch\nLOCATION: Dim Hall\n</state>" | |
| "<choices>\n1. Go north.\n2. Light the torch.\n</choices>" | |
| ) | |
| narrative, choices, lines = parser.parse(raw) | |
| check("narrative extracted", narrative == "You enter a dim hall.") | |
| check("choices extracted", choices == ["Go north.", "Light the torch."]) | |
| check("state lines count", len(lines) == 4) | |
| def test_apply_changes_clamped(): | |
| state = GameState(hp=20, max_hp=20, gold=10) | |
| parser.apply_state_changes(state, ["HP: -5", "GOLD: +10", "ITEM_ADD: Torch"]) | |
| check("hp reduced", state.hp == 15) | |
| check("gold added", state.gold == 20) | |
| check("item added", state.has_item("Torch")) | |
| # over-heal is clamped to max_hp | |
| parser.apply_state_changes(state, ["HP: +999"]) | |
| check("heal clamped to max", state.hp == 20) | |
| # can't go below zero gold | |
| parser.apply_state_changes(state, ["GOLD: -9999"]) | |
| check("gold floored at 0", state.gold == 0) | |
| def test_death(): | |
| state = GameState(hp=5) | |
| parser.apply_state_changes(state, ["HP: -50"]) | |
| check("hp floored at 0", state.hp == 0) | |
| check("game over on death", state.game_over) | |
| def test_combat_flow(): | |
| state = GameState() | |
| parser.apply_state_changes(state, ["ENEMY: Goblin|hp=10|atk=4"]) | |
| check("combat started", state.enemy is not None and state.enemy.name == "Goblin") | |
| parser.apply_state_changes(state, ["ENEMY_HP: -6"]) | |
| check("enemy damaged", state.enemy.hp == 4) | |
| parser.apply_state_changes(state, ["ENEMY_HP: -10"]) | |
| check("combat ended on death", state.enemy is None) | |
| def test_leveling(): | |
| state = GameState(level=1, xp=0, max_hp=20) | |
| parser.apply_state_changes(state, ["XP: +10"]) | |
| check("leveled up", state.level == 2) | |
| check("max hp grew", state.max_hp == 25) | |
| def test_unparseable_ignored(): | |
| state = GameState(hp=20) | |
| before = state.to_dict() | |
| parser.apply_state_changes(state, ["HP: lots", "WUT: 5", "random gibberish"]) | |
| check("garbage ignored", state.to_dict() == before) | |
| def test_full_engine_mock(): | |
| engine = GameEngine(build_backend("mock")) | |
| opening = engine.start() | |
| check("opening has narrative", len(opening.narrative) > 0) | |
| check("opening has choices", len(opening.choices) == 3) | |
| # force a combat then attack | |
| engine.state.start_combat(Enemy("Wraith", hp=10, max_hp=10, attack=3)) | |
| res = engine.act("I attack the wraith with my dagger") | |
| check("attack damaged enemy or ended combat", | |
| engine.state.enemy is None or engine.state.enemy.hp < 10) | |
| def main(): | |
| tests = [v for k, v in sorted(globals().items()) if k.startswith("test_")] | |
| for t in tests: | |
| t() | |
| print(f"\nAll {len(tests)} test groups passed.") | |
| if __name__ == "__main__": | |
| main() | |