Brain Terrain is a text-based mystery/adventure game in which the player must identify a missing person’s home year by accessing memory zones labelled with single-letter keys (A, B, C, E, F, M, P, S, T, V). Each zone contains one or more encoded clues stored in a compressed string table; the player cycles through clues displayed with a scrolling marquee effect and ultimately guesses a four-digit year. Clue text is stored compactly in the string T$ using a delta-encoding scheme where each character’s code is offset relative to the first character of each entry, with comma (CHR$ 26 in this context) as a delimiter. A help feature, unlocked only after a failed guess, reveals the last words heard by the character Mr. Winkler.
Program Analysis
Program Structure
The program is divided into a main loop and three subroutines:
- Lines 5–350: Initialisation — dimensions, string constants, and decompression of the clue table from
T$into the arrayA$. - Lines 360–860: Main game loop — title display, zone selection, marquee display, guess handling, and help.
- Lines 1000–1130: Subroutine
GOSUB 1000— word-wrapping text printer that splitsR$onCHR$ 0boundaries. - Lines 2000–2020: Subroutine
GOSUB 2000— centred single-line printer usingTAB. - Line 3000:
SAVEstatement (not reached during normal play).
Delta-Encoded Clue Table
All clue text is packed into the single string T$ (line 180) and decompressed at startup (lines 190–340) into the three-dimensional string array A$(11,4,22). The 11 zones have between 2 and 4 entries each, as specified by L$="42232232222". Each entry is terminated by CHR$ 26 (used here as a comma/delimiter sentinel — notably not a standard ASCII comma).
The encoding scheme (lines 280–310) is a character-level delta code:
- The first character of each raw entry sets a base value
C = CODE D$(1) - 27. - Each subsequent character is stored as
N = CODE D$(Z) - C, then adjusted modulo-36 via the expressionN + 36*((N<28) AND (N+C>27)) + 28*(N<0)to keep values in a printable range. - The decoded character count is stored in byte 1 of each
A$(X,Y)entry, so retrieval at line 600 usesA$(FL,M)(2 TO CODE A$(FL,M,1)).
This encoding reduces the storage footprint of the clue strings and makes casual inspection of the source listing unrevealing.
Scrolling Marquee Display
Lines 430–490 implement a simple horizontally scrolling marquee for the currently selected clue. A padding string S$ of 32 asterisks (built by doubling "*" five times at lines 120–150) and a blank string B$ of 32 null bytes (CHR$ 0, similarly constructed) are used to pad Q$ on both sides. The scroll offset NS is randomised each iteration (line 444–455): normally 1–3 characters, but after a failed guess (M=1) it ranges up to half the display width, creating a faster, more chaotic scroll. The previous line is erased by printing B$ at row 18 before the new frame.
Game Logic and Zone Keys
Ten letter keys map to memory zones via V$="ACVEFSBMPT" (line 160). Pressing one selects a random clue from that zone (M=INT(RND*VAL L$(FL)+1), line 590). Zone 11 is special: it holds the answer year in A$(11,1)(2 TO 5) and a hint phrase in A$(11,2)(2 TO 21), but has no key assigned in V$ — it is only accessed internally.
| Key | V$ index | Clue count |
|---|---|---|
| A | 1 | 4 |
| C | 2 | 2 |
| V | 3 | 2 |
| E | 4 | 3 |
| F | 5 | 2 |
| S | 6 | 2 |
| B | 7 | 3 |
| M | 8 | 2 |
| P | 9 | 2 |
| T | 10 | 2 |
Word-Wrap Subroutine
The subroutine at line 1000 splits long strings stored in R$ at CHR$ 0 boundaries (not spaces). It searches backward from position WL+1 for a null byte, prints the segment before it, then recurses via GOTO 1000 with the remainder. This means the author must manually insert null bytes into message strings where line breaks are desired — a deliberate authoring convention rather than automatic word wrapping.
Help Gate and Guess Flow
The help key H is only advertised and active after the player has made at least one failed guess (GW=1, set at line 720). The help screen (lines 760–790) reveals the string stored in zone 11, entry 2 — a contextual hint. After either a failed guess or viewing help, Q$ and M are reset (lines 800–810) and the game loops back to the title.
Notable Techniques and Observations
- The display width constant
WL=32(line 50) is used throughout, making the layout logic easy to retarget to a different line length. E$=CHR$ 11(line 90) is used in the instructions string as an inline formatting code, suggesting the display routine may interpret it, though no explicit handler is visible — it may simply render as a control character or be incidental.- The
FAST/SLOWpair (lines 10, 350) confines the slow decompression loop to FAST mode, then returns to SLOW for display. - The
SAVE "1003%2"at line 3000 uses an inverse-2 character in the filename, which on ZX81/TS1000 acts as an auto-run flag causing the program to execute immediately onLOAD. - The sentinel value
CHR$ 26inT$(checked at line 250) is unusual — this is not a printable character, so it survives in the string literal without visual ambiguity but could be fragile if the listing is re-entered manually.
Content
Source Code
5 REM %B%R%A%I%N% %T%E%R%R%A%I%N
10 FAST
20 RAND
30 DIM A$(11,4,22)
40 DIM N(11)
50 LET WL=32
60 LET GW=0
70 LET M=0
80 LET Q$=""
90 LET E$=CHR$ 11
100 LET B$=CHR$ 0
110 LET S$="*"
120 FOR X=1 TO 5
130 LET B$=B$+B$
140 LET S$=S$+S$
150 NEXT X
160 LET V$="ACVEFSBMPT"
170 LET L$="42232232222"
180 LET T$="A34L4TN,B4036NNUZS,HXSAB>BS3205Y,J32705Y,R72565,C2VOX$564V2R5,9XSQR 3,I51AA7A,R206,Q2FI54FD,50XX0ZGZOUT,PEHOD64,VSEJPANVNAAJ,WPTB7Q,I516C,F774SO(XA39Y3W,TA4LAFCHA,4MFGNY,EUO3P8X2V,FA5(Q3T(T4C3,HT6C55UO5Y,V0IEHEJC,HAEOAA>UZWWAW,MYE5FG8L,02A7A,D;$$$:6O0ZZ:67S3:$$$;,"
190 LET P=1
200 FOR X=1 TO 11
210 FOR Y=1 TO VAL L$(X)
220 LET D$=""
230 LET D$=D$+T$(P)
240 LET P=P+1
250 IF T$(P)<>CHR$ 26 THEN GOTO 230
260 LET P=P+1
270 LET A$(X,Y)=CHR$ (LEN D$)
280 LET C=CODE D$(1)-27
290 FOR Z=2 TO LEN D$
300 LET N=CODE D$(Z)-C
310 LET A$(X,Y)(Z)=CHR$ (N+36*((N<28) AND (N+C>27))+28*(N<0))
320 NEXT Z
330 NEXT Y
340 NEXT X
350 SLOW
360 LET R$="THOUGHT RECORDER, MODEL XIV (PATENT PENDING)"
370 GOSUB 1000
380 PRINT
390 LET R$="PRESS A ZONE KEY(A, B, C, E, F, M, P, S, T, OR V)TO ACCESS MEMORY"
400 IF GW=1 THEN LET R$=R$+", "+E$+"H"+E$+" FOR HELP,"
410 LET R$=R$+" OR "+E$+"G"+E$+" TO GUESS."
420 GOSUB 1000
430 LET TB=INT ((WL-LEN Q$)/2)
440 LET NS=INT (RND*3+1)
450 IF M=1 THEN LET NS=INT (RND*TB+1)
460 LET N$=S$( TO NS)+Q$+S$( TO NS)
470 PRINT AT 18,0;B$;
480 PRINT AT 18,0;
490 GOSUB 2000
500 LET K$=INKEY$
510 IF K$="" THEN GOTO 440
520 IF K$="G" THEN GOTO 620
530 IF K$="H" AND GW=1 THEN GOTO 760
540 LET FL=0
550 FOR X=1 TO 10
560 IF V$(X)=K$ THEN LET FL=X
570 NEXT X
580 IF FL=0 THEN GOTO 440
590 LET M=INT (RND*VAL L$(FL)+1)
600 LET Q$=A$(FL,M)(2 TO CODE A$(FL,M,1))
610 GOTO 430
620 CLS
630 LET N$="SPACE/TIME MACHINE ACTIVE."
640 GOSUB 2000
650 PRINT AT 4,0;"INPUT YEAR."
660 INPUT Y$
670 IF Y$<>A$(11,1)(2 TO 5) THEN GOTO 720
680 CLS
690 PRINT AT 10,11;"WHOOOOSH."
700 PRINT AT 13,1;"SUBJECT RETURNED HOME SAFELY."
710 STOP
720 LET GW=1
730 CLS
740 PRINT AT 6,1;"FAILURE. INCORRECT TIME FRAME."
750 GOTO 800
760 CLS
770 LET R$="THE LAST WORDS MR.WINKLER HEARD WERE"
780 GOSUB 1000
790 PRINT AT 3,6;A$(11,2)(2 TO 21)
800 LET Q$=""
810 LET M=0
820 PRINT AT 20,2;"(PRESS ANY KEY TO CONTINUE.)"
830 LET K$=INKEY$
840 IF K$="" THEN GOTO 830
850 CLS
860 GOTO 360
\n1000 IF LEN R$>WL THEN GOTO 1040
\n1010 LET N$=R$
\n1020 GOSUB 2000
\n1030 RETURN
\n1040 LET J=WL+1
\n1050 FOR I=WL+1 TO 1 STEP -1
\n1060 IF R$(I)<>CHR$ 0 THEN GOTO 1090
\n1070 LET J=I
\n1080 LET I=1
\n1090 NEXT I
\n1100 LET N$=R$( TO J-1)
\n1110 GOSUB 2000
\n1120 LET R$=R$(J+1 TO LEN R$)
\n1130 GOTO 1000
\n2000 PRINT TAB (WL-LEN N$)/2;N$;
\n2010 IF LEN N$<WL THEN PRINT
\n2020 RETURN
\n3000 SAVE "1003%2"
\n4000 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

