Spaces:
Running on Zero
Running on Zero
File size: 4,682 Bytes
9fca766 | 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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | """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 βββ",
)
|