Crates
Turn one room into a game of three — DATA-driven levels selected with RESTORE, a restart key, a move count, and a play-again loop.
One solved room is a demo; three are a game. This last unit wraps the puzzle in everything around it —
a title, three levels chosen from DATA with RESTORE, a restart key for when you wedge a crate, a
move counter, and a loop back to the start.
10 BORDER 0: PAPER 0: INK 7: CLS
20 PRINT AT 5, 10; "*** CRATES ***"
30 PRINT AT 8, 3; "Push crates onto red targets."
40 PRINT AT 9, 3; "I/J/K/L to move. R restarts."
50 PRINT AT 11, 3; "You can push but never pull."
60 PRINT AT 14, 4; "Press any key to begin"
70 PAUSE 0
80 LET lv = 1: LET moves = 0
90 RESTORE 900 + (lv - 1) * 10
100 READ w, h
110 IF w = 0 THEN GO TO 790
120 LET sr = INT ((22 - h) / 2) + 1
130 LET sc = INT ((32 - w) / 2)
140 LET ps = 0: LET moves = 0
150 DIM g(h, w)
160 CLS
170 PRINT AT 0, 10; "*** CRATES ***"
180 PRINT AT 0, 0; "Level "; lv
190 PRINT AT 0, 24; "Moves: "; moves
200 FOR r = 1 TO h
210 READ r$
220 FOR c = 1 TO w
230 LET q$ = r$(c TO c)
240 IF q$ = "W" THEN LET g(r,c) = 1: PRINT AT sr+r-1, sc+c-1; PAPER 1; INK 0; " "
250 IF q$ = " " THEN PRINT AT sr+r-1, sc+c-1; PAPER 7; INK 0; " "
260 IF q$ = "." THEN LET g(r,c) = 2: PRINT AT sr+r-1, sc+c-1; PAPER 2; INK 0; " "
270 IF q$ = "C" THEN LET g(r,c) = 3: PRINT AT sr+r-1, sc+c-1; PAPER 6; INK 0; " "
280 IF q$ = "P" THEN LET pr = r: LET pc = c: PRINT AT sr+r-1, sc+c-1; PAPER 7; INK 4; "P"
290 NEXT c
300 NEXT r
320 IF INKEY$ <> "" THEN GO TO 320
330 LET k$ = INKEY$: IF k$ = "" THEN GO TO 330
340 IF k$ = "r" OR k$ = "R" THEN LET moves = 0: GO TO 90
350 LET dy = 0: LET dx = 0
360 IF k$ = "i" OR k$ = "I" THEN LET dy = -1
370 IF k$ = "k" OR k$ = "K" THEN LET dy = 1
380 IF k$ = "j" OR k$ = "J" THEN LET dx = -1
390 IF k$ = "l" OR k$ = "L" THEN LET dx = 1
400 IF dy = 0 AND dx = 0 THEN GO TO 320
410 LET nr = pr + dy: LET nc = pc + dx
420 LET v = g(nr, nc)
430 IF v = 1 THEN GO TO 320
440 IF v = 3 OR v = 4 THEN GO TO 510
450 IF v <> 0 AND v <> 2 THEN GO TO 320
460 PRINT AT sr+pr-1, sc+pc-1; PAPER (7-ps*5); INK 0; " "
470 LET ps = 0: IF v = 2 THEN LET ps = 1
480 LET pr = nr: LET pc = nc
490 PRINT AT sr+pr-1, sc+pc-1; PAPER (7-ps*5); INK 4; "P"
500 LET moves = moves + 1: PRINT AT 0, 24; "Moves: "; moves; " ": GO TO 320
510 REM --- Push crate ---
520 LET br = nr + dy: LET bc = nc + dx
530 LET bv = g(br, bc)
540 IF bv <> 0 AND bv <> 2 THEN BEEP 0.05, -5: GO TO 320
550 IF bv = 0 THEN LET g(br,bc) = 3: PRINT AT sr+br-1, sc+bc-1; PAPER 6; INK 0; " "
560 IF bv = 2 THEN LET g(br,bc) = 4: PRINT AT sr+br-1, sc+bc-1; PAPER 4; INK 0; " "
570 PRINT AT sr+pr-1, sc+pc-1; PAPER (7-ps*5); INK 0; " "
580 LET ps = 0: IF v = 4 THEN LET ps = 1
590 IF v = 3 THEN LET g(nr,nc) = 0
600 IF v = 4 THEN LET g(nr,nc) = 2
610 LET pr = nr: LET pc = nc
620 PRINT AT sr+pr-1, sc+pc-1; PAPER (7-ps*5); INK 4; "P"
630 LET moves = moves + 1
640 PRINT AT 0, 24; "Moves: "; moves; " "
650 REM --- All targets covered? ---
660 LET win = 1
670 FOR r = 1 TO h
680 FOR c = 1 TO w
690 IF g(r, c) = 2 THEN LET win = 0
700 NEXT c
710 NEXT r
720 IF win = 0 THEN GO TO 320
730 BEEP 0.1, 10: BEEP 0.1, 15: BEEP 0.1, 20
740 PRINT AT 20, 6; "Level complete! Press any key"
750 PAUSE 0
760 LET lv = lv + 1
770 GO TO 90
790 CLS
800 PRINT AT 6, 10; "*** CRATES ***"
810 PRINT AT 9, 4; INK 4; "All levels complete!"
820 BEEP 0.1, 10: BEEP 0.1, 15: BEEP 0.1, 20: BEEP 0.2, 25
830 PRINT AT 18, 4; "Press any key to play again"
840 PAUSE 0
850 LET lv = 1: LET moves = 0: GO TO 90
900 DATA 5,5
901 DATA "WWWWW"
902 DATA "W . W"
903 DATA "W C W"
904 DATA "W P W"
905 DATA "WWWWW"
910 DATA 6,6
911 DATA "WWWWWW"
912 DATA "W W"
913 DATA "W . W"
914 DATA "W C W"
915 DATA "W P W"
916 DATA "WWWWWW"
920 DATA 6,6
921 DATA "WWWWWW"
922 DATA "WP W"
923 DATA "WCC W"
924 DATA "W.. W"
925 DATA "W W"
926 DATA "WWWWWW"
930 DATA 0,0
Levels chosen with RESTORE
Each level is a block of DATA — dimensions then rows — at lines 900, 910, 920, ending in a 0,0
sentinel (line 930). Line 90 is the trick: RESTORE 900 + (lv - 1) * 10 jumps the DATA pointer to
the block for the current level, so the same loader reads level 1, 2 or 3 depending on lv. You used
RESTORE with a fixed line in Quiz Master; here the line is computed from a variable, which turns one
parser into a level loader. Line 110 ends the game when it reads the 0,0 sentinel — a classic
read-until-marker, like Cipher's word list.
Restart, count, replay
A push puzzle is quick to wedge — a crate in a corner is lost — so line 340 lets R reload the current
level by jumping back to the loader, no harm done. A move counter (line 500, 630) ticks each step and
shows in the status line, giving solvers something to optimise. When the last level's 0,0 sentinel is
reached, lines 790–850 show "All levels complete!" and GO TO 90 after a key, restarting from level
one.
What you built
Crates made Volume 2's idea literal: the 2D array is the world. You loaded a level from DATA
strings, then read and wrote that one grid for movement, wall collision, the push, crate-on-target
state, and the win check — no parallel data, no screen-scraping, the array always the single source of
truth. Along the way you took your first real-time input with INKEY$ and built a mechanic from a
two-cell lookahead. The lesson underneath is that one tight rule, cleanly modelled, can generate
endless depth — which is exactly what a good puzzle game is.
That closes Volume 2 — Patterns of State. Across eight games you have held state as a hidden value, a list, a 2D grid, a graph, a set of persistent resources, and now a writable world — and learned to read it, search it, evaluate it, and change it. Volume 3 — Worlds and Rules — builds on all of it, starting with real-time movement and the loops that drive a living screen.