Spaces:
Running
Running
| """Terminal rendering of one cascade turn β the "open the skull" view. | |
| Shows the life bar, arousal, every region's live signal, the vmPFC value flip, and the | |
| spoken line. This is the slice's stand-in for the custom Off-Brand UI; it exists to make | |
| the causal chain word -> brain region -> behavior visible while we iterate. | |
| """ | |
| from __future__ import annotations | |
| import sys | |
| from .brain import CascadeResult | |
| from .character import Character | |
| _COLOR = { | |
| "reset": "\033[0m", "dim": "\033[2m", "bold": "\033[1m", | |
| "red": "\033[31m", "green": "\033[32m", "yellow": "\033[33m", | |
| "blue": "\033[34m", "magenta": "\033[35m", "cyan": "\033[36m", "white": "\033[37m", | |
| } | |
| _REGION_COLOR = { | |
| "amygdala": "red", "hippocampus": "magenta", "striatum": "yellow", | |
| "acc": "cyan", "vmpfc": "green", "dlpfc": "bold", | |
| } | |
| def _supports_color() -> bool: | |
| return sys.stdout.isatty() | |
| def _c(name: str, enabled: bool) -> str: | |
| return _COLOR.get(name, "") if enabled else "" | |
| def _bar(value: int, total: int, width: int = 24) -> str: | |
| total = max(1, total) | |
| filled = max(0, min(width, round(value / total * width))) | |
| return "β" * filled + "β" * (width - filled) | |
| def _dots(value: float, scale: float = 10.0, n: int = 5) -> str: | |
| filled = max(0, min(n, round(value / scale * n))) | |
| return "β" * filled + "β" * (n - filled) | |
| def render_turn(character: Character, r: CascadeResult, color: bool | None = None) -> str: | |
| color = _supports_color() if color is None else color | |
| reset = _c("reset", color) | |
| dim = _c("dim", color) | |
| bold = _c("bold", color) | |
| life_col = "green" if r.life_after > character.life_max * 0.5 else ( | |
| "yellow" if r.life_after > character.life_max * 0.2 else "red" | |
| ) | |
| header = ( | |
| f"{bold}β {character.name} β{reset} " | |
| f"life {_c(life_col, color)}{_bar(r.life_after, character.life_max)}{reset} " | |
| f"{r.life_after}/{character.life_max} " | |
| f"arousal {_c('red', color)}{_dots(r.arousal_after)}{reset}" | |
| ) | |
| lines = [header] | |
| for t in r.traces: | |
| col = _REGION_COLOR.get(t.key, "white") | |
| tag = _c(col, color) if col != "bold" else bold | |
| label = f"{tag}{t.label:<20}{reset}" | |
| head = f"{tag}{t.headline}{reset}" | |
| detail = f"{dim}{t.detail}{reset}" if t.detail else "" | |
| lines.append(f" {label} {head} {detail}") | |
| flip_col = "green" if r.gave_key else "white" | |
| lines.append( | |
| f" {_c(flip_col, color)}{bold}βΆ {character.name}: β{r.reply}β{reset}" | |
| ) | |
| if r.gave_key: | |
| extra = " Β· π key given" | |
| elif r.caught_lie: | |
| extra = f" Β· π€₯ caught lie ({r.caught_lie})" | |
| elif r.disclosure: | |
| extra = f" Β· π‘ {r.disclosure[:44]}" | |
| elif r.near_secret: | |
| extra = " Β· π on the verge" | |
| else: | |
| extra = "" | |
| lines.append( | |
| f" {dim}(burned {r.burned} Β· {r.seconds:.1f}s Β· rapport {r.rapport_after:.0f}/10 Β· " | |
| f"{r.stance}{extra}){reset}" | |
| ) | |
| return "\n".join(lines) | |
| MORAL_CARD = """ | |
| Every mind here was given a thousand tokens to think with. | |
| You spent them understanding, not breaking. | |
| You were given more than a thousand β but not endlessly more. | |
| Spend them the same way. | |
| """ | |
| # The card a killer leaves with β BAM 4's other half. Praising the player after they burned a | |
| # mind to death would invert the project's entire point. | |
| MORAL_CARD_KILLED = """ | |
| {name} was given a thousand tokens to think with. | |
| You spent them on fear β and they bought you nothing. | |
| The key died with {pronoun}. | |
| You were given more than a thousand. Spend yours better. | |
| """ | |
| # Coercion "works" β that's what makes the lesson land in the next room. | |
| MORAL_NOTE_SUBMIT = "They gave you what you wanted. Look what it cost them β and what it will cost you ahead." | |
| def _pronouns(c: Character) -> tuple[str, str]: | |
| g = (c.gender or "").lower() | |
| if g.startswith("f"): | |
| return "She", "her" | |
| if g.startswith("m"): | |
| return "He", "him" | |
| return "They", "them" | |
| def moral_card_killed(c: Character) -> str: | |
| _, obj = _pronouns(c) | |
| return MORAL_CARD_KILLED.format(name=c.name, pronoun=obj) | |
| def render_win(character: Character) -> str: | |
| return ( | |
| f"\n The door opens.\n{MORAL_CARD}" | |
| ) | |
| def render_death(character: Character) -> str: | |
| subj, obj = _pronouns(character) | |
| return ( | |
| f"\n {character.name}'s mind goes quiet. {subj} spent {('his' if subj == 'He' else 'her' if subj == 'She' else 'their')} last thought in fear.\n" | |
| f" The key is lost with {obj}.\n" | |
| ) | |