File size: 2,528 Bytes
7fe39f3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | """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] = []
# ------------------------------------------------------------------ public
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
# ----------------------------------------------------------------- internal
def _turn(self, user_message: str) -> parser.TurnResult:
try:
raw = self.backend.chat(prompts.SYSTEM_PROMPT, user_message)
except Exception as exc: # network/model errors shouldn't crash the UI
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
|