Spaces:
Running
Running
Case Zero - initial public release (fully local: Qwen2.5-1.5B via llama.cpp + Supertonic, custom pixel-noir SPA via gradio.Server)
414dc55 | """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) | |