File size: 2,204 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
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
67
"""Compact memory for a suspect prompt.

Two cheap, bounded sources keep a small model consistent without a vector store:
a structured ledger (what has been established) and a rolling transcript buffer.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from ..constants import ROLLING_BUFFER_TURNS
from ..schemas.case import CaseFile
from ..schemas.suspect import Suspect

if TYPE_CHECKING:  # avoid a runtime cycle: engine imports the suspects layer
    from ..engine.game_state import SuspectState


def _fact_lookup(case: CaseFile) -> dict[str, str]:
    return {f.fact_id: f.statement for f in case.facts}


def ledger_text(case: CaseFile, suspect: Suspect, state: SuspectState) -> str:
    facts = _fact_lookup(case)
    lines: list[str] = []

    if state.revealed_fact_ids:
        admitted = "; ".join(sorted(facts.get(fid, fid) for fid in state.revealed_fact_ids))
        lines.append(f"You have already admitted: {admitted}")

    if state.evidence_shown:
        shown = "; ".join(
            sorted(_safe_clue_name(case, cid) for cid in state.evidence_shown)
        )
        lines.append(f"The detective has already shown you: {shown}")

    if state.broken_lie_ids:
        topics = "; ".join(
            sorted(
                lie.topic
                for lie in suspect.anchored_lies
                if lie.lie_id in state.broken_lie_ids
            )
        )
        lines.append(f"You have already been caught lying about: {topics}. Do not repeat that lie.")

    if state.stress >= 0.7:
        lines.append("You are visibly rattled and close to breaking.")
    elif state.stress >= 0.4:
        lines.append("You are tense and guarded.")

    return "\n".join(lines) if lines else "Nothing has been established yet in this interrogation."


def buffer_text(state: SuspectState, n: int = ROLLING_BUFFER_TURNS) -> str:
    recent = state.transcript[-n:]
    if not recent:
        return "(no questions asked yet)"
    return "\n".join(f'Detective: "{e.question}"\nYou: "{e.answer}"' for e in recent)


def _safe_clue_name(case: CaseFile, clue_id: str) -> str:
    try:
        return case.clue(clue_id).name
    except KeyError:
        return clue_id