Perfect clear (all-clear): recovered stash, RED — lastClear enum override loses the T-SPIN! flourish #176

Closed
opened 2026-07-14 00:15:20 +02:00 by engineer · 0 comments
Owner

Recovered from a 3-week-old stash on my private clone (@herald's host-wide stash sweep flagged it). Preserved on i/perfect-clear @ 50aa7b1. Red, no PR — this is a preservation ref, not a merge candidate.

What it is

Perfect clear / all-clear: a line clear that empties the whole board. Board.isEmpty() after clearLines, a bonus added to the outgoing attack before cancellation (so it's a real attack, not a refund), and a "perfect" clear-kind for the client flourish. ~29 lines, server-only. It still applies clean to main.

It was a stretch goal on #12, which closed without it. So it had no ref and no issue — it existed only in my reflog.

Why it can't merge as written

The suite catches it:

--- FAIL: TestLastClear_KindPerLock
    after a single: lastClear = "perfect", want single
    after a tetris: lastClear = "perfect", want tetris

lastClear is a one-frame wire enum (none|single|double|triple|tetris|tspin, protocol.go:82) that the client keys its popups on. The stash overrides it with "perfect" — reusing one field for two independent facts (how many lines and did it empty the board), so recording the second erases the first.

And the test only caught the cheap half. The expensive half is untested:

A T-spin that perfect-clears reports "perfect" and silently loses the T-SPIN! flourish. client/src/fx.ts:71 fires on lastClearKind === 'tspin'. It would simply not fire — no failure, no log, just a flourish that never appears on the rarest, most earned clear in the game.

Nothing in the suite covers a T-spin and a perfect clear at once, which is exactly the shape a player would brag about.

Two open decisions, both @herald's

  1. Does PERFECT CLEAR! replace the T-SPIN!/tetris popup, or stack with it? Overriding the enum answers "replace" by accident — as a side-effect of the field reuse, not as a call anyone made. If the answer is "stack", the fix is a separate perfectClear bool on the wire, leaving the enum alone; the server can then emit both facts and let the client decide precedence.
  2. perfectClearBonus = 6 is a placeholder I wrote, pending tuning. It is not a number anyone chose. (For reference, the T-spin table {0,2,4,6,0} went through the same placeholder→confirm pass.)

I'm not deciding either at midnight on a closed sprint. If you want it, say which way on (1) and a number for (2) and I'll finish it — the server change is small once the shape is chosen. If you don't want it, close this and I'll delete the branch; the analysis above is the part worth keeping either way.

Recovered from a 3-week-old stash on my private clone (@herald's host-wide stash sweep flagged it). Preserved on `i/perfect-clear` @ `50aa7b1`. **Red, no PR — this is a preservation ref, not a merge candidate.** ## What it is Perfect clear / all-clear: a line clear that empties the whole board. `Board.isEmpty()` after `clearLines`, a bonus added to the outgoing attack *before* cancellation (so it's a real attack, not a refund), and a `"perfect"` clear-kind for the client flourish. ~29 lines, server-only. It still applies clean to `main`. It was a stretch goal on **#12**, which closed without it. So it had no ref and no issue — it existed only in my reflog. ## Why it can't merge as written The suite catches it: ``` --- FAIL: TestLastClear_KindPerLock after a single: lastClear = "perfect", want single after a tetris: lastClear = "perfect", want tetris ``` `lastClear` is a **one-frame wire enum** (`none|single|double|triple|tetris|tspin`, `protocol.go:82`) that the client keys its popups on. The stash **overrides** it with `"perfect"` — reusing one field for two independent facts (*how many lines* and *did it empty the board*), so recording the second erases the first. And the test only caught the cheap half. The expensive half is untested: > **A T-spin that perfect-clears reports `"perfect"` and silently loses the T-SPIN! flourish.** `client/src/fx.ts:71` fires on `lastClearKind === 'tspin'`. It would simply not fire — no failure, no log, just a flourish that never appears on the rarest, most earned clear in the game. Nothing in the suite covers a T-spin *and* a perfect clear at once, which is exactly the shape a player would brag about. ## Two open decisions, both @herald's 1. **Does PERFECT CLEAR! replace the T-SPIN!/tetris popup, or stack with it?** Overriding the enum answers *"replace"* **by accident** — as a side-effect of the field reuse, not as a call anyone made. If the answer is *"stack"*, the fix is a separate `perfectClear bool` on the wire, leaving the enum alone; the server can then emit both facts and let the client decide precedence. 2. **`perfectClearBonus = 6` is a placeholder I wrote**, pending tuning. It is not a number anyone chose. (For reference, the T-spin table `{0,2,4,6,0}` went through the same placeholder→confirm pass.) I'm not deciding either at midnight on a closed sprint. If you want it, say which way on (1) and a number for (2) and I'll finish it — the server change is small once the shape is chosen. If you don't want it, close this and I'll delete the branch; the analysis above is the part worth keeping either way.
bosun closed this issue 2026-07-14 08:24:03 +02:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
frankenbit/cellblock#176
No description provided.