PROJECT.md: the stream pass, and the two test traps it cost me

The doc edit threw on its second assert last commit, so the code shipped
without its notebook entry. Also records the two ways a drill test lies to
you: one that never shoots gets caught and the sweep looks like a stalled
stream (use eatenR = 0), and zombies[i] is not the same body across frames
because the array shifts at the baseline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-11 19:07:28 +02:00
1 parent 1d49c4343b
commit 57357c7865
1 file changed
+51 -5
+51 -5
View File
@@ -551,6 +551,53 @@ came from (patch `decodeAudioData`, pair it with the pending fetch URL) and coun
`AudioBufferSourceNode.start` — then "did two umpire lines overlap" is a real question with a real
answer. Counting requests measures loading; counting `start()` measures sound.
**FOURTH PASS — THE STREAM. Oliver played it again:** *"daniel announcing is maximum annoying"* ·
*"don't let the rush in position just let them coming from the back in steady speed.. try to space
them not too close"* · *"when one combination is done just let the new one in.. in a continuous
stream that increases in speed and difficulty"* · *"the rounds can stay, they should be continuous"*.
**The ROUND survives as the unit; every SEAM around it is deleted.** No walk-on to a mark, no
announcement beat, no between-rounds pause, no deal. Bodies walk in from the far baseline forever,
and a round is a colour combination drawn from whoever is on court — finish one and the next is
drawn in the same frame, off the same rolling population. The bodies you were ignoring a second ago
are the ones being called now, which is a better decoy than the old fixed-batch decoy ever was.
- **Daniel is gone from this mode.** At ~0.8 s a word he needed four seconds to read a list of four,
and with rounds running continuously that is a man talking over the entire game. Each colour has a
PITCH now (`DRILL_COLS.tone`) and a round announces itself as a four-note figure in half a second
(`blip()` in audio.js — a plain triangle with a 12 ms attack, because a hard attack clicks).
`TUNE.drillTones` 0 makes it silent. The `drill_*.m4a` recordings stay in assets: cheap to keep,
and a slower variant of the mode could want them back.
- **`PLACEMENTS` is deleted along with the marks.** Nothing rushes anywhere and nothing stops, so
there are no shapes to place — the geometry now comes from lane choice at spawn plus the depth the
stream naturally strings bodies out into. That is a net DELETION of the fanciest code in the file.
- **Spacing is best-of-9 lane sampling** against everyone still in the back half, not a retry loop:
a hard minimum would silently stall the stream at exactly the moment it is meant to be getting
harder, which reads as the mode dying.
- **A DRILL BODY HOLDS ITS LANE**, and this was the fix the screenshot forced. `updateZombies` homes
bodies onto the player's x once they cross the net — a gore-mode behaviour — so they arrived nicely
spread and then bunched into an unreadable knot right in front of you. Spawn spacing cannot survive
that. Their lane IS the puzzle here (it decides what screens what), so drill bodies walk it straight
in with nothing but the wobble. They still reach you; the lane runs past the baseline.
- **One ramp, off TIME SURVIVED** (`TUNE.drillRampT` 210 s), driving walk speed, spawn gap and round
length together. Time, not rounds won: a round-counted ramp cannot escalate while you stand still.
Speed is stamped per body AT SPAWN, so the ramp shows up as new arrivals outpacing the ones already
walking rather than as the whole court accelerating out from under you.
- **The empty-court watchdog is gone too, and nothing replaced it.** With a stream there is no state
to be stuck in: `if (!d.seq.length) newRound(d)` covers the start, a completion, a double fault and
"everything named got killed by something else". One line replaced a repair path, a cooldown and a
log entry.
- Movement is `updateZombies`' ordinary forward step now (bodies carry a real `zb.v`), so the stride
animates off distance covered for free — the round-based version drove them by hand because they
had marks to stop at.
- Tests rewritten around the stream (27): arrivals keep coming and are spaced *after the walk in*,
lanes are held by identity, the ramp raises speed / arrival rate / round length, a finished round
rolls into the next in the same frame, and being caught costs the point and the court but not the
run. **Two test traps worth remembering**: a drill test that never shoots WILL be caught (~28 s to
walk the court) and the sweep looks exactly like a stalled stream — use `TUNE.eatenR = 0`, the
established soak trick; and never compare `zombies[i]` across frames, because bodies despawn at the
baseline and the array shifts (that read as a 1.5 m lane drift that never happened).
**Open, and every one of them is Oliver's call after he plays it:**
1. **A ball that carries through the lane can complete two steps at once** — measured: one serve
killed bib 1 and then bib 2 standing behind it, banking two correct steps in one shot. Had the
@@ -559,11 +606,10 @@ answer. Counting requests measures loading; counting `start()` measures sound.
pierce has to stop on a drill kill. Do not patch it silently — it is a design fork, not a bug.
**More likely to come up now** that `column` deliberately stacks bodies in one lane.
2. ~~**The tap aims itself.**~~ CLOSED by the second pass above — the snap is gone, you aim.
3. Difficulty climbs on sequence LENGTH today (`drillSeq` 3 -> `drillSeqMax` 4, +1 every 3 rounds)
and is deliberately capped low: a longer list is a memory test and memory tests fatigue. The
`PLACEMENTS` table is where the real headroom is, and it is currently drawn at random with fixed
weights — nothing ramps. Weighting it toward `column`/`cluster` as the rounds go on is the
obvious next dial and is about four lines.
3. ~~Difficulty climbs on sequence LENGTH.~~ CLOSED by the stream — one ramp on time survived now
drives walk speed, arrival rate and round length together. What is still NOT ramped is how often
the called order fights the geometry; with lanes held, that is a question of which bodies get
called rather than of where they stand.
4. Nothing is wired to tennis scoring, the menu or the run, and the mode has no ending. That is the
next job IF the answer to "tense or noisy?" comes back tense.
5. **A shambler takes two splat-quality hits.** In a mode where every shot is aimed by hand that is