phantom-grid / game /turn_engine.py
unity4ar's picture
Ship Phantom Grid Docker Space
d2e6f94 verified
Raw
History Blame Contribute Delete
3.47 kB
from __future__ import annotations
from .police_actions import tick_blocks
from .state import GameState, JunctionCheck
from .story_engine import apply_turn_bundle, generate_turn_bundle
from .witness_engine import (
corrupt_witnesses_slightly,
generate_ambient_witness_batch,
generate_patrol_witness_batch,
)
from .win_conditions import apply_junction_check, culprit_has_escaped
def advance_turn(state: GameState, use_model: bool = False) -> str:
if state.result:
return "Game is already complete."
pre_catch = _search_team_catch(state, phase="stakeout")
if pre_catch is not None:
return _finalize_capture(state, pre_catch, "A search team was already watching this junction when the turn opened.")
move, story, witnesses, venues = generate_turn_bundle(state, use_model=use_model)
setattr(move, "previous_disguise", state.culprit.current_disguise)
apply_turn_bundle(state, move, story, witnesses, venues)
patrol_batch = generate_patrol_witness_batch(state, move)
if patrol_batch:
state.witness_batches.append(patrol_batch)
ambient_batch = generate_ambient_witness_batch(state, [witness.potential_id for witness in witnesses])
if ambient_batch:
state.witness_batches.append(ambient_batch)
corrupt_witnesses_slightly(state)
state.active_blocks = tick_blocks(state.active_blocks)
active_block_ids = {block.block_id for block in state.active_blocks}
state.placed_tactics = [
tactic
for tactic in state.placed_tactics
if tactic.linked_block_id is None or tactic.linked_block_id in active_block_ids
]
state.turn_number += 1
state.phase = "commissioner_action"
post_catch = _search_team_catch(state, phase="intercept")
if post_catch is not None:
return _finalize_capture(state, post_catch, "A search team intercepted the culprit after their move.")
if culprit_has_escaped(state):
state.result = "culprit_escape"
state.phase = "complete"
message = "The culprit avoided detection until the turn limit expired."
else:
message = f"Turn advanced. Public report: no confirmed capture. Turns remaining: {state.max_turns - state.turn_number + 1}."
if ambient_batch:
message += f" {ambient_batch.total_witnesses} new witness report(s) surfaced across the city."
if patrol_batch:
message += f" {patrol_batch.total_witnesses} patrol officer report(s) logged."
state.game_log.append({"turn_number": state.turn_number, "kind": "turn_advance", "message": message})
return message
def _search_team_catch(state: GameState, phase: str) -> int | None:
search_team_junctions = {t.junction_id for t in state.placed_tactics if t.tactic_type == "search_team"}
if state.culprit.current_junction in search_team_junctions:
return state.culprit.current_junction
return None
def _finalize_capture(state: GameState, junction_id: int, reason: str) -> str:
apply_junction_check(state, junction_id)
check_id = f"check_t{state.turn_number:03d}_search_team"
state.junction_checks.append(JunctionCheck(
check_id=check_id,
turn_number=state.turn_number,
junction_id=junction_id,
result="culprit_found",
))
message = f"Commissioner wins. {reason} Junction {junction_id} secured."
state.game_log.append({"turn_number": state.turn_number, "kind": "turn_advance", "message": message})
return message