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 | """Deterministic verdict scoring. | |
| The verdict is a pure function of the structured accusation and the discovered/cited | |
| evidence - never of any model output. A jailbroken confession in dialogue therefore | |
| cannot change the outcome: the case resolves only on the data. | |
| """ | |
| from __future__ import annotations | |
| from ..schemas.accusation import Accusation, Verdict | |
| from ..schemas.case import CaseFile | |
| from .game_state import GameState | |
| _CULPRIT_POINTS = 50 | |
| _WEAPON_POINTS = 15 | |
| _MOTIVE_POINTS = 15 | |
| _EVIDENCE_POINTS = 20 | |
| def score_accusation(case: CaseFile, accusation: Accusation, state: GameState) -> Verdict: | |
| sol = case.solution | |
| culprit_correct = accusation.accused_sus_id == sol.culprit_sus_id | |
| weapon_correct = accusation.weapon_id == sol.weapon_id if accusation.weapon_id else False | |
| motive_correct = accusation.motive_id == sol.motive_id if accusation.motive_id else False | |
| minimal = set(sol.minimal_clue_set) | |
| cited = set(accusation.cited_clue_ids) | |
| discovered = state.discovered_clue_ids | |
| valid_citations = minimal & cited & discovered | |
| evidence_ratio = (len(valid_citations) / len(minimal)) if minimal else 0.0 | |
| # Weapon, motive, and evidence credit only count once the right person is named: | |
| # an incorrect accusation scores zero, so a jailbroken confession earns nothing. | |
| culprit_pts = _CULPRIT_POINTS if culprit_correct else 0 | |
| weapon_pts = _WEAPON_POINTS if (culprit_correct and weapon_correct) else 0 | |
| motive_pts = _MOTIVE_POINTS if (culprit_correct and motive_correct) else 0 | |
| evidence_pts = round(_EVIDENCE_POINTS * evidence_ratio) if culprit_correct else 0 | |
| score = culprit_pts + weapon_pts + motive_pts + evidence_pts | |
| breakdown = ( | |
| ("culprit", culprit_pts), | |
| ("weapon", weapon_pts), | |
| ("motive", motive_pts), | |
| ("evidence", evidence_pts), | |
| ) | |
| rationale = _rationale(case, culprit_correct, valid_citations, minimal) | |
| return Verdict( | |
| solved=culprit_correct, | |
| culprit_correct=culprit_correct, | |
| weapon_correct=weapon_correct, | |
| motive_correct=motive_correct, | |
| score=score, | |
| breakdown=breakdown, | |
| rationale=rationale, | |
| deduction_chain=sol.deduction_chain, | |
| ) | |
| def _rationale( | |
| case: CaseFile, culprit_correct: bool, valid: set[str], minimal: set[str] | |
| ) -> str: | |
| culprit_name = case.suspect(case.solution.culprit_sus_id).name | |
| if culprit_correct: | |
| if valid >= minimal and minimal: | |
| return f"Correct, and fully evidenced. {culprit_name} did it, and you proved it." | |
| return f"You named {culprit_name} - the right person - but the case could be tighter." | |
| return f"Not quite. The killer was {culprit_name}." | |