Spaces:
Running
Running
File size: 1,350 Bytes
414dc55 | 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 | """The discoverability gate.
A case proven solvable on paper is worthless if a key clue can only ever come from a
suspect who chooses to stay silent. This gate guarantees every clue in the solution's
minimal set is reachable by a non-interrogation channel (search / forensic / document),
so the player can always obtain it.
"""
from __future__ import annotations
from ..schemas.case import CaseFile
from ..schemas.enums import DiscoveryMethod
_NON_INTERROGATION = frozenset(
{DiscoveryMethod.SEARCH, DiscoveryMethod.FORENSIC, DiscoveryMethod.DOCUMENT}
)
def undiscoverable_minimal_clues(case: CaseFile) -> tuple[str, ...]:
"""Return minimal-set clue ids with no non-interrogation discovery path."""
by_id = {c.clue_id: c for c in case.clues}
loc_ids = {loc.loc_id for loc in case.setting.locations}
bad: list[str] = []
for cid in case.solution.minimal_clue_set:
clue = by_id.get(cid)
if clue is None:
bad.append(cid)
continue
reachable = (
clue.discovery_method in _NON_INTERROGATION
and clue.discoverable_at_loc_id in loc_ids
)
if not reachable:
bad.append(cid)
return tuple(bad)
def is_discoverable(case: CaseFile) -> bool:
return not case.solution.minimal_clue_set or not undiscoverable_minimal_clues(case)
|