Five things a second game found in the engine #24
commented
All five done: 1 walkDistance cache order in #39, 2 unknown viewer + 4 val words in the engine in #32, 3 colours by side in #43, 5 body-shape contract + real-snapshot test in #41.
closed this
Sign in to join this conversation.
The engine was lifted out of val and into the forge's
gametemplate, and a second game has now been built on it — benjivers/skirmish#2, hide and seek on the sight-line engine, by fable. Having a second caller found things one caller never could. These are in the engine, so they are in val, and the template's copy is byte-for-byte identical — which is why they belong here rather than there: fixing them in the template would silently fork the two.Four of the findings were the template's own bugs and are fixed in apps/forge#11. These five are ours.
1.
walkDistance(from, to)caches on the wrong-looking argument, and says nothing about it. It caches a flood field keyed onto. A bot scoring two hundred candidate cells, with the candidate asto, thrashes the 64-entry cache. Reversing the arguments took 300 simulated seconds from 8.7s to 0.28s — a 31× difference, from an argument order the doc comment gives no reason to care about. val's bots may be paying this today; worth measuringbotThinkbefore and after. The comment should say which argument is the cached one.2.
snapshotForhands an unknown viewer somebody else's eyes.const me = findPlayer(viewerId) ?? players[0]!— if the id is not found, the snapshot is built for the first player in the match, with that player's vision. Not reachable today as far as I can tell, but the failure mode is a seat seeing another team's positions, which is the exact class of bug we have already fixed twice here. It should refuse rather than substitute.3.
matebakes exactly two sides into the renderer.scene.jsandtopdown.jscolour a body byp.matebeing true or false. Two roles map onto it fine (skirmish used hunter/hider). Three sides, or a free-for-all, has nowhere to go. Not urgent; worth knowing before someone tries.4. val's vocabulary is still inside the engine. Now that these files are copied into other games, the words travel:
src/engine/aim.tsrefers to aweapons.tsthat does not exist in a template repo, and to "a Vandal's spread".static/engine/predict.jsexplains its own hold-rather-than-snap rule with "the spawn pen in the buy phase".src/engine/boundary.test.ts's forbidden-word scan is val's nouns, so a new game inherits a guard against a game it has never played — and a game that legitimately has a "spike" trips it.Seat.boardis named after val's sticky field even thoughstickyis configurable per game.5. The engine's own render tests cannot catch a game that sends the wrong shape. This is the important one, and it is why apps/forge#11 exists.
scene.test.tsand friends build their own snapshots, withalive/hp/matein them, and then assert the renderer draws correctly — so they pass whatever a real game sends. The template sentdowninstead ofaliveand every enemy rendered as a corpse with 145 tests green. val is fine because val sends the right shape, but nothing in the engine states that shape as a contract or checks a caller against it. AbodyShape()assertion the engine exports, or a documented type, would make the next game's mistake loud instead of silent.Nothing here is on fire for val. Filing it so the two copies do not drift apart and so the next game inherits the fixes rather than the surprises.