"""Confrontation (Section 8.5). The player pins two prior statements from a character and confronts them. The engine verifies the contradiction is *real* against that character's ledger claims (topic/polarity, and engine truth values). A verified contradiction is a primary crack trigger; a bogus one does nothing. The ledger is the weapon. """ from __future__ import annotations from dataclasses import dataclass from .ledger import LedgerStore @dataclass class ConfrontResult: verified: bool reason: str claim_a: str = "" claim_b: str = "" def verify_confrontation( ledger: LedgerStore, character: str, claim_id_a: str, claim_id_b: str ) -> ConfrontResult: a = ledger.claim_by_id(character, claim_id_a) b = ledger.claim_by_id(character, claim_id_b) if a is None or b is None: missing = [cid for cid, c in ((claim_id_a, a), (claim_id_b, b)) if c is None] return ConfrontResult(False, f"unknown statement id(s): {missing}") if a.claim_id == b.claim_id: return ConfrontResult(False, "those are the same statement") # A real contradiction: same topic, opposite polarity ... if a.topic == b.topic and {a.polarity, b.polarity} == {"affirm", "deny"}: return ConfrontResult( True, f"They said both \"{a.proposition}\" and \"{b.proposition}\" about " f"{a.topic} — those cannot both be true.", a.proposition, b.proposition, ) # ... or one is known-false against ground truth while contradicting the other. if ( a.topic == b.topic and "false" in (a.engine_truth_value, b.engine_truth_value) and a.proposition != b.proposition ): return ConfrontResult( True, f"Their statements about {a.topic} don't square: " f"\"{a.proposition}\" vs \"{b.proposition}\".", a.proposition, b.proposition, ) return ConfrontResult( False, "There's no real contradiction between those two statements.", a.proposition, b.proposition, )