File size: 1,448 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
"""Deterministic relevance: how a presented clue relates to a suspect.

This is computed from ground truth, never from the model. ``BREAKING`` means the clue
is one that an anchored lie of this suspect explicitly breaks on - the only way a lie
is ever caught.
"""

from __future__ import annotations

from dataclasses import dataclass

from ..schemas.case import CaseFile
from ..schemas.enums import Relevance
from ..schemas.suspect import Suspect


@dataclass(frozen=True)
class RelevanceResult:
    relevance: Relevance
    broken_lie_id: str | None = None


def assess_relevance(case: CaseFile, suspect: Suspect, clue_id: str | None) -> RelevanceResult:
    if clue_id is None:
        return RelevanceResult(Relevance.NONE)

    try:
        clue = case.clue(clue_id)
    except KeyError:
        return RelevanceResult(Relevance.NONE)

    # Breaking: this clue defeats one of the suspect's anchored lies.
    for lie in suspect.anchored_lies:
        if clue_id in lie.breaks_on:
            return RelevanceResult(Relevance.BREAKING, broken_lie_id=lie.lie_id)

    if clue.contradicts_alibi_of == suspect.sus_id:
        return RelevanceResult(Relevance.BREAKING)

    if clue.points_to_sus_id == suspect.sus_id:
        return RelevanceResult(Relevance.DIRECT)

    if clue.supports_fact_id and clue.supports_fact_id in suspect.knows_facts:
        return RelevanceResult(Relevance.DIRECT)

    return RelevanceResult(Relevance.TANGENTIAL)