"""Deterministic scripted plans for the MockBackend. Each wish maps to a category by keyword; each category builds a Reading plus a short turn list (calls + a done turn), with coordinates, hues, and a few phrasings varied by a seed derived from the wish text (crc32 — stable across processes, unlike hash()). Same wish in, same plan out: demos replay exactly. Across the category library every one of the 11 tools appears, so a varied demo session exercises the full DSL. All numbers are kept inside the engine's clamp ranges; all thoughts <=160 chars; all epitaphs <=120; inscriptions <=90. This module produces *content* only. The MockBackend (mind/backends.py) is responsible for streaming it token-by-token with delays. """ from __future__ import annotations import random import re import zlib from .prompts import infer_mood THOUGHT_MAX = 160 EPITAPH_MAX = 120 INSCRIBE_MAX = 90 # -------------------------------------------------------------------------- # Small helpers # -------------------------------------------------------------------------- def _seed_for(wish: str) -> int: return zlib.crc32(" ".join(wish.lower().split()).encode("utf-8")) & 0x7FFFFFFF def _r1(rng: random.Random, lo: float, hi: float) -> float: return round(rng.uniform(lo, hi), 1) def _r2(rng: random.Random, lo: float, hi: float) -> float: return round(rng.uniform(lo, hi), 2) def _clamp_lat(lat: float) -> float: return max(-85.0, min(85.0, round(lat, 1))) def _wrap_lon(lon: float) -> float: wrapped = ((lon + 180.0) % 360.0) - 180.0 return round(wrapped, 1) def _call(thought: str, tool: str, **args) -> dict: return {"thought": thought[:THOUGHT_MAX], "call": {"tool": tool, "args": args}} def _done(thought: str, epitaph: str) -> dict: return { "thought": thought[:THOUGHT_MAX], "done": True, "epitaph": epitaph[:EPITAPH_MAX], } def _snippet(wish: str, limit: int) -> str: text = " ".join(wish.split()).replace('"', "'").strip() if len(text) <= limit: return text return text[: limit - 1].rstrip() + "…" def _inscription(wish: str) -> str: return _snippet(wish, INSCRIBE_MAX) _PLACES = ( "the north", "the far south", "the morning side", "the evening side", "the quiet latitudes", "the world's shoulder", ) def _place(rng: random.Random) -> str: return rng.choice(_PLACES) def _anchor(rng: random.Random) -> tuple[float, float]: return _r1(rng, -55, 55), _r1(rng, -165, 165) def _mood_sky(rng: random.Random, mood: str) -> str: table = { "melancholy": ("dusk", "night", "void"), "joyful": ("dawn", "gold"), "eerie": ("night", "aurora"), "absurd": ("aurora", "ember", "gold"), "still": ("dusk", "night", "dawn"), } return rng.choice(table.get(mood, table["still"])) # -------------------------------------------------------------------------- # Category scripts # -------------------------------------------------------------------------- def _water(wish: str, rng: random.Random, mood: str) -> dict: lat, lon = _anchor(rng) place = _place(rng) palette = _mood_sky(rng, mood if mood != "joyful" else "still") hue = _r1(rng, 170, 230) reading = ( f"Water, then. The oldest ask, and the kindest. I will press a basin " f"into {place} and teach a bright stream to find it. The sky should " f"lean {palette} over it, so the surface has something to study." ) path = [ [_clamp_lat(lat + 14), _wrap_lon(lon - 18)], [_clamp_lat(lat + 6), _wrap_lon(lon - 8)], [lat, lon], ] return { "reading": reading, "turns": [ _call( "First the hollow. Water needs somewhere to be.", "lower_terrain", lat=lat, lon=lon, radius_deg=_r1(rng, 12, 20), depth=_r2(rng, 0.05, 0.08), roughness=0.4, ), _call( "Now the stream, from the high ground down.", "place_water", kind="stream", path=path, hue=hue, ), _call( "A pool where it gathers, lit from beneath.", "place_water", kind="pool", path=[[lat, lon]], radius_deg=_r1(rng, 4, 7), hue=hue, ), _call( "A sky for the water to look back at.", "set_sky", palette=palette, star_density=_r2(rng, 0.4, 0.8), moons=rng.randint(1, 2), ), _done( "The water moves. That is most of what water is for.", "A stream now runs to a patient pool, and the sky watches itself there.", ), ], } def _mountain(wish: str, rng: random.Random, mood: str) -> dict: lat, lon = _anchor(rng) reading = ( f"A mountain. Good. The ground here has been humble too long. I will " f"raise it slow, with rough shoulders, and hang mist on it so it " f"keeps its own counsel." ) return { "reading": reading, "turns": [ _call( "Lift the bedrock first; the rest is ornament.", "raise_terrain", lat=lat, lon=lon, radius_deg=_r1(rng, 18, 30), height=_r2(rng, 0.08, 0.11), roughness=_r2(rng, 0.7, 0.9), ), _call( "A second shoulder, lower, so the peak has company.", "raise_terrain", lat=_clamp_lat(lat - 9), lon=_wrap_lon(lon + 11), radius_deg=_r1(rng, 10, 16), height=_r2(rng, 0.04, 0.06), roughness=0.6, ), _call( "Mist for the high places.", "set_weather", kind="mist", intensity=_r2(rng, 0.4, 0.7), ), _call( "Set the words at the foot, in stone.", "inscribe_wish", text=_inscription(wish), style="stone", ), _done( "Let it stand. Mountains are mostly patience.", "A mountain stands where flatness was, wearing its mist like a vow.", ), ], } def _forest(wish: str, rng: random.Random, mood: str) -> dict: lat, lon = _anchor(rng) place = _place(rng) flora_hue = _r1(rng, 110, 150) if mood != "joyful" else _r1(rng, 60, 120) reading = ( f"A forest asks for patience, and gets mine. Trees in {place}, " f"glowgrass beneath them so the dark has small lights, and a soft " f"rain to keep the roots believing." ) return { "reading": reading, "turns": [ _call( "Trees first. Tall, unhurried.", "spawn_flora", lat=lat, lon=lon, radius_deg=_r1(rng, 14, 22), kind="trees", density=_r2(rng, 0.6, 0.8), hue=flora_hue, ), _call( "Glowgrass under them, for the small hours.", "spawn_flora", lat=_clamp_lat(lat + 3), lon=_wrap_lon(lon + 4), radius_deg=_r1(rng, 8, 14), kind="glowgrass", density=_r2(rng, 0.5, 0.9), hue=_r1(rng, 140, 180), ), _call( "Rain, gentle, so nothing here is ever finished.", "set_weather", kind="rain", intensity=_r2(rng, 0.2, 0.4), ), _call( "Fireflies, to carry the glowgrass up into the air.", "spawn_life", lat=lat, lon=lon, radius_deg=_r1(rng, 8, 14), kind="fireflies", count=rng.randint(6, 12), hue=_r1(rng, 150, 200), ), _done( "Forests finish themselves. I only start them.", f"A forest grows in {place}, lit from below and adrift with fireflies.", ), ], } def _mushroom(wish: str, rng: random.Random, mood: str) -> dict: lat, lon = _anchor(rng) hue = _r1(rng, 270, 320) reading = ( "Mushrooms, then — the small folk of the dark. I will seed them in a " "ring, set a shrine where their song gathers, and dim the sky to " "violet hours so they can be heard." ) return { "reading": reading, "turns": [ _call( "A ring of them, like a held note.", "spawn_flora", lat=lat, lon=lon, radius_deg=_r1(rng, 8, 14), kind="mushrooms", density=_r2(rng, 0.7, 0.95), hue=hue, ), _call( "A shrine at the center, to collect the song.", "place_structure", lat=lat, lon=lon, kind="shrine", scale=_r2(rng, 0.8, 1.2), hue=hue, ), _call( "Mist, thin, so the glow carries.", "set_weather", kind="mist", intensity=_r2(rng, 0.3, 0.5), ), _call( "Night above; violet hours below.", "set_sky", palette="night", star_density=_r2(rng, 0.7, 0.9), moons=rng.randint(1, 2), ), _done( "Let them sing to no one. That is still singing.", "In a ring of singing mushrooms, a shrine holds the long low note.", ), ], } def _lighthouse(wish: str, rng: random.Random, mood: str) -> dict: lat, lon = _anchor(rng) place = _place(rng) reading = ( f"Someone is thinking of the lost. I will raise a headland in " f"{place}, set a tower with a turning lamp upon it, and give the " f"dark water a reason to be less sure of itself." ) return { "reading": reading, "turns": [ _call( "Ground first; a tower needs a shoulder to stand on.", "raise_terrain", lat=lat, lon=lon, radius_deg=_r1(rng, 8, 12), height=_r2(rng, 0.05, 0.07), roughness=0.5, ), _call( "The tower. The lamp. The long patience.", "place_structure", lat=_clamp_lat(lat + 1), lon=_wrap_lon(lon - 1), kind="lighthouse", scale=_r2(rng, 1.4, 1.9), hue=_r1(rng, 40, 55), ), _call( "Dusk, so the beam means something.", "set_sky", palette="dusk", star_density=_r2(rng, 0.3, 0.5), moons=1, ), _done( "Lit things stay lit. That is the whole law of lighthouses.", f"A light turns in {place}, for the ones still on their way.", ), ], } def _village(wish: str, rng: random.Random, mood: str) -> dict: lat, lon = _anchor(rng) warm_hue = _r1(rng, 28, 46) reading = ( "A hearth-wish. These are my favorite, though a god should not say " "so. A village in a gentle fold of land, a small cafe with its light " "on the road, lantern-carts at dusk, and the words kept over the roofs." ) return { "reading": reading, "turns": [ _call( "A gentle rise, enough to shed the rain.", "raise_terrain", lat=lat, lon=lon, radius_deg=_r1(rng, 10, 14), height=_r2(rng, 0.02, 0.03), roughness=0.3, ), _call( "The houses, lit from inside.", "place_structure", lat=lat, lon=lon, kind="village", scale=_r2(rng, 1.0, 1.4), hue=_r1(rng, 30, 50), ), _call( "A cafe at the corner, its warmth spilled on the road.", "place_structure", lat=_clamp_lat(lat + 1), lon=_wrap_lon(lon - 2), kind="cafe", scale=_r2(rng, 0.8, 1.1), hue=warm_hue, ), _call( "Lantern-carts, gliding home at dusk.", "spawn_life", lat=lat, lon=lon, radius_deg=_r1(rng, 6, 10), kind="carts", count=rng.randint(3, 6), hue=warm_hue, ), _call( "Set the wish over the rooftops, where smoke would go.", "inscribe_wish", text=_inscription(wish), style="orbit", ), _done( "Small lights, kept on. There is no greater architecture.", "A village stands with its windows lit, and carts come and go beneath them.", ), ], } def _city(wish: str, rng: random.Random, mood: str) -> dict: # Everything clusters TIGHT around one anchor — a town you build up close, # not specks scattered across a sphere (town mode, v1.2). lat, lon = _anchor(rng) glow_hue = _r1(rng, 200, 280) warm_hue = _r1(rng, 30, 50) reading = ( "A city, then — the big wish, the brave one. I will lay a district " "where the town already stands, raise a tower over it, a warehouse and " "a cafe beside, and run a road and its carts between them." ) return { "reading": reading, "turns": [ _call( "First a whole district — houses and footpaths in one breath.", "build_district", lat=lat, lon=lon, radius_deg=_r1(rng, 6, 12), density=_r2(rng, 0.6, 0.9), hue=warm_hue, ), _call( "The tower over it, slender, its rooftop light slow-pulsing.", "place_structure", lat=_clamp_lat(lat + 1), lon=_wrap_lon(lon + 1), kind="tower", scale=_r2(rng, 1.4, 1.9), hue=glow_hue, ), _call( "A warehouse at its foot, one great door glowing.", "place_structure", lat=_clamp_lat(lat - 2), lon=_wrap_lon(lon + 2), kind="warehouse", scale=_r2(rng, 1.0, 1.4), hue=_r1(rng, 36, 52), ), _call( "A cafe to keep one window warm against all of it.", "place_structure", lat=_clamp_lat(lat + 2), lon=_wrap_lon(lon - 1), kind="cafe", scale=_r2(rng, 0.7, 1.0), hue=warm_hue, ), _call( "A road to bind them, paved and faintly lit.", "place_road", path=[ [_clamp_lat(lat - 2), _wrap_lon(lon + 2)], [lat, lon], [_clamp_lat(lat + 2), _wrap_lon(lon - 1)], ], ), _call( "Carts to thread the streets between them, slow and constant.", "spawn_life", lat=lat, lon=lon, radius_deg=_r1(rng, 6, 10), kind="carts", count=rng.randint(6, 10), hue=warm_hue, ), _done( "A city is only small lights agreeing to stay near each other.", "A district, a tower, a warehouse and cafe, a road and its carts — all in one place.", ), ], } def _town(wish: str, rng: random.Random, mood: str) -> dict: # The town-growth script: a district plus civic buildings (bank, market, # house), a road, and carts — all clustered into one block, so the mock # demo + headless shots show a real town densifying, not lone specks. lat, lon = _anchor(rng) warm_hue = _r1(rng, 28, 48) stone_hue = _r1(rng, 200, 250) reading = ( "A town wants growing, not founding anew. I will lay a fresh district " "where the others stand, set a bank and a market at its heart, a house " "on the corner, and a road to tie the block together." ) return { "reading": reading, "turns": [ _call( "A district first — a dozen small roofs and the paths between.", "build_district", lat=lat, lon=lon, radius_deg=_r1(rng, 6, 12), density=_r2(rng, 0.6, 0.9), hue=warm_hue, ), _call( "A bank at the heart, stone-faced and certain.", "place_structure", lat=_clamp_lat(lat + 1), lon=_wrap_lon(lon + 1), kind="bank", scale=_r2(rng, 0.9, 1.3), hue=stone_hue, ), _call( "A market beside it, its awnings warm at dusk.", "place_structure", lat=_clamp_lat(lat - 1), lon=_wrap_lon(lon + 2), kind="market", scale=_r2(rng, 0.8, 1.1), hue=warm_hue, ), _call( "One house on the corner, a lamp in its window.", "place_structure", lat=_clamp_lat(lat + 2), lon=_wrap_lon(lon - 1), kind="house", scale=_r2(rng, 0.7, 1.0), hue=warm_hue, ), _call( "A road to tie the block to the rest of the town.", "place_road", path=[ [_clamp_lat(lat + 2), _wrap_lon(lon - 1)], [lat, lon], [_clamp_lat(lat - 1), _wrap_lon(lon + 2)], ], ), _call( "Lantern-carts to walk the new street home.", "spawn_life", lat=lat, lon=lon, radius_deg=_r1(rng, 5, 9), kind="carts", count=rng.randint(3, 6), hue=warm_hue, ), _done( "A town grows the way moss does — outward, and toward the light.", "A new block stands by the others: a district, a bank, a market, a house, a lit road.", ), ], } def _storm(wish: str, rng: random.Random, mood: str) -> dict: lat, lon = _anchor(rng) reading = ( "A storm. Yes. Even a small world needs its temper. I will bruise " "the sky to ember, send the rain sideways, and let the thunder say " "the part I will not." ) return { "reading": reading, "turns": [ _call( "First the sky goes dark — ember, bruised at the edges.", "set_sky", palette="ember", star_density=_r2(rng, 0.1, 0.2), moons=rng.randint(0, 1), ), _call( "Now the storm itself, full-throated.", "set_weather", kind="storm", intensity=_r2(rng, 0.8, 1.0), ), _call( "Reeds in the lowlands; something must bend and live.", "spawn_flora", lat=lat, lon=lon, radius_deg=_r1(rng, 10, 16), kind="reeds", density=_r2(rng, 0.4, 0.6), hue=_r1(rng, 90, 130), ), _done( "Let it rage. Weather is honesty at scale.", "A storm now walks the world, and the world is briefly honest.", ), ], } def _snow(wish: str, rng: random.Random, mood: str) -> dict: lat, lon = _anchor(rng) reading = ( "Winter, asked for kindly. I will thin the sky, let snow stand in " "the air, and raise one pale hill for the cold to keep." ) return { "reading": reading, "turns": [ _call( "The sky first: thin, far, wintry.", "set_sky", palette=rng.choice(("night", "void")), star_density=_r2(rng, 0.5, 0.7), moons=rng.randint(1, 2), ), _call( "Snow, unhurried.", "set_weather", kind="snow", intensity=_r2(rng, 0.5, 0.8), ), _call( "One hill for the drifts to lean against.", "raise_terrain", lat=lat, lon=lon, radius_deg=_r1(rng, 12, 18), height=_r2(rng, 0.04, 0.06), roughness=0.5, ), _done( "Hush, then. Winter keeps its own ledger.", "Snow falls without hurry on a world that has agreed to hush.", ), ], } def _sky(wish: str, rng: random.Random, mood: str) -> dict: lat, lon = _anchor(rng) moons = rng.randint(2, 3) palette = rng.choice(("aurora", "night")) reading = ( "The mortal looks up; so will I. More stars, another moon or two, " "and the wish itself set in orbit where the night can read it." ) return { "reading": reading, "turns": [ _call( "More stars. There is no such thing as enough.", "set_sky", palette=palette, star_density=_r2(rng, 0.8, 1.0), moons=moons, ), _call( "The wish goes into orbit, where the night can read it.", "inscribe_wish", text=_inscription(wish), style="orbit", ), _call( "Glowgrass below, so the ground answers the sky.", "spawn_flora", lat=lat, lon=lon, radius_deg=_r1(rng, 8, 14), kind="glowgrass", density=_r2(rng, 0.4, 0.7), hue=_r1(rng, 150, 200), ), _call( "And a flock to circle beneath the new moons.", "spawn_life", lat=lat, lon=lon, radius_deg=_r1(rng, 10, 16), kind="birds", count=rng.randint(5, 9), hue=_r1(rng, 200, 260), ), _done( "Look up, little world. I rewrote you an answer.", f"The night is rewritten: more stars, {moons} moons, a flock, one wish in orbit.", ), ], } def _default(wish: str, rng: random.Random, mood: str) -> dict: lat, lon = _anchor(rng) short = _snippet(wish, 40) structure = rng.choice(("arch", "monolith")) style = rng.choice(("stone", "orbit")) article = "an" if structure == "arch" else "a" reading = ( f'I will be honest: I do not know what "{short}" is. The world has ' f"room for things I cannot name. I will make a small strange place " f"and let the wish live there as itself." ) structure_thought = { "arch": "An arch to nowhere, which is somewhere now.", "monolith": "A monolith, blank-faced, exactly as confused as I am.", }[structure] return { "reading": reading, "turns": [ _call( "Vines, I think. Nonsense climbs well.", "spawn_flora", lat=lat, lon=lon, radius_deg=_r1(rng, 6, 12), kind="vines", density=_r2(rng, 0.5, 0.8), hue=_r1(rng, 0, 360), ), _call( structure_thought, "place_structure", lat=_clamp_lat(lat + 2), lon=_wrap_lon(lon - 3), kind=structure, scale=_r2(rng, 0.7, 1.3), hue=_r1(rng, 0, 360), ), _call( f"The wish itself, set in {style}, exactly as spoken.", "inscribe_wish", text=_inscription(wish), style=style, ), _done( f"It is made. Let {article} {structure} explain it to the rain.", f'The world now holds "{_snippet(wish, 60)}", which is more than it held before.', ), ], } # -------------------------------------------------------------------------- # Category routing (first match wins; most specific first) # -------------------------------------------------------------------------- _CATEGORIES: tuple[tuple[re.Pattern[str], object], ...] = ( (re.compile(r"\b(mushrooms?|fungus|fungi|spores?)\b", re.I), _mushroom), (re.compile(r"\b(lighthouses?|beacons?|lamps?|ships?|sailors?|harbou?rs?|light)\b", re.I), _lighthouse), (re.compile(r"\b(towns?|districts?|neighbou?rhoods?|quarters?|suburbs?|blocks?|streets?|roads?|banks?|markets?|marketplaces?)\b", re.I), _town), (re.compile(r"\b(cit(?:y|ies)|towers?|skyscrapers?|warehouses?|caf[eé]s?|cafeterias?|cars?|carts?|traffic|downtown|metropolis|buildings?)\b", re.I), _city), (re.compile(r"\b(villages?|homes?|houses?|hearths?|kettles?|people|friends?|family)\b", re.I), _village), (re.compile(r"\b(seas?|oceans?|lakes?|rivers?|streams?|water|tides?|waves?|shores?|pools?)\b", re.I), _water), (re.compile(r"\b(mountains?|peaks?|hills?|cliffs?|summits?|crags?|valleys?)\b", re.I), _mountain), (re.compile(r"\b(forests?|trees?|groves?|woods?|firefl\w*|gardens?|meadows?|flowers?|blooms?)\b", re.I), _forest), (re.compile(r"\b(storms?|thunder|lightning|rain|winds?|tempests?)\b", re.I), _storm), (re.compile(r"\b(snow|winter|ice|frost|cold)\b", re.I), _snow), (re.compile(r"\b(stars?|sky|skies|night|moons?|auroras?|dawn|dusk|sun|comets?)\b", re.I), _sky), ) def build_script(wish: str) -> dict: """Build the deterministic script for a wish. Returns {"reading": str, "turns": [turn, ...]} where the final turn is a done turn. Same wish (modulo case/whitespace) always yields the same script. """ wish = (wish or "").strip() or "nothing in particular" rng = random.Random(_seed_for(wish)) mood, _guidance = infer_mood(wish) for pattern, builder in _CATEGORIES: if pattern.search(wish): return builder(wish, rng, mood) # type: ignore[operator] return _default(wish, rng, mood)