"""숨은 상태 3값 — 신뢰축 / 쇠약도 / 앎 (P0 4종 엔진 명세 준거). 규칙: - 신뢰축: trust_score / doubt_score 누적 (시소). 축 이벤트가 민다 - 쇠약도: 단조 증가만 허용 (되돌리는 코드는 결함) - 앎(유저): E01-01 도입에서 1회 확립 후 불변 - 유저에게 숫자·게이지 노출 금지 — 상태는 톤·결·해금으로만 표출 (trust_level 환산) - 권위: 포켓 안에서는 session.state, 포켓 종료 시 여기로 커밋 (이중 갱신 금지) """ from __future__ import annotations from dataclasses import dataclass, field # 축 이벤트 1회당 이동량 (엔진 설정 — 서사 내용 아님). # E01~E06 축 이벤트 밀도 기준으로 엔딩 3종이 모두 도달 가능하도록 조정된 값. AXIS_DELTA = 12 # 신뢰 레벨 환산 경계 (P0 0-3: 숫자 대신 이 레벨이 톤·결·해금을 결정) TRUST_LEVELS = ("doubt_high", "doubt_mid", "neutral", "trust_mid", "trust_high") @dataclass class HiddenState: trust_score: int = 0 doubt_score: int = 0 frailty: int = 0 # 쇠약도 — 단조 증가 awareness_established: bool = False # 앎(유저) — 도입 1회 확립 axis_log: list[str] = field(default_factory=list) # 연구용 이력 (card_id:axis) # ── 신뢰축 ────────────────────────────────────────────── def apply_axis(self, axis: str | None, source: str = "") -> None: if axis == "trust": self.trust_score += AXIS_DELTA elif axis == "doubt": self.doubt_score += AXIS_DELTA elif axis in (None, "neutral"): return else: raise ValueError(f"알 수 없는 axis: {axis}") self.axis_log.append(f"{source}:{axis}") @property def seesaw(self) -> int: """의심우세(-) ←─[0]─→ 신뢰우세(+)""" return self.trust_score - self.doubt_score @property def trust_level(self) -> str: """P0 0-3 환산 출력 — 프롬프트의 '결'과 해금을 결정한다.""" s = self.seesaw if s >= 36: return "trust_high" if s >= 12: return "trust_mid" if s <= -36: return "doubt_high" if s <= -12: return "doubt_mid" return "neutral" @property def deep_unlock(self) -> bool: """심층 고백 해금 (엔진 D) — 신뢰高에서만.""" return self.trust_level == "trust_high" # ── 쇠약도 ────────────────────────────────────────────── def advance_frailty(self, amount: int = 1) -> None: if amount < 0: raise ValueError("쇠약도는 되돌아가지 않는다") self.frailty += amount # ── 앎 ───────────────────────────────────────────────── def establish_awareness(self) -> None: self.awareness_established = True # ── 영속화 스냅샷 (전 필드 — 포켓용 to_session과 별개) ── def snapshot(self) -> dict: return { "trust_score": self.trust_score, "doubt_score": self.doubt_score, "frailty": self.frailty, "awareness_established": self.awareness_established, "axis_log": list(self.axis_log), } @classmethod def from_snapshot(cls, data: dict) -> "HiddenState": return cls( trust_score=int(data["trust_score"]), doubt_score=int(data["doubt_score"]), frailty=int(data["frailty"]), awareness_established=bool(data["awareness_established"]), axis_log=list(data.get("axis_log", [])), ) # ── 포켓 커밋 (session.state ↔ 영속 상태 단일 경로) ───── def to_session(self) -> dict: """포켓 진입 시 session.state에 실어 보낼 스냅샷.""" return { "hidden_trust": self.trust_score, "hidden_doubt": self.doubt_score, "trust_level": self.trust_level, "deep_unlock": self.deep_unlock, } def commit_from_session(self, state: dict, source: str = "pocket") -> None: """포켓 종료 시 session.state의 증분만 커밋 (유일한 역방향 경로).""" self.trust_score = max(self.trust_score, int(state.get("hidden_trust", self.trust_score))) self.doubt_score = max(self.doubt_score, int(state.get("hidden_doubt", self.doubt_score))) self.axis_log.append(f"{source}:commit")