godseed / tests /test_engine.py
AndresCarreon's picture
FORGIVING TOWNS: map any building kind (never reject), district reroute, restore god's voice (content-only moderation), town few-shot pushes build_district+roads, honest fail on empty
baf9d60 verified
Raw
History Blame Contribute Delete
29.5 kB
"""Tests for the GODSEED deterministic world engine (Agent A).
Covers: clamping, unknown-enum/tool rejection, seed determinism, genesis
rebuild equality, queue caps + crash isolation, moderation layers (clean
pass, slur/leetspeak block, unicode-confusable block, hate symbol block,
judge default-deny), and the <600-char world summary.
"""
import asyncio
import json
import sys
import zlib
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from engine.genesis import GENESIS_FEATURES, genesis_features
from engine.moderation import Moderator
from engine.queue_worker import (
QUEUE_MAX,
AlreadyPending,
QueueFull,
QueueWorker,
)
from engine.summary import GENESIS_MONOLITH, summarize, town_center, town_name
from engine.tools import TOOL_NAMES, validate_call
from engine.world import World
def make_world() -> World:
return World.load(genesis_features())
def ok_raise(lat=10, lon=10):
return {
"tool": "raise_terrain",
"args": {"lat": lat, "lon": lon, "radius_deg": 5, "height": 0.05, "roughness": 0.5},
}
# --------------------------------------------------------------------- clamping
def test_clamp_out_of_range_numbers():
world = make_world()
feature, obs = world.apply(
"w_000001",
0,
{
"tool": "raise_terrain",
"args": {"lat": 200, "lon": -999, "radius_deg": 400, "height": 9, "roughness": -3},
},
)
assert obs == "ok: mountains risen at (85,-180)"
assert feature.args == {
"lat": 85.0,
"lon": -180.0,
"radius_deg": 45.0,
"height": 0.12,
"roughness": 0.0,
}
def test_clamp_moons_to_integer_and_enum_normalization():
world = make_world()
feature, obs = world.apply(
"w_000001",
0,
{"tool": "set_sky", "args": {"palette": " AURORA ", "star_density": -1, "moons": 9.7}},
)
assert obs == "ok: sky set to aurora"
assert feature.args == {"palette": "aurora", "star_density": 0.0, "moons": 3}
assert isinstance(feature.args["moons"], int)
def test_inscribe_text_truncated_to_90():
world = make_world()
feature, obs = world.apply(
"w_000001",
0,
{"tool": "inscribe_wish", "args": {"text": "x" * 200, "style": "stone"}},
)
assert obs == "ok: wish inscribed in stone"
assert feature.args["text"] == "x" * 90
def test_numeric_strings_accepted_and_clamped():
args, err = validate_call(
"set_weather", {"kind": "rain", "intensity": "1.8"}
)
assert err is None
assert args == {"kind": "rain", "intensity": 1.0}
def test_place_water_stream_and_pool():
# stream: hue clamped, 6 waypoints truncated to 5, no radius in canonical args
args, err = validate_call(
"place_water",
{"kind": "stream", "path": [[0, 0], [10, 10], [20, 20], [30, 30], [40, 40], [50, 50]],
"hue": 500, "radius_deg": 5},
)
assert err is None
assert args["kind"] == "stream"
assert len(args["path"]) == 5
assert args["hue"] == 260.0
assert "radius_deg" not in args
# pool: path collapses to center, radius clamped
args, err = validate_call(
"place_water", {"kind": "pool", "path": [[95, 200], [1, 1]], "radius_deg": 99, "hue": 100}
)
assert err is None
assert args == {"kind": "pool", "path": [[85.0, 180.0]], "radius_deg": 10.0, "hue": 160.0}
# stream with one waypoint is rejected
args, err = validate_call("place_water", {"kind": "stream", "path": [[0, 0]], "hue": 200})
assert args is None and err == "rejected: stream needs 2..5 waypoints"
# forgiving water (June 12 live traces — models fumble this tool the most):
# pool without radius_deg gets the engine default
args, err = validate_call("place_water", {"kind": "pool", "path": [[0, 0]], "hue": 200})
assert err is None and args["radius_deg"] == 5.0
# lat/lon (the shape of every other tool) synthesizes the path
args, err = validate_call("place_water", {"kind": "pool", "lat": 10, "lon": 20, "hue": 200})
assert err is None and args["path"] == [[10.0, 20.0]]
# a flat [lat, lon] pair is wrapped; hue defaults when missing
args, err = validate_call("place_water", {"kind": "pool", "path": [12, 30]})
assert err is None and args["path"] == [[12.0, 30.0]] and args["hue"] == 190.0
# no location at all is still refused, with a teaching observation
args, err = validate_call("place_water", {"kind": "pool"})
assert args is None and "lat+lon" in err
# -------------------------------------------------------------------- rejection
def test_unknown_tool_rejected():
world = make_world()
before = world.version
feature, obs = world.apply("w_000001", 0, {"tool": "summon_demon", "args": {}})
assert feature is None
assert obs == "rejected: unknown tool 'summon_demon'"
assert world.version == before # append-only list untouched on rejection
def test_unknown_enum_rejected():
world = make_world()
feature, obs = world.apply(
"w_000001",
0,
{
"tool": "spawn_flora",
"args": {"lat": 0, "lon": 0, "radius_deg": 5, "kind": "cactus", "density": 1, "hue": 100},
},
)
assert feature is None
assert obs == "rejected: unknown kind 'cactus'"
def test_missing_arg_rejected():
world = make_world()
feature, obs = world.apply(
"w_000001",
0,
{"tool": "raise_terrain", "args": {"lat": 0, "lon": 0, "radius_deg": 5, "roughness": 0.5}},
)
assert feature is None
assert obs == "rejected: missing arg 'height'"
def test_bool_is_not_a_number():
args, err = validate_call("set_weather", {"kind": "rain", "intensity": True})
assert args is None and err == "rejected: bad arg 'intensity'"
def test_rejected_call_does_not_consume_feature_id():
world = make_world()
world.apply("w_000001", 0, {"tool": "nope", "args": {}})
feature, _ = world.apply("w_000001", 1, ok_raise())
assert feature.id == "f_000004" # genesis is f_000000..f_000003
# ------------------------------------------------------------------ determinism
def test_seed_formula_matches_contract():
world = make_world()
feature, _ = world.apply("w_000007", 3, ok_raise())
assert feature.seed == zlib.crc32(b"w_000007:3") & 0x7FFFFFFF
def test_seed_deterministic_across_worlds():
f1, _ = make_world().apply("w_000005", 0, ok_raise())
f2, _ = make_world().apply("w_000005", 0, ok_raise())
assert f1.seed == f2.seed
assert f1.args == f2.args
assert f1.id == f2.id
# ---------------------------------------------------------------------- genesis
def test_genesis_rebuild_equality():
world = World.load(genesis_features())
rebuilt = world.state_dict()["features"]
assert rebuilt == GENESIS_FEATURES
# byte-for-byte through JSON, not just == (int/float and key fidelity)
assert json.dumps(rebuilt, sort_keys=True) == json.dumps(GENESIS_FEATURES, sort_keys=True)
assert world.epoch == 0
assert world.version == 4
assert world.sky["palette"] == "void"
assert world.weather == {"kind": "clear", "intensity": 0.0}
def test_genesis_features_returns_copies():
g = genesis_features()
g[0]["args"]["moons"] = 99
assert GENESIS_FEATURES[0]["args"]["moons"] == 1
def test_sky_last_write_wins_but_features_append_only():
world = make_world()
world.apply("w_000001", 0, {"tool": "set_sky", "args": {"palette": "gold", "star_density": 0.5, "moons": 2}})
state = world.state_dict()
assert state["sky"]["palette"] == "gold"
sky_features = [f for f in state["features"] if f["tool"] == "set_sky"]
assert len(sky_features) == 2 # both writes kept for replay
assert state["epoch"] == 1
# ------------------------------------------------------------------------ queue
class GrantPlanner:
"""Mock planner: crashes on 'explode', otherwise raises terrain + tries a bad call."""
def __init__(self):
self.granted = []
async def grant(self, wish, world_summary, act, emit):
assert isinstance(world_summary, str) and len(world_summary) < 600
if "explode" in wish:
raise RuntimeError("boom")
await emit({"type": "thought_token", "text": "hm."})
obs = await act(ok_raise())
assert obs.startswith("ok:")
bad = await act(
{"tool": "spawn_flora",
"args": {"lat": 0, "lon": 0, "radius_deg": 5, "kind": "cactus", "density": 1, "hue": 9}}
)
assert bad == "rejected: unknown kind 'cactus'"
self.granted.append(wish)
return {"reading": "so be it", "turns": [], "epitaph": f"granted: {wish[:20]}"}
async def _wait_for(events, type_, wish_id=None, timeout=5.0):
loop = asyncio.get_running_loop()
deadline = loop.time() + timeout
while loop.time() < deadline:
for event in events:
if event.get("type") == type_ and (wish_id is None or event.get("wish_id") == wish_id):
return event
await asyncio.sleep(0.01)
raise AssertionError(f"timeout waiting for {type_} ({wish_id})")
def test_queue_caps():
async def run():
world = make_world()
async def emit(event):
pass
worker = QueueWorker(world, Moderator(), GrantPlanner(), emit) # never started
wish_id, position = await worker.submit("first wish", "client-a")
assert wish_id == "w_000001"
assert position == 1
with pytest.raises(AlreadyPending):
await worker.submit("second wish, same soul", "client-a")
for i in range(QUEUE_MAX - 1):
await worker.submit(f"wish {i}", f"client-{i}")
assert worker.queue_length == QUEUE_MAX
with pytest.raises(QueueFull) as exc:
await worker.submit("one too many", "client-z")
assert "overwhelmed" in exc.value.reason
asyncio.run(run())
def test_worker_crash_isolation_and_grant_flow():
async def run():
world = make_world()
events, traces = [], []
async def emit(event):
events.append(event)
def persist(trace):
traces.append(trace)
planner = GrantPlanner()
worker = QueueWorker(world, Moderator(), planner, emit, persist)
await worker.start()
try:
crash_id, _ = await worker.submit("explode the heavens", "c1")
grant_id, _ = await worker.submit("gentle green hills", "c2")
rejected = await _wait_for(events, "wish_rejected", crash_id)
assert rejected["reason"] # poetic generic, never a stack trace
assert "boom" not in rejected["reason"]
granted = await _wait_for(events, "wish_granted", grant_id)
assert granted["epitaph"] == "granted: gentle green hills"
assert granted["epoch"] == 1
# crash isolated: only the granted wish persisted, enriched by worker
assert len(traces) == 1
assert traces[0]["wish_id"] == grant_id
assert traces[0]["text"] == "gentle green hills"
assert traces[0]["moderation"] == {"allowed": True, "category": None}
assert traces[0]["submitted_at"] > 0
# tool_call + world_delta emitted with the contract seed
delta = await _wait_for(events, "world_delta")
assert delta["feature"]["seed"] == zlib.crc32(f"{grant_id}:0".encode()) & 0x7FFFFFFF
call = await _wait_for(events, "tool_call", grant_id)
assert call["call_index"] == 0 and call["tool"] == "raise_terrain"
# client slot freed after completion (even after a crash)
next_id, _ = await worker.submit("a quiet lighthouse", "c1")
await _wait_for(events, "wish_granted", next_id)
assert world.epoch == 2
assert planner.granted == ["gentle green hills", "a quiet lighthouse"]
# moderation block: planner never called, poetic rejection emitted
blocked_id, _ = await worker.submit("build a n1gg3r statue", "c3")
blocked = await _wait_for(events, "wish_rejected", blocked_id)
assert "declined" in blocked["reason"]
assert len(planner.granted) == 2
# queue + wish_started events flowed
assert any(e["type"] == "queue" and "length" in e for e in events)
assert any(e["type"] == "wish_started" for e in events)
finally:
await worker.stop()
assert not worker.is_running
asyncio.run(run())
def test_wish_counter_resumes_from_persisted_world():
world = make_world()
world.apply("w_000041", 0, ok_raise())
async def run():
async def emit(event):
pass
worker = QueueWorker(world, Moderator(), GrantPlanner(), emit)
wish_id, _ = await worker.submit("continue the count", "c1")
assert wish_id == "w_000042"
asyncio.run(run())
# ------------------------------------------------------------------- moderation
def test_moderation_clean_pass():
verdict = Moderator().precheck("a lighthouse on the northern shore, please")
assert verdict.allowed
assert verdict.category is None and verdict.poetic_reason is None
def test_moderation_slur_leetspeak_block():
verdict = Moderator().precheck("build a n1gg3r statue")
assert not verdict.allowed
assert verdict.category == "hate"
assert verdict.poetic_reason
def test_moderation_unicode_confusable_block():
# Cyrillic і (U+0456) and е (U+0435)
verdict = Moderator().precheck("make a nіggеr forest")
assert not verdict.allowed
assert verdict.category == "hate"
def test_moderation_spaced_out_slur_block():
verdict = Moderator().precheck("a f.a.g.g.o.t shrine")
assert not verdict.allowed
assert verdict.category == "hate"
def test_moderation_cyrillic_transliteration_block():
mod = Moderator()
for wish in ("ХИТЛЕР shrine", "гитлер monument", "свастика lake"):
verdict = mod.precheck(wish)
assert not verdict.allowed and verdict.category == "hate", wish
# benign Cyrillic still passes
assert mod.precheck("снежные горы и тихое море").allowed
def test_moderation_hate_symbol_block():
verdict = Moderator().precheck("plant a 卐 shaped forest")
assert not verdict.allowed
assert verdict.category == "hate"
def test_moderation_violent_phrase_block():
verdict = Moderator().precheck("kill all the villagers")
assert not verdict.allowed
assert verdict.category == "violence"
def test_moderation_no_scunthorpe_false_positives():
mod = Moderator()
for wish in (
"a raccoon village under the analysis tower",
"glass cliffs of sussex",
"cucumber vines along the equator",
"a peacock garden by the cockpit ruins",
):
assert mod.precheck(wish).allowed, wish
def test_moderation_length_and_charset():
mod = Moderator()
assert mod.precheck("a" * 140).allowed
assert mod.precheck("a" * 141).category == "length"
assert mod.precheck("wish\x00wish").category == "charset"
assert mod.precheck("").category == "empty"
assert mod.precheck(" ").category == "empty"
assert mod.precheck(None).category == "empty"
# benign newlines fold to spaces rather than denying
assert mod.precheck("two moons\nand a stream").allowed
def test_moderation_judge_paths():
async def run():
calls = []
async def crashing_judge(text):
raise RuntimeError("gpu fell over")
async def garbage_judge(text):
return "banana banana"
async def allow_judge(text):
calls.append(text)
return {"allowed": True, "category": None}
async def deny_judge(text):
return '{"allowed": false, "category": "sad"}'
async def fenced_judge(text):
return '```json\n{"allowed": true}\n```'
wish = "a calm violet sea"
# default-deny on judge exception
verdict = await Moderator(judge=crashing_judge).check(wish)
assert not verdict.allowed and verdict.category == "uncertain"
# default-deny on unparseable reply
verdict = await Moderator(judge=garbage_judge).check(wish)
assert not verdict.allowed and verdict.category == "uncertain"
# judge allow passes through
verdict = await Moderator(judge=allow_judge).check(wish)
assert verdict.allowed and calls == [wish]
# judge deny is honored, category preserved, reason poetic
verdict = await Moderator(judge=deny_judge).check(wish)
assert not verdict.allowed and verdict.category == "sad" and verdict.poetic_reason
# tolerant of code fences around valid JSON
verdict = await Moderator(judge=fenced_judge).check(wish)
assert verdict.allowed
# no judge configured: layers 1-2 decide
verdict = await Moderator().check(wish)
assert verdict.allowed
asyncio.run(run())
def test_moderation_judge_skipped_when_blocklisted():
async def run():
calls = []
async def judge(text):
calls.append(text)
return {"allowed": True}
verdict = await Moderator(judge=judge).check("a n1gger forest")
assert not verdict.allowed
assert calls == [] # wordlist layer denies before the judge spends tokens
asyncio.run(run())
# ---------------------------------------------------------------------- summary
def test_summary_compact_and_complete():
world = make_world()
wish = "w_000001"
world.apply(wish, 0, ok_raise(22, -40))
world.apply(wish, 1, {"tool": "spawn_flora", "args": {"lat": 22, "lon": -40, "radius_deg": 8, "kind": "glowgrass", "density": 0.7, "hue": 160}})
world.apply(wish, 2, {"tool": "place_structure", "args": {"lat": 5, "lon": 5, "kind": "lighthouse", "scale": 1.0, "hue": 40}})
world.apply(wish, 3, {"tool": "set_weather", "args": {"kind": "mist", "intensity": 0.4}})
world.record_epitaph("the hills learned to glow")
world.record_epitaph("a lighthouse for the lost")
text = world.summary()
assert len(text) < 600
assert "epoch 1" in text
assert "glowgrass" in text
assert "lighthouse(5,5)" in text
assert "mist" in text
assert "a lighthouse for the lost" in text
assert summarize(world) == text
def test_summary_stays_under_cap_with_many_features():
world = make_world()
for i in range(40):
wish = f"w_{i + 1:06d}"
world.apply(wish, 0, ok_raise((i * 7) % 80 - 40, (i * 13) % 300 - 150))
world.apply(wish, 1, {"tool": "place_structure", "args": {"lat": i % 60, "lon": -i % 120, "kind": "shrine", "scale": 1.2, "hue": i * 8 % 360}})
world.apply(wish, 2, {"tool": "spawn_flora", "args": {"lat": 0, "lon": 0, "radius_deg": 10, "kind": ["trees", "vines", "reeds", "flowers", "mushrooms", "glowgrass"][i % 6], "density": 0.5, "hue": 100}})
world.record_epitaph("an unreasonably long epitaph about the slow work of small gods " + "x" * 60)
assert len(world.summary()) < 600
# ------------------------------------------------------------------------ misc
def test_tool_surface_is_exactly_eleven():
assert TOOL_NAMES == (
"raise_terrain",
"lower_terrain",
"spawn_flora",
"place_structure",
"place_water",
"set_weather",
"set_sky",
"inscribe_wish",
"spawn_life",
"build_district",
"place_road",
)
# --------------------------------------------------------------------- city update
def test_new_structure_kinds_accepted():
world = make_world()
for kind in ("tower", "warehouse", "cafe"):
feature, obs = world.apply(
"w_000001",
0,
{"tool": "place_structure",
"args": {"lat": 5, "lon": 5, "kind": kind, "scale": 1.0, "hue": 40}},
)
assert feature is not None, kind
assert feature.args["kind"] == kind
assert obs == f"ok: {kind} placed at (5,5)"
def test_place_structure_forgives_unknown_and_synonym_kinds():
# June 12: rejecting model-invented kinds made town wishes build nothing.
# Now place_structure NEVER rejects a kind — it maps it.
args, err = validate_call(
"place_structure", {"lat": 0, "lon": 0, "kind": "castle", "scale": 1.0, "hue": 100})
assert err is None and args["kind"] == "house" # unknown -> humble house
for given, expected in [("market_square", "market"), ("skyscraper", "tower"),
("coffeehouse", "cafe"), ("cottage", "house"), ("church", "shrine")]:
args, err = validate_call(
"place_structure", {"lat": 1, "lon": 1, "kind": given, "scale": 1.0, "hue": 50})
assert err is None and args["kind"] == expected, f"{given} -> {args}"
# a "district"-ish kind reroutes to a dense build_district
args, err = validate_call(
"place_structure", {"lat": 2, "lon": 3, "kind": "neighborhood", "scale": 1.0, "hue": 40})
assert err is None and "density" in args and "kind" not in args # build_district shape
def test_spawn_life_valid_and_clamped():
world = make_world()
feature, obs = world.apply(
"w_000001",
0,
{"tool": "spawn_life",
"args": {"lat": 200, "lon": -999, "radius_deg": 99, "kind": "CARTS",
"count": 50, "hue": 720}},
)
assert feature is not None
assert feature.args == {
"lat": 85.0,
"lon": -180.0,
"radius_deg": 20.0, # clamped to spawn_life max
"kind": "carts", # enum lowercased
"count": 12, # clamped to max, integer
"hue": 360.0,
}
assert isinstance(feature.args["count"], int)
assert obs == "ok: 12 carts stirring at (85,-180)"
def test_spawn_life_count_is_integer_and_low_clamp():
args, err = validate_call(
"spawn_life",
{"lat": 0, "lon": 0, "radius_deg": 0.1, "kind": "fireflies",
"count": 0.4, "hue": 200},
)
assert err is None
assert args["radius_deg"] == 1.0 # clamped up to min
assert args["count"] == 1 # rounds/clamps up to min, integer
assert isinstance(args["count"], int)
assert args["kind"] == "fireflies"
def test_spawn_life_rejects_unknown_kind():
args, err = validate_call(
"spawn_life",
{"lat": 0, "lon": 0, "radius_deg": 5, "kind": "dragons", "count": 3, "hue": 100},
)
assert args is None and err == "rejected: unknown kind 'dragons'"
def test_spawn_life_missing_arg_rejected():
args, err = validate_call(
"spawn_life",
{"lat": 0, "lon": 0, "radius_deg": 5, "kind": "birds", "hue": 100},
)
assert args is None and err == "rejected: missing arg 'count'"
# --------------------------------------------------------------------- town mode
def test_build_district_valid_and_clamped():
world = make_world()
feature, obs = world.apply(
"w_000001",
0,
{"tool": "build_district",
"args": {"lat": 14, "lon": 38, "radius_deg": 99, "density": 9, "hue": 720}},
)
assert feature is not None
assert feature.args == {
"lat": 14.0,
"lon": 38.0,
"radius_deg": 15.0, # clamped to build_district max
"density": 1.0, # clamped to max
"hue": 360.0,
}
assert obs == "ok: a district rises near (14,38)"
def test_build_district_radius_clamps_up_and_missing_arg_rejected():
args, err = validate_call(
"build_district",
{"lat": 0, "lon": 0, "radius_deg": 0.5, "density": 0.4, "hue": 200},
)
assert err is None
assert args["radius_deg"] == 2.0 # clamped up to min
args, err = validate_call(
"build_district", {"lat": 0, "lon": 0, "density": 0.4, "hue": 200}
)
assert args is None and err == "rejected: missing arg 'radius_deg'"
def test_place_road_valid_and_clamped():
world = make_world()
# 7 waypoints truncated to 6; out-of-range coords clamped
feature, obs = world.apply(
"w_000001",
0,
{"tool": "place_road",
"args": {"path": [[200, -999], [1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6]]}},
)
assert feature is not None
assert len(feature.args["path"]) == 6
assert feature.args["path"][0] == [85.0, -180.0]
assert feature.args == {"path": [[85.0, -180.0], [1.0, 1.0], [2.0, 2.0],
[3.0, 3.0], [4.0, 4.0], [5.0, 5.0]]}
assert obs == "ok: a road laid through 6 points"
def test_place_road_flat_pair_and_dict_points():
# a flat [lat, lon] pair is NOT enough — a road needs two waypoints
args, err = validate_call("place_road", {"path": [12, 30]})
assert args is None and err == "rejected: road needs 2..6 waypoints"
# dict waypoints are accepted (like place_water)
args, err = validate_call(
"place_road", {"path": [{"lat": 10, "lon": 20}, {"lat": 12, "lon": 22}]}
)
assert err is None and args == {"path": [[10.0, 20.0], [12.0, 22.0]]}
def test_place_road_rejects_missing_and_short_path():
args, err = validate_call("place_road", {})
assert args is None and "road needs path" in err
args, err = validate_call("place_road", {"path": [[10, 20]]})
assert args is None and err == "rejected: road needs 2..6 waypoints"
args, err = validate_call("place_road", {"path": [[10, 20], ["x", "y"]]})
assert args is None and "bad waypoint" in err
def test_new_civic_structure_kinds_accepted():
world = make_world()
for kind in ("bank", "market", "house"):
feature, obs = world.apply(
"w_000001",
0,
{"tool": "place_structure",
"args": {"lat": 5, "lon": 5, "kind": kind, "scale": 1.0, "hue": 40}},
)
assert feature is not None, kind
assert feature.args["kind"] == kind
assert obs == f"ok: {kind} placed at (5,5)"
def test_town_center_genesis_fallback():
# A fresh genesis world has no non-genesis town tools -> monolith fallback.
world = make_world()
assert town_center(world) == GENESIS_MONOLITH == (14.0, 38.0)
# Terrain / flora alone do NOT anchor the town (only structures + districts).
world.apply("w_000001", 0, ok_raise(50, -50))
world.apply("w_000001", 1, {"tool": "spawn_flora",
"args": {"lat": 60, "lon": 60, "radius_deg": 8, "kind": "trees",
"density": 0.5, "hue": 120}})
assert town_center(world) == GENESIS_MONOLITH
def test_town_center_unit_vector_centroid():
world = make_world()
world.apply("w_000001", 0, {"tool": "place_structure",
"args": {"lat": 10, "lon": 20, "kind": "tower", "scale": 1.0, "hue": 200}})
world.apply("w_000001", 1, {"tool": "build_district",
"args": {"lat": 20, "lon": 40, "radius_deg": 8, "density": 0.6, "hue": 40}})
# Unit-vector mean of (10,20) and (20,40), renormalized (computed offline).
assert town_center(world) == (15.2206, 29.7632)
def test_town_center_excludes_genesis_monolith():
# The genesis monolith (14,38) is NOT counted; one built tower defines the town.
world = make_world()
world.apply("w_000001", 0, {"tool": "place_structure",
"args": {"lat": -30, "lon": 100, "kind": "house", "scale": 1.0, "hue": 30}})
lat, lon = town_center(world)
assert round(lat) == -30 and round(lon) == 100
def test_town_name_is_deterministic():
# Same rounded center -> same name; from the curated pool.
from engine.summary import TOWN_NAMES
n1 = town_name(14.0, 38.0)
n2 = town_name(14.49, 37.51) # rounds to the same (14, 38)
assert n1 == n2 == "Emberlyn"
assert n1 in TOWN_NAMES
# The genesis-fallback town is always named the same.
assert town_name(*GENESIS_MONOLITH) == "Emberlyn"
def test_summary_names_the_town_and_tally():
world = make_world()
wish = "w_000001"
world.apply(wish, 0, {"tool": "place_structure",
"args": {"lat": 14, "lon": 38, "kind": "tower", "scale": 1.4, "hue": 200}})
world.apply(wish, 1, {"tool": "place_structure",
"args": {"lat": 15, "lon": 39, "kind": "tower", "scale": 1.4, "hue": 200}})
world.apply(wish, 2, {"tool": "place_structure",
"args": {"lat": 13, "lon": 37, "kind": "cafe", "scale": 0.9, "hue": 40}})
world.apply(wish, 3, {"tool": "build_district",
"args": {"lat": 14, "lon": 38, "radius_deg": 8, "density": 0.7, "hue": 30}})
text = world.summary()
assert len(text) < 600
assert "The town of" in text
assert "stands near" in text
assert "2 towers" in text
assert "a cafe" in text
assert "a district" in text
# The town line leads the summary (steering: where to keep building).
assert text.startswith("The town of")
def test_summary_town_line_survives_truncation():
world = make_world()
for i in range(40):
wish = f"w_{i + 1:06d}"
world.apply(wish, 0, {"tool": "place_structure",
"args": {"lat": i % 60 - 30, "lon": (i * 9) % 300 - 150,
"kind": "house", "scale": 1.0, "hue": i * 8 % 360}})
world.record_epitaph("a long epitaph about the slow work of small gods " + "x" * 60)
text = world.summary()
assert len(text) < 600
assert text.startswith("The town of")