Give chapter 18 the six pieces a reader had to invent

A reader building the game from the chapter alone had to guess at all of
these, and three of them are code the chapter describes and never shows:

- the HUD eraser stamp, promised in Step 6 as "make a second one while you
  are here" and then never given
- the gem roll at the end of ONEBRICK, without which nothing ever drops
- BX1# and BY1#, which SPAWNGEM reads and only ONEBRICK can set

And three rules the chapter leans on without stating:

- MID and INSTR count from zero, which is what makes a glyph number an
  array index with no adjustment
- sprite n sets bit 2^(n-1), which is where 16, 32 and 64 come from
- states 5 and 6 have no tick routine on purpose; that is what a pause is

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
This commit is contained in:
2026-08-02 13:26:47 -04:00
parent fa5623b722
commit 851b03bcf6

View File

@@ -411,7 +411,18 @@ RETURN
Make a second one the width of the HUD strip while you are here, for the same reason: the
strip is rewritten whenever a number in it changes, and the old digits have to go
somewhere before the new ones are drawn.
somewhere before the new ones are drawn. It is the same code with a bigger rectangle:
```basic norun
FOR K# = 0 TO 59
DRAW 1, 0, 160 + K# TO 799, 160 + K#
NEXT K#
SSHAPE Z$, 0, 160, 800, 220 : HBL$ = Z$
```
Both blocks belong at the end of `DRAWPROTOS` in Step 4, drawn below the six brick
prototypes and captured with them, so the eight `SSHAPE` slots are all spent in one place.
`HBL$` is stamped at the top of the HUD redraw and `BL$` by `ERASEBRICK`.
**This is what makes the whole field a draw-once job.** Draw the walls and all sixty bricks
when a level is laid out, and after that touch only what changes. A program that had to
@@ -504,7 +515,11 @@ DATA 4, 0,6, 0,30, 3,33, 6,36
4-unit `SCORE` in the HUD.
Turning a character into a glyph number is one call: `INSTR(ALPHA$, MID(TX$, TXI#, 1))`
over an alphabet string. That is why the order of the `DATA` lines matters — they have to
over an alphabet string. **`MID` and `INSTR` both count from zero in this dialect**, which
is the opposite of Commodore BASIC and the opposite of most other BASICs — `MID(A$, 0, 1)`
is the first character, and `INSTR` answers 0 for a match at the front and 1 for no match
at all. That is what makes the glyph number an array index with no adjustment, and it is
also why `MID("0123456789", D#, 1)` turns a digit straight into its character. That is why the order of the `DATA` lines matters — they have to
match the order of the characters in `ALPHA$`. The finished game uses a 41-character
alphabet covering `A` to `Z`, `0` to `9`, space, colon, dash, full stop and exclamation
mark.
@@ -700,7 +715,8 @@ RETURN
```
**The mask is by sprite, and the balls are sprites 5, 6 and 7** — so their bits are 16, 32
and 64, not 1, 2 and 4. That is the same `5 + B#` arithmetic `MOVSPR` uses for them
and 64, not 1, 2 and 4. Sprite *n* sets bit 2 to the power of *n* 1: sprite 1 is 1,
sprite 2 is 2, sprite 3 is 4, sprite 4 is 8, sprite 5 is 16, and so on to sprite 8 at 128. That is the same `5 + B#` arithmetic `MOVSPR` uses for them
everywhere else, and getting it wrong costs nothing visible: the handler runs, tests bits
nothing sets, and returns.
@@ -724,11 +740,23 @@ SOLID N# + 1
BRN# = BRN# - 1
SCORE# = SCORE# + BRV#(R#)
SOUND 1, 17175 - R# * 2100, 4
BX1# = BRKX# + C# * 72
BY1# = BRKY# + R# * 24
GOSUB ERASEBRICK
GOSUB HUDTEXT
IF GMON# = 1 THEN RETURN
RNMAX# = 7
GOSUB NEXTRAND
IF RNVAL# = 0 THEN GOSUB SPAWNGEM
RETURN
```
`BX1#` and `BY1#` are the broken brick's top-left corner in pixels, and they are set here
because this is the only routine that knows it — Step 9's `SPAWNGEM` starts the gem falling
from that spot. The last four lines are the whole of "a broken brick drops a gem about one
time in seven": one roll, and `IF GMON# = 1 THEN RETURN` first because there is one gem
sprite and a gem already falling means no roll at all.
Fields 2, 3 and 4 push the ball exactly clear along the contact normal. Field 7 is which
axis to reverse — **the minimum translation axis**, which is why a ball clipping the end of
a row goes sideways rather than straight back down. Working that out by hand means building
@@ -976,6 +1004,12 @@ Each is a subroutine called once a frame while that state is current. This is a
shape from Chapter 17's branch targets, and it works here because nothing jumps out of a
loop to reach it.
There are seven state numbers and only five tick routines. **5 is game over and 6 is
paused, and neither has one on purpose**: the frame loop's `IF STATE# = n THEN GOSUB` list
simply has no line for them, so the frame draws whatever is on the screen, reads the
keyboard and does nothing else. That is exactly what a pause is, and writing an empty
subroutine to say so would cost a label.
```basic norun
LABEL TITLETICK
STIMER# = STIMER# + 1