| """The turn loop. |
| |
| GameEngine ties the pieces together: |
| |
| player action ββΆ build prompt from GameState ββΆ model.chat() ββΆ parser |
| ββ narrative + updated state ββββββββββββββββββββββ |
| |
| It also enforces a couple of rules the model should not be trusted with: |
| * if the model forgets to make a defeated enemy stop attacking, combat still ends |
| when enemy HP hits 0 (handled in the parser); |
| * a guaranteed enemy counter-attack option exists even if the model is lazy |
| (the engine never *adds* damage on its own β it only ever clamps). |
| """ |
|
|
| from __future__ import annotations |
|
|
| from .game_state import GameState |
| from .llm import Backend |
| from . import prompts, parser |
|
|
|
|
| class GameEngine: |
| def __init__(self, backend: Backend, state: GameState | None = None): |
| self.backend = backend |
| self.state = state or GameState() |
| self.history: list[parser.TurnResult] = [] |
|
|
| |
| def start(self) -> parser.TurnResult: |
| """Generate the opening scene.""" |
| user = prompts.build_opening_prompt(self.state.context_snapshot()) |
| return self._turn(user) |
|
|
| def act(self, player_action: str) -> parser.TurnResult: |
| """Process one player action.""" |
| if self.state.game_over: |
| return parser.TurnResult( |
| narrative="Your tale has ended. Start a new adventure to play again.", |
| choices=[], |
| applied=[], |
| raw="", |
| ) |
| action = player_action.strip() or "look around" |
| user = prompts.build_turn_prompt(self.state.context_snapshot(), action) |
| return self._turn(user) |
|
|
| def reset(self) -> "GameEngine": |
| self.state = GameState() |
| self.history.clear() |
| return self |
|
|
| |
| def _turn(self, user_message: str) -> parser.TurnResult: |
| try: |
| raw = self.backend.chat(prompts.SYSTEM_PROMPT, user_message) |
| except Exception as exc: |
| return parser.TurnResult( |
| narrative=f"(The mists swirl β the storyteller faltered: {exc})", |
| choices=["Try again."], |
| applied=[], |
| raw="", |
| ) |
| result = parser.run_turn(self.state, raw) |
| self.history.append(result) |
| return result |
|
|