Skip to content

Keep your game

Save Sonar and check it from a fresh load.

  • Sinclair ZX Spectrum
  • BASIC

The game now has its intended rules. Keep a version that can start from its instructions when loaded, then check the whole experience without relying on an already running session.

Save the completed program

Enter Q to return to BASIC. Use the emulator’s tape-recording workflow with this ROM command:

BASIC
SAVE "sonar" LINE 10

"sonar" is the name stored in the tape header. LINE 10 records where execution should begin after loading. The SAVE command does not go into the numbered program. Follow the ROM’s recording prompt and export the captured tape to a file using your emulator. Keep both your readable source and the tape: one is easy to inspect, the other can be loaded by the Spectrum.

Start a fresh 48K session, attach that saved tape and enter:

BASIC
LOAD "sonar"

Start tape playback using your emulator’s controls. After loading, the instructions should appear automatically. If the ROM keeps searching, check the attached tape, playback and the saved program name. If the wrong program appears, check that you exported the latest recording.

Check a whole visit

Begin from the instructions. Enter an invalid coordinate, reveal two different clues, repeat a probe, find the target, retry, then quit. On retry, check that the new board is empty and the count is zero. A successful save message alone does not check any of those behaviours.

Why test in a fresh session instead of loading over the running game?

Show the explanation

A fresh session does not already contain the program or its variables. Loading and playing there demonstrates that the saved tape supplies what is needed to begin the game again.

The complete source is the same as the preceding lesson:

step-01.bas
  10 LET stage=6
  15 GO SUB 5000
  16 RANDOMIZE
  20 LET tr=INT (RND*8)+1: LET tc=INT (RND*8)+1
  25 DIM g(8,8)
  26 FOR r=1 TO 8: FOR c=1 TO 8: LET g(r,c)=-1: NEXT c: NEXT r
  27 LET n=0: LET lr=0: LET lc=0: LET pr=0: LET pc=0
  28 LET d=0: LET v=0: LET a$="": LET b$="": LET p$=""
  30 GO SUB 1000
  80 LET s$="N=1-2, M=3-4, F=5+ steps.": GO SUB 2600
 100 LET p$="Row": GO SUB 3000: LET pr=v
 110 LET p$="Column": GO SUB 3000: LET pc=v
 120 LET seen=g(pr,pc)
 130 LET d=seen
 140 IF seen<>-1 THEN GO TO 200
 150 LET d=ABS (pr-tr)+ABS (pc-tc)
 151 IF d>4 THEN LET d=3: GO TO 160
 152 IF d>2 THEN LET d=2: GO TO 160
 153 IF d>0 THEN LET d=1
 160 LET g(pr,pc)=d: LET n=n+1
 200 LET oldr=lr: LET oldc=lc: LET lr=pr: LET lc=pc
 210 IF oldr=0 THEN GO TO 240
 220 LET r=oldr: LET c=oldc: GO SUB 2100
 240 LET r=pr: LET c=pc: GO SUB 2100
 250 GO SUB 2500
 260 LET s$="Far: at least 5 steps away."
 261 IF d=2 THEN LET s$="Medium: 3 or 4 steps away."
 262 IF d=1 THEN LET s$="Near: 1 or 2 steps away."
 270 IF seen<>-1 THEN LET s$="Already probed. No extra count."
 280 IF d=0 THEN LET s$="Found in "+STR$ n+" distinct probes."
 290 GO SUB 2600
 300 IF d=0 THEN GO TO 6000
 310 GO TO 100
1000 BORDER 1: PAPER 0: INK 7: CLS
1010 PRINT AT 0,6;"SONAR N1-2 M3-4 F5+"
1020 FOR c=1 TO 8
1030 PRINT AT 2,6 +3 *(c-1);c
1040 NEXT c
1050 PRINT AT 3,4;"+------------------------+"
1060 PRINT AT 20,4;"+------------------------+"
1070 FOR r=1 TO 8
1080 PRINT AT 4 +2 *(r-1),2;r
1090 FOR k=0 TO 1
1100 PRINT AT 4 +2 *(r-1)+k,4;":";AT 4 +2 *(r-1)+k,29;":"
1110 NEXT k
1120 FOR c=1 TO 8
1130 GO SUB 2100
1150 NEXT c
1160 PAPER 0: INK 7
1170 NEXT r
1180 GO SUB 2500
1190 RETURN
2000 LET y=4 +2 *(r-1): LET x=5 +3 *(c-1)
2010 PAPER 1: INK 7
2020 IF r+c=2 *INT ((r+c)/2) THEN PAPER 5: INK 0
2030 RETURN
2100 GO SUB 2000
2110 LET z=g(r,c): LET b$=". "
2120 IF z=-1 THEN GO TO 2160
2130 LET b$=" F"
2140 IF z=2 THEN LET b$=" M"
2145 IF z=1 THEN LET b$=" N"
2150 IF z=0 THEN LET b$=" *"
2160 LET m$=" "
2170 IF r=lr AND c=lc THEN LET m$=">"
2180 PRINT AT y,x;m$;b$;AT y+1,x;"   "
2190 RETURN
2500 PAPER 0: INK 7
2510 PRINT AT 1,0;"Distinct probes: ";n;"   "
2520 RETURN
2600 PAPER 0: INK 7
2610 PRINT AT 21,0;"                               ";
2620 PRINT AT 21,0;s$;
2630 RETURN
3000 PAPER 0: INK 7
3010 INPUT (p$+" (1-8, Q): "); LINE a$
3015 IF LEN a$>16 THEN GO SUB 1000
3020 IF a$="q" OR a$="Q" THEN GO TO 9000
3030 IF LEN a$<>1 THEN GO TO 3100
3040 IF a$<"1" OR a$>"8" THEN GO TO 3100
3050 LET v=VAL a$
3060 RETURN
3100 LET s$="Use one digit from 1 to 8.": GO SUB 2600
3110 GO TO 3010
5000 BORDER 1: PAPER 0: INK 7: CLS
5010 PRINT AT 2,9;"SONAR - BANDS"
5020 PRINT AT 5,1;"Find one object on an 8x8 grid."
5030 PRINT AT 7,1;"Enter a row, then a column."
5040 PRINT AT 9,1;"N = near 1-2. M = medium 3-4."
5050 PRINT AT 11,1;"F = far 5+. Rows plus columns."
5060 PRINT AT 13,1;"Old clues stay. > marks latest."
5070 PRINT AT 15,1;"* means found. No probe limit."
5080 PRINT AT 18,1;"Q leaves from any prompt."
5090 INPUT "ENTER to search, Q quits: "; LINE a$
5100 IF a$="q" OR a$="Q" THEN GO TO 9000
5110 IF a$<>"" THEN GO TO 5000
5120 RETURN
6000 INPUT "R another round, Q quits: "; LINE a$
6010 IF LEN a$>16 THEN GO SUB 1000
6020 IF a$="q" OR a$="Q" THEN GO TO 9000
6030 IF a$="r" OR a$="R" THEN GO TO 20
6040 LET s$="Enter R for another round or Q.": GO SUB 2600
6050 GO TO 6000
9000 LET s$="Finished. RUN to try again.": GO SUB 2600
9010 STOP

Optional revisit: return to a program we already know

Put the game aside, then return using the saved source and tape rather than rebuilding it from the earlier lessons. Load a fresh 48K session as above and repeat the whole-visit check. Keep that working source and tape unchanged; make a separately named copy for this experiment.

Our intended change is small: use + instead of > to mark the latest probe. The clues, target and counting rules should stay the same. Before editing, find these parts in the complete source and explain their jobs:

Lines What to follow
200–240 Remember the old and new probe positions; redraw both cells
2100–2150 Read the cell’s stored clue and choose its displayed letters
2160–2180 Start with a blank marker, mark only the latest cell, then print
5060 Explain the marker on the instruction screen

Predict what happens to the old marker after the next probe. Which code removes it? Does moving the marker require changing g, the array that stores the clues?

Compare the explanation

The old cell is redrawn after lr and lc have changed to the latest probe. It no longer passes line 2170’s test, so line 2160’s blank marker survives. Its stored clue still comes from g(r,c). Presentation changes; the remembered clue does not.

In the experimental copy, change only the marker string in line 2170 from > to +. Update the matching marker in the instruction text at 5060. Keep the coordinate condition, clue characters and other lines as they are.

Load or run the changed version and compare with the prediction: probe one cell, probe a different cell, then repeat the first. If either probe finds the target, retry and choose two non-winning cells for this check. There should be one latest marker; both clues should remain, and the repeated probe should not add to the count. Retry and check that the fresh board has no old marker. Read the instructions too.

If two markers remain, inspect the redraw of the old cell and the blank-marker assignment before changing the counting code. If every cell gains a marker, inspect the condition at 2170. The visible symptom helps us choose where to look; it does not justify rewriting the whole display routine.

This is a small behaviour change, because the player sees a different symbol. A refactor would reorganise the implementation while preserving the agreed behaviour. Even then, we would check the affected costs and assumptions: extra redrawing takes time, and a routine can disturb shared variables despite producing the right final picture.

Keep a short note of the two edited lines, the intended change and the checks made. Save the experiment under a distinct name such as sonarplus, retaining the original source and tape. Once we work with source files in Git, a baseline commit and a small reviewed diff serve the same purpose; Git is not needed to understand this first revisit.

A search you can explain

The hidden world is two coordinates. The player’s knowledge is a two-dimensional array. Input changes that knowledge, and drawing turns it into a view. That division lets us recover the picture without changing the search and retry without accidentally keeping old clues.

Our game uses text characters and PAPER colours rather than imported artwork. The program and character-based graphics are original course material under the samples repository’s MIT licence. Sonar is deliberately silent; its letters and messages provide the essential feedback.

A larger board is a possible extension. Before changing it, sketch how the labels, clues and prompts would fit. Revisit the input range, array dimensions, coordinate conversion and random range together. Also consider what the unchanged far band would tell you on a much larger sea. A bigger search is useful only if its extra decisions are interesting.

Crates takes the grid in another direction: it stores a warehouse whose crates move when a push is accepted.

Sources

Steven Vickers, edited by Robin Bradbeer, ZX Spectrum BASIC Programming, second edition (Sinclair Research, 1983), chapter 20 (named tape programs and SAVE with LINE).

Run it here

This unit's program, built for you.

Program
step-01.tap
Machine
ZX Spectrum 48K (PAL)

Download step-01.tap