Spaces:
Running on Zero
Running on Zero
| """Legacy: what survives between runs. The Warden keeps files on you. | |
| One JSON store (SCRYPT_HOME/legacy.json) holds everything that crosses | |
| run boundaries: | |
| crashes post-mortems of dead runs β fuel for /var/crash dumps, the | |
| Warden's taunts, and conscription (your dead fight for it) | |
| estate cycles + one card sealed behind a password when you die; | |
| recoverable next run via forensics on your own crash dump | |
| contraband the one card you smuggled out of a won run | |
| daemon your most-played card, installed in future sandboxes as a | |
| service you can find and arm | |
| intel the password you read in the Warden's diary (root epilogue); | |
| next run's archive puzzle reuses it | |
| wins/runs the uptime ledger; audit_level() scales director cruelty | |
| shards the Warden's distilled memory of you, persisted | |
| Everything mechanical that comes OUT of this store is bounded at load | |
| time β corrupt or hand-edited files can never mint a broken card. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| import re | |
| from pathlib import Path | |
| from .cards import KNOWN_SIGILS, Card, Cost, CostType | |
| MAX_CRASHES = 5 | |
| MAX_STATEMENT = 120 | |
| _PRINTABLE = re.compile(r"[^\x20-\x7e]") | |
| EMPTY = { | |
| "initiated": False, # has this installation ever seen orientation? | |
| "runs": 0, | |
| "wins": 0, | |
| "crashes": [], | |
| "estate": None, | |
| "contraband": None, | |
| "daemon": None, | |
| "intel": None, | |
| "shards": [], | |
| "diary": [], # the Warden's own entries about won runs, newest last | |
| } | |
| def _store_path() -> Path: | |
| home = Path(os.environ.get("SCRYPT_HOME", "~/.scrypt")).expanduser() | |
| return home / "legacy.json" | |
| def load() -> dict: | |
| try: | |
| data = json.loads(_store_path().read_text(encoding="utf-8")) | |
| if not isinstance(data, dict): | |
| return dict(EMPTY) | |
| except (OSError, ValueError): | |
| return dict(EMPTY) | |
| out = dict(EMPTY) | |
| out.update({k: data[k] for k in EMPTY if k in data}) | |
| for key in ("crashes", "shards", "diary"): | |
| if not isinstance(out[key], list): | |
| out[key] = [] | |
| return out | |
| def save(data: dict) -> None: | |
| path = _store_path() | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| path.write_text(json.dumps(data, indent=2), encoding="utf-8") | |
| def clean_statement(raw: str) -> str: | |
| """The exit-interview statement: printable, trimmed, never empty.""" | |
| text = _PRINTABLE.sub("", raw).strip()[:MAX_STATEMENT].strip() | |
| return text or "(they said nothing)" | |
| def record_crash( | |
| data: dict, | |
| *, | |
| encounter: str, | |
| turn: int, | |
| deck_ids: list[str], | |
| strongest: dict | None, | |
| statement: str, | |
| cycles: int, | |
| estate_card: str | None, | |
| password: str, | |
| ) -> None: | |
| """File a dead run, and seal its estate behind the password.""" | |
| data["runs"] += 1 | |
| data["crashes"].append( | |
| { | |
| "n": data["runs"], | |
| "encounter": encounter, | |
| "turn": turn, | |
| "deck": deck_ids[:16], | |
| "strongest": strongest, | |
| "statement": clean_statement(statement), | |
| } | |
| ) | |
| data["crashes"] = data["crashes"][-MAX_CRASHES:] | |
| if cycles > 0 or estate_card: | |
| data["estate"] = { | |
| "cycles": min(cycles, 50), | |
| "card": estate_card, | |
| "password": password, | |
| } | |
| def record_win(data: dict, *, most_played: str | None, diary_password: str) -> None: | |
| data["runs"] += 1 | |
| data["wins"] += 1 | |
| if most_played: | |
| data["daemon"] = most_played | |
| data["intel"] = diary_password | |
| def audit_level(data: dict) -> int: | |
| """0 until your second win β the first victory is a pure payoff. | |
| After that the machine starts taking you seriously. Capped: the | |
| director's cruelty budget must stay bounded.""" | |
| return max(0, min(2, int(data.get("wins", 0)) - 1)) | |
| def conscript(crash: dict) -> Card | None: | |
| """The strongest process you died holding, now on the Warden's payroll. | |
| Stats are clamped no matter what the JSON says.""" | |
| raw = crash.get("strongest") | |
| if not raw: | |
| return None | |
| sigils = tuple(s for s in raw.get("sigils", ()) if s in KNOWN_SIGILS)[:1] | |
| try: | |
| power = max(1, min(4, int(raw["power"]))) | |
| health = max(1, min(6, int(raw["health"]))) | |
| except (KeyError, TypeError, ValueError): | |
| return None | |
| name = clean_statement(str(raw.get("name", "defector")))[:16] | |
| return Card( | |
| id="conscript", | |
| name=name, | |
| power=power, | |
| health=health, | |
| cost=Cost(CostType.FREE), | |
| sigils=sigils, | |
| flavor="It remembers being yours.", | |
| art=" βββ\nβ β β\n βββ", | |
| ) | |