“Santa’s Dilemma” is a Christmas-themed herding game in which the player uses the cursor keys (5–8) to guide Santa around a screen full of randomly placed trees and bushes, nudging eight reindeer into a pen before a timer runs out. The program defines four UDG characters (slots a–d) loaded from DATA statements to represent a tree top, tree trunk, a bush/reindeer figure, and Santa himself. Screen attributes are used heavily for collision detection: ATTR values of 56 (plain paper 7/ink 0) indicate open ground, while 57 (ink 1) identifies a reindeer and 60 (ink 4) marks tree cells. A built-in music routine at line 600 plays a fragment of “We Wish You a Merry Christmas” using BEEP with rests encoded as data value 99 triggering PAUSE instead of a note. The score is derived from the system frames counter at addresses 23672–23673, converting elapsed time into a “presents” figure that starts at 65000 and counts down.
Program Analysis
Program Structure
The program is organized into clearly delineated blocks separated by REM statements:
- Lines 60–120: UDG initialization — four characters loaded from DATA.
- Lines 450–690: Subroutine block — tree printer (510), keypress wait (530–570), and carol player (600–690).
- Lines 1000–1290: Title screen and two pages of instructions.
- Lines 1300–1590: Variable initialization and screen setup.
- Lines 1600–2070: Main game loop — timer display, Santa movement, reindeer herding, win/lose logic.
- Lines 3000–3080: Keyboard-reading subroutine, mapping key codes to direction index
z. - Lines 5000–5010: Direction and reindeer offset DATA.
Execution begins at line 10 via the LINE 10 parameter in the SAVE command, and line 450 immediately redirects to the title sequence at 1000 with GO TO 1000, bypassing the subroutine block.
UDG Characters
Four UDGs are defined at lines 60–120, occupying slots a–d (characters 144–147):
| UDG | Use |
|---|---|
\a | Tree top (printed in INK 4, cyan) |
\b | Tree trunk (printed in INK 3, yellow) |
\c | Reindeer / bush figure (INK 1, blue) |
\d | Santa (INK 2, red) |
Only 32 bytes (4 × 8) are poked, covering exactly four UDG slots starting at USR "a".
Collision Detection via ATTR
Rather than maintaining a separate map array, the game reads screen attribute bytes directly using the ATTR(y,x) function. The color combinations used as tokens are:
56— PAPER 7, INK 0 (white background, black ink): passable empty ground.57— PAPER 7, INK 1 (white background, blue ink): a reindeer cell.60— PAPER 7, INK 4 (white background, cyan ink): a tree top cell, used to prevent stacking trees at line 1030/1500.
Santa can only move to cells with ATTR 56 (line 1690), and a reindeer is only shoved if the cell it would move into also has ATTR 56 (line 1780). This means visual appearance and game logic are tightly coupled to the color scheme chosen.
Timer and Scoring
The game uses the system frames counter held in addresses 23672–23673 as a real-time clock. At line 1610, b = PEEK 23672 + 256*PEEK 23673 gives elapsed frames, and the score p = 65000 - b counts down from 65000. If p exceeds 60000 it is capped there (line 1620), preventing an artificially high score if the game is reached very quickly. The game-over condition triggers when p drops below 30000 (line 1640), which at 50 frames per second corresponds to roughly 700 seconds of play. The POKE at line 1590 resets the frame counter to zero at the start of each game.
Direction and Movement Data
Line 5000 stores four direction vectors for Santa (up, down, left, right as row/column deltas). Line 5010 stores eight offset pairs used to check all eight surrounding cells for reindeer. The subroutine at line 3000 maps raw key codes to a direction index z (1–4):
- Keys 5–8 (codes 53–56): mapped by subtracting 52, yielding
z= 1–4. - Cursor keys (codes 8–11, i.e., left/right/down/up arrow CHR$ codes): explicitly mapped to
z= 1–4. - Any other key sets
z=0, causing the loop to skip movement.
Reindeer AI
The “AI” is purely reactive: for each of the eight surrounding cells, if a reindeer occupies it (ATTR 57) and the cell directly beyond it in the same direction is empty (ATTR 56), the reindeer is moved one step away from Santa. This creates a simple repulsion effect. The eight offset vectors stored in m() (4 entries) and r() (8 entries) are loaded from DATA at lines 5000–5010 using RESTORE 5000 at line 1320. Note that DIM m(4,2) at line 1320 is read from only four pairs (for Santa movement), while DIM r(8,2) at line 1340 uses eight pairs for reindeer detection — both sourced from the same DATA block.
Carol Music Routine
The subroutine at lines 600–690 plays “We Wish You a Merry Christmas” using BEEP duration,pitch pairs read from DATA. A sentinel value of 99 in the duration field triggers a PAUSE b (using the pitch field as the pause duration) instead of a BEEP, implementing rests without a separate data format. The FOR i=1 TO 29 loop at line 620 counts data pairs rather than notes, so rests consume loop iterations alongside real notes; this means the loop count must be set carefully to match the DATA length.
Pen Gate and Win Condition
The pen is drawn on the left side of the screen (column 5, rows 7–13) using INVERSE characters, with a gap at row 10 (line 1440) acting as the entrance. The game detects a reindeer entering the pen by checking ATTR(10,5) = 57 at line 1810 — i.e., whether a reindeer (blue ink) occupies the gate cell. Each capture increments tr, erases that cell, and prints a reindeer icon in the pen interior (line 1870: AT 6+tr,0). When tr reaches 6 the win sequence fires. A subtle limitation is that only one reindeer can be counted per main loop iteration because only one gate cell is polled.
Keypress-Wait Idiom
Lines 550–560 implement a clean edge-triggered key wait: line 550 loops while INKEY$ is non-empty (flushing any held key), then line 560 loops while it is empty (waiting for a fresh press). This avoids the common problem of a held key from a previous screen immediately advancing the next screen.
Bugs and Anomalies
- Line 1700 checks bounds
y<0 OR y>21 OR x<0 OR x>31after the ATTR call at line 1690, so if the computed coordinates are off-screen, the ATTR call at 1690 executes first and may return an unexpected value rather than causing an error — this ordering could be reversed for robustness. - The
DIM r(8,2)array is filled with eight pairs from DATA at line 5010, but the DATA at 5000 already supplies four pairs; the RESTORE at 1320 resets the pointer to 5000, so both arrays are filled sequentially from the same stream — this works correctly but ties the two arrays’ DATA together implicitly. - At line 1030/1500, trees are prevented from overlapping by checking ATTR of
y+1,x(the trunk row) for value 60 (cyan/tree-top color), but the trunk itself uses INK 3 (yellow). This means the check detects the top of a previously placed tree at the candidate trunk position, not a trunk-on-trunk collision — an effective heuristic but not a fully rigorous overlap test. - Line 1940 contains the word “alot” (should be “a lot”) — a typographical error in the original listing.
Content
Source Code
10 REM "Santa's Dilemma"
11 REM Spectrum Program from ZX Computing December 1986
50 REM UDGS
60 LET a=USR "a"
70 FOR i=0 TO 31: READ q
80 POKE a+i,q: NEXT i
90 DATA 24,60,24,126,60,255,126,255
100 DATA 24,24,24,24,24,24,60,60
110 DATA 10,10,14,142,124,124,68,68
120 DATA 24,24,60,90,189,60,126,126
130 BORDER 4: PAPER 7: INK 0: CLS
450 GO TO 1000
500 REM SUBROUTINE
510 REM 1. Print trees
520 PRINT INK 4;AT y,x;"\a"; INK 3;AT y+1,x;"\b": RETURN
530 REM Press a key
540 PRINT AT 21,1; INVERSE 1;" Press any key to continue"
550 IF INKEY$<>"" THEN GO TO 550
560 IF INKEY$="" THEN GO TO 560
570 LET z$=INKEY$: RETURN
600 REM Merry Christmas
610 RESTORE 650
620 FOR i=1 TO 29
630 READ a: READ b: IF a=99 THEN PAUSE b: GO TO 630
640 BEEP a,b: NEXT i
650 DATA .5,0,.25,5,99,10,.25,5,.25,7,.25,5,.25,4,.5,2,99,2,.5,2
660 DATA 99,5,.5,2,.25,7,99,10,.25,7,.25,9,.25,7,.25,5,.25,4,99,10,.25,4,99,10
670 DATA .5,4,.25,9,99,10,.25,9,.25,10,.25,9,.25,7,.5,5,.5,2
680 DATA .5,0,.5,2,.5,7,.5,4,.5,5
690 RETURN
1000 REM Print Title
1010 FOR i=1 TO 50
1020 LET y=RND*20: LET x=RND*31
1030 IF ATTR (y+1,x)=60 THEN GO TO 1020
1040 GO SUB 510: NEXT i
1050 PAUSE 100
1060 PRINT AT 5,12; INK 6; PAPER 1;"SANTA'S"
1070 PAUSE 50
1080 PRINT AT 8,11; FLASH 1;" DILEMMA "
1090 PAUSE 100: GO SUB 600
1100 PAUSE 100: BORDER 4: CLS
1110 PRINT AT 2,9; PAPER 1; INK 6;"Santa's Dilemma"'''
1120 PRINT " It's Christmas Eve, and Santa wants to get under way to "
1130 PRINT "deliver sackfulls of super microgames to all those lucky girls"
1140 PRINT "and boys whose parents have bought them Spectrums, QLs,"
1150 PRINT "and other micros for Christmas."
1160 PRINT "But someone has left his gates open, and all the reindeer have"
1170 PRINT "got out. You'll have to guide Santa to help him get 6 reindeer";
1180 PRINT " back into the pen at the left of the Screen."
1190 GO SUB 530: CLS
1200 PRINT AT 2,9; PAPER 1; INK 6;" Santa's Dilemma "'''
1210 PRINT " You guide Santa by using the cursor keys (5-8). The Reindeer"
1220 PRINT "will move away from Santa when he stands next to them."''
1230 PRINT " You have to get 6 reindeer into the pen as fast as possible. The";
1240 PRINT " number at the bottom of the Screen is the number of presents";
1250 PRINT " Santa has time to deliver. As you want to disappoint as few"
1260 PRINT "children as possible, aim to drive the Reindeer into the pen"
1270 PRINT "as quickly as you can."''
1280 PRINT " Good Luck"
1290 GO SUB 530
1300 REM Initialise Variables
1310 LET sany=8: LET sanx=2
1320 RESTORE 5000: DIM m(4,2)
1330 FOR i=1 TO 4: READ m(i,1): READ m(i,2): NEXT i
1340 DIM r(8,2)
1350 FOR i=1 TO 8: READ r(i,1): READ r(i,2): NEXT i
1360 LET tr=0
1400 REM Set up Screen
1410 BORDER 4: INK 0: PAPER 7: CLS
1420 INK 3: PRINT AT 6,0;"\..\..\..\..\..\.."
1430 PRINT AT 14,0; INVERSE 1;"\..\..\..\..\..\.."
1440 FOR i=7 TO 13: IF i=10 THEN GO TO 1460
1450 PRINT AT i,5; INVERSE 1;"\: "
1460 NEXT i: INK 0
1470 FOR i=1 TO 50
1480 LET y=RND*20: LET x=RND*31
1490 IF x<10 AND (y>3 AND y<16) THEN GO TO 1480
1500 IF ATTR (y+1,x)=60 THEN GO TO 1480
1510 GO SUB 510: NEXT i
1520 FOR i=1 TO 10
1530 LET y=1+RND*20: LET x=1+RND*29
1540 IF x<10 AND (y>3 AND y<16) THEN GO TO 1530
1550 IF ATTR (y,x)<>56 THEN GO TO 1530
1560 PRINT AT y,x; INK 1;"\c"
1570 NEXT i
1580 PRINT AT 8,2; INK 2;"\d"
1590 POKE 23672,0: POKE 23673,0
1600 REM Go Get Them
1610 LET b=PEEK 23672+256*PEEK 23673: LET p=65000-b
1620 IF p>60000 THEN LET p=60000
1630 PRINT #1;AT 0,0;"Presents: ";p;" "
1640 IF p<30000 THEN GO TO 2000
1650 REM Move Santa
1660 GO SUB 3000: IF z=0 THEN GO TO 1600
1680 LET y=sany+m(z,1): LET x=sanx+m(z,2)
1690 IF ATTR (y,x)<>56 THEN GO TO 1600
1700 IF y<0 OR y>21 OR x<0 OR x>31 THEN GO TO 1600
1710 PRINT AT sany,sanx;" "
1720 LET sany=y: LET sanx=x
1730 PRINT AT y,x; INK 2;"\d": BEEP .01,-5
1740 REM Shoo Reindeer
1750 FOR i=1 TO 8
1760 LET y=sany+r(i,1): LET x=sanx+r(i,2)
1770 IF ATTR (y,x)<>57 THEN GO TO 1800
1775 IF y<1 OR y>20 OR x<1 OR x>30 THEN GO TO 1800
1780 IF ATTR (y+r(i,1),x+r(i,2))<>56 THEN GO TO 1800
1790 PRINT AT y,x;" ";AT y+r(i,1),x+r(i,2); INK 1;"\c": BEEP .01,5
1800 NEXT i
1810 IF ATTR (10,5)<>57 THEN GO TO 1600
1820 LET tr=tr+1
1830 FOR i=1 TO 5
1840 BEEP .15,-5: BEEP .15,0: BEEP .15,5
1850 NEXT i
1860 PRINT AT 10,5;" "
1870 PRINT AT 6+tr,0; INK 1;"\c"
1880 IF tr<6 THEN GO TO 1600
1890 PAUSE 100: GO SUB 600: PAUSE 200
1900 BORDER 4: CLS
1910 PRINT AT 4,8;"Santa can deliver ";AT 6,9;p;" presents"
1920 PRINT AT 10,0;
1930 IF p<40000 THEN PRINT "You've disappointed many children": GO TO 1980
1940 IF p<55000 THEN PRINT "Not too bad, but Santa will have to miss quite alot of children.": GO TO 1980
1950 PRINT "Well done; there won't be too many disappointments tonight."
1980 PAUSE 100: GO SUB 600
1990 GO TO 2060
2000 FOR i=20 TO -10 STEP -.5
2010 BEEP .05,i: NEXT i
2020 CLS
2030 PRINT AT 4,0;"Oh dear! You've failed to catch enough reindeer in time."
2040 PRINT AT 9,0;"How can you ever forgive yourself for the broken hearts"
2050 PRINT "you have caused!"
2060 PAUSE 500: RUN
2070 STOP
3000 LET z$=INKEY$: LET z=0
3010 IF z$="" THEN RETURN
3020 LET z=CODE z$
3030 IF z>51 AND z<57 THEN LET z=z-52: RETURN
3040 IF z=8 THEN LET z=1: RETURN
3050 IF z=9 THEN LET z=4: RETURN
3060 IF z=10 THEN LET z=2: RETURN
3070 IF z=11 THEN LET z=3: RETURN
3080 LET z=0: RETURN
5000 DATA 0,-1,1,0,-1,0,0,1
5010 DATA -1,0,-1,-1,1,0,1,1,-1,1,1,-1,0,1,0,-1
9999 SAVE "SANTA DIL" LINE 10
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

