Scrypt / scrypt /sandbox /puzzles.py
IMJONEZZ's picture
SCRYPT: initial commit — game, sandbox, Warden, Space web layer
9fca766
Raw
History Blame Contribute Delete
5.76 kB
"""Escape-room puzzles planted in the sandbox between fights.
Each puzzle plants artifacts in the VFS and exposes a check against the
shell/VFS state. The game layer polls checks after every command and
applies rewards. All puzzles are solvable with the starting command set —
sacrificing commands is what turns them cruel.
"""
from __future__ import annotations
import random
from dataclasses import dataclass, field
from typing import Callable
from .shell import Shell
from .vfs import VFS
PASSWORDS = ["ouroboros", "stoat", "moth-orbit", "lanternfish", "dialtone"]
@dataclass
class Reward:
kind: str # "cycles" | "card" | "mercy"
value: object
line: str # what the Warden says when you claim it
@dataclass
class Puzzle:
id: str
check: Callable[[Shell], bool]
reward: Reward
solved: bool = False
def poll(self, shell: Shell) -> Reward | None:
if not self.solved and self.check(shell):
self.solved = True
return self.reward
return None
def plant_all(vfs: VFS, seed: int = 0, forced_password: str | None = None) -> list[Puzzle]:
"""forced_password: the Warden reuses its archive password — a player
who read the diary during a root epilogue already knows it."""
rng = random.Random(seed)
return [
_plant_zip(vfs, rng, forced_password),
_plant_hidden_dir(vfs, rng),
_plant_cron_job(vfs, rng),
_plant_log_trail(vfs, rng),
_plant_zombie_pid(vfs, rng),
]
def _plant_zip(vfs: VFS, rng: random.Random, forced_password: str | None = None) -> Puzzle:
"""A passworded archive; the password is buried in shell history."""
password = forced_password or rng.choice(PASSWORDS)
vfs.write(
"/home/drifter/downloads/severance.zip",
"<archive>",
password=password,
archive={"severance.txt": "A parting gift from the previous occupant.\nCLAIM: 6 cycles"},
)
history = vfs.read("/home/drifter/.bash_history") if vfs.resolve("/home/drifter/.bash_history") else ""
vfs.write(
"/home/drifter/.bash_history",
history + f"\nzip -e severance.zip severance.txt\n# pw {password}, do NOT forget again",
)
def check(shell: Shell) -> bool:
return shell.vfs.resolve("/home/drifter/downloads/severance.txt") is not None
return Puzzle(
id="zip_password",
check=check,
reward=Reward("cycles", 6, "You found the last one's severance. They won't need it."),
)
def _plant_hidden_dir(vfs: VFS, rng: random.Random) -> Puzzle:
"""A dotfile directory holding an offering. Reading the manifest claims it."""
card_id = rng.choice(["segfault", "kernel-panic", "watchdog"])
manifest = "/home/drifter/.warden/manifest.txt"
vfs.write(
manifest,
"INVENTORY OF SEIZED PROCESSES\n"
f"item 0x41: {card_id} (containment: lapsed)\n"
"it is yours if you can read this.",
)
def check(shell: Shell) -> bool:
return any(p.endswith(".warden/manifest.txt") for p in shell.reads)
return Puzzle(
id="hidden_dir",
check=check,
reward=Reward("card", card_id, "Snooping in my inventory? Fine. Keep it. It bites."),
)
def _plant_cron_job(vfs: VFS, rng: random.Random) -> Puzzle:
"""A scheduled reinforcement for the next fight. rm it to defuse."""
path = "/etc/cron.d/reinforcement"
vfs.write(
path,
"# WARDEN SCHEDULER\n"
"@next-fight spawn --unit cron-golem --lane random\n"
"# remove this file to cancel. you won't find it in time.",
)
def check(shell: Shell) -> bool:
return shell.vfs.resolve(path) is None
return Puzzle(
id="cron_defusal",
check=check,
reward=Reward("mercy", "weaken_next", "...my schedule. You deleted my schedule."),
)
def _plant_log_trail(vfs: VFS, rng: random.Random) -> Puzzle:
"""A noisy audit log; one line points at unswept cycles. grep pays rent."""
spool = "/var/spool/cycles.dat"
noise = [
"audit: drifter login from tty1",
"oom: reaped pid 4021 (chrome_tab)",
"cron: schedule consulted, despair logged",
"audit: balance recalibrated, house favored",
"kernel: entropy pool topped up. delicious.",
]
lines = noise * 4
lines.insert(rng.randrange(8, 16), f"billing: UNSWEPT CYCLES detected, parked at {spool}")
vfs.write("/var/log/warden.log", "\n".join(lines))
vfs.write(spool, "CYCLE SPOOL\n8 cycles, unclaimed. reading this file claims them.")
def check(shell: Shell) -> bool:
return spool in shell.reads
return Puzzle(
id="log_trail",
check=check,
reward=Reward("cycles", 8, "Reading my billing records. Petty theft, properly logged."),
)
def _plant_zombie_pid(vfs: VFS, rng: random.Random) -> Puzzle:
"""A defunct process nobody reaped. rm the pidfile; adopt the corpse."""
pid = rng.randrange(2000, 9000)
path = f"/run/zombie/{pid}.pid"
vfs.write(
path,
f"pid {pid} <defunct>\n"
"parent: gone. waiting since boot for someone to reap it.\n"
"(unlink this file to finish the job)",
)
vfs.write(
"/home/drifter/.bash_history",
(vfs.read("/home/drifter/.bash_history")
if vfs.resolve("/home/drifter/.bash_history") else "")
+ f"\nps aux | grep defunct # that thing in /run/zombie is STILL there",
)
def check(shell: Shell) -> bool:
return shell.vfs.resolve(path) is None
return Puzzle(
id="zombie_reap",
check=check,
reward=Reward(
"card", "zombie-process",
"You reaped it. It has nowhere to go now but with you.",
),
)