"""Anomaly selection and the small "drift" tricks. Nothing here screams. An anomaly is a single property quietly swapped for a plausible variant. The drift helpers change wording, punctuation, and the heading itself so the player can never compare two loops character-for-character and must fall back on memory of *meaning*. """ from __future__ import annotations import random from typing import Dict, List, Optional, Tuple def pick_anomaly(properties: Dict[str, dict], rng: random.Random) -> Tuple[str, str]: """Choose one property and one of its anomalous values. Returns (property_name, anomaly_value). """ candidates = [name for name, spec in properties.items() if spec.get("anomalies")] prop = rng.choice(candidates) value = rng.choice(list(properties[prop]["anomalies"].keys())) return prop, value def drift_heading(title_variants: List[str], loops: int, rng: random.Random) -> str: """The heading is usually 'Hallway 8', but every so often it slips. Early on it is stable. The longer the player survives, the more the heading is allowed to wander -- a meta anomaly they can never act on. """ if loops < 4: return title_variants[0] # a small, growing chance of a slipped heading if rng.random() < min(0.05 + loops * 0.01, 0.30): return rng.choice(title_variants) return title_variants[0] def drift_sign(sign_variants: List[str], rng: random.Random) -> str: """The exit sign's spacing/arrow drifts even when it is *not* the anomaly.""" return rng.choice(sign_variants) def render_sentence(template: str, sign_text: str) -> str: """Fill the shared placeholders in a sentence template.""" return template.replace("{SIGN}", sign_text)