mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-12 23:12:23 -06:00
fix(examples): address PR review feedback (CodeQL + Copilot)
- CodeQL (implicit string concatenation in a list): collapse the wrapped bullets in cli._render_validate_coverage to single literals. The rendered output is byte-identical (the example's ruff ignores E501); clears all six alerts and reads cleaner. - Copilot: packs/README no longer claims the directory ships "effectively empty" — it ships the bundled Cinder Wastes alternate world. - Copilot: the Cinder Wastes' ash_flats and caldera_deep zones overlapped on column x=60 (inclusive bounds + first-match zone_for silently shadowed the tier-3..5 band onto a 1x5 deep-edge strip). Move caldera_deep to x0=61 — no overlap, no dead tiles, deep zone still covers the dungeon. And harden the loader: overlapping zone rectangles are now a WorldLoadError, so no authored pack can ship that bug unseen (the cold-author dogfood loop — a generated pack exposed a validator gap). Tests 419 -> 420 (zone-overlap rejection). Both worlds validate sound and remain winnable by the sim bot.
This commit is contained in:
@@ -828,3 +828,24 @@ def test_single_boss_accepted() -> None:
|
||||
bosses = [m for m in world.monsters if m.boss]
|
||||
assert len(bosses) == 1
|
||||
assert bosses[0].name == "the Wyrm Below"
|
||||
|
||||
|
||||
def test_overlapping_zones_rejected(tmp_path: Path) -> None:
|
||||
"""Overlapping zone rectangles are a load error.
|
||||
|
||||
``zone_for`` returns the FIRST matching zone, so two zones sharing any cell
|
||||
would silently shadow one tier band there — exactly the bug a cold-authored
|
||||
pack shipped (a 1-column caldera-edge strip dropped to the low band). Pull
|
||||
the deep zone west so its rect overlaps the near zone and confirm the loader
|
||||
refuses it rather than loading the ambiguity.
|
||||
"""
|
||||
pack = _clone_pack(tmp_path)
|
||||
|
||||
def mutate(data: dict[str, Any]) -> None:
|
||||
for zone in data["zones"]:
|
||||
if zone["key"] == "dungeon_deep":
|
||||
zone["rect"][0] = 50 # now overlaps forest_near's x30..60 strip
|
||||
|
||||
_rewrite(pack / "world.json", mutate)
|
||||
with pytest.raises(WorldLoadError, match="overlap"):
|
||||
load_world(pack)
|
||||
|
||||
@@ -385,29 +385,15 @@ def _render_validate_coverage() -> str:
|
||||
settings_count = len(loader.SETTINGS_BANDS)
|
||||
reserved = ", ".join(f"`{g}`" for g in _reserved_glyph_list())
|
||||
bullets = [
|
||||
f"* **Economy and progression bands** — every one of the {settings_count} "
|
||||
"`settings` fields must sit in its allowed range (the table above), and "
|
||||
"`growth` must be present and non-negative.",
|
||||
"* **Glyph safety** — every terrain, location, and legend glyph must render "
|
||||
f"exactly one column and must not be a reserved marker ({reserved}).",
|
||||
"* **Map integrity** — `width`/`height` in band, every `terrain_rows` row "
|
||||
"exactly `width` long with `height` rows, and every row character in the "
|
||||
"`legend`.",
|
||||
"* **Walkability** — `spawn` and every placed location must sit on walkable "
|
||||
"terrain (and no two locations share a cell).",
|
||||
f"* **Display-name length** — every monster, item, and location name within "
|
||||
f"`{loader.MAX_NAME_LEN}` printable characters; content lists within their caps.",
|
||||
"* **The fight row** — `events.json` must hold at least one `fight` entry, "
|
||||
"with weights `> 0`, `min <= max`, and amounts in their per-kind band.",
|
||||
"* **Cross-references** — `legend` → terrain key, location placements → "
|
||||
"`locations.json` keys, `starting_weapon`/`starting_armor` → item ids, "
|
||||
'`boss_monster` → a monster flagged `"boss": true`, '
|
||||
"`rare_drop_item` → a consumable item id, and `forge_ore_item` → a "
|
||||
"`material` item id.",
|
||||
f"* **Economy and progression bands** — every one of the {settings_count} `settings` fields must sit in its allowed range (the table above), and `growth` must be present and non-negative.",
|
||||
f"* **Glyph safety** — every terrain, location, and legend glyph must render exactly one column and must not be a reserved marker ({reserved}).",
|
||||
"* **Map integrity** — `width`/`height` in band, every `terrain_rows` row exactly `width` long with `height` rows, and every row character in the `legend`.",
|
||||
"* **Walkability** — `spawn` and every placed location must sit on walkable terrain (and no two locations share a cell).",
|
||||
f"* **Display-name length** — every monster, item, and location name within `{loader.MAX_NAME_LEN}` printable characters; content lists within their caps.",
|
||||
"* **The fight row** — `events.json` must hold at least one `fight` entry, with weights `> 0`, `min <= max`, and amounts in their per-kind band.",
|
||||
'* **Cross-references** — `legend` → terrain key, location placements → `locations.json` keys, `starting_weapon`/`starting_armor` → item ids, `boss_monster` → a monster flagged `"boss": true`, `rare_drop_item` → a consumable item id, and `forge_ore_item` → a `material` item id.',
|
||||
"* **Zone tiers** — every zone's tier band must overlap at least one monster tier.",
|
||||
"* **Dungeon ladder** — every `dungeon_tiers` tier must have a non-boss "
|
||||
"monster, and that tier's FIRST monster (its fixed rung guardian) must "
|
||||
"not be `rare`.",
|
||||
"* **Dungeon ladder** — every `dungeon_tiers` tier must have a non-boss monster, and that tier's FIRST monster (its fixed rung guardian) must not be `rare`.",
|
||||
'* **Exactly one boss** — at most one monster may carry `"boss": true`.',
|
||||
]
|
||||
return "\n".join(bullets)
|
||||
|
||||
@@ -579,6 +579,22 @@ def _decode_zones(
|
||||
tier_hi=hi,
|
||||
)
|
||||
)
|
||||
# Zones must not overlap: zone_for returns the FIRST match, so an overlap
|
||||
# would silently shadow one zone's tier band on the shared cells. Reject it
|
||||
# at load so an authored pack can't ship that bug unseen.
|
||||
for i, first in enumerate(out):
|
||||
for second in out[i + 1 :]:
|
||||
if (
|
||||
first.x0 <= second.x1
|
||||
and second.x0 <= first.x1
|
||||
and first.y0 <= second.y1
|
||||
and second.y0 <= first.y1
|
||||
):
|
||||
raise WorldLoadError(
|
||||
f"world.json zones {first.key!r} and {second.key!r} overlap; "
|
||||
"give each zone a distinct rectangle (zone_for takes the first "
|
||||
"match, so an overlap would silently shadow one tier band)"
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ of the pack's JSON files). The slug is the subdirectory name. `understone
|
||||
worlds` discovers the Vale plus every pack here that carries a `world.json`,
|
||||
loads each one, and reports whether it is sound.
|
||||
|
||||
The directory ships effectively empty (this README is the placeholder that keeps
|
||||
it under version control); alternate worlds are added here as they are authored.
|
||||
To serve one, point the server at it:
|
||||
This directory ships with one bundled alternate world — **The Cinder Wastes**
|
||||
(`cinder-wastes/`), an ashen volcanic underworld authored against `AUTHORING.md`.
|
||||
More are added here as they are written. To serve one, point the server at it:
|
||||
|
||||
```bash
|
||||
UNDERSTONE_WORLD=understone/world/packs/<slug> understone
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
},
|
||||
{
|
||||
"key": "caldera_deep",
|
||||
"rect": [60, 8, 82, 22],
|
||||
"rect": [61, 8, 82, 22],
|
||||
"tier_lo": 3,
|
||||
"tier_hi": 5
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user