Android Nim

Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Game

Android Nim is a two-player strategy game where the human competes against the computer to remove “android” figures from one of six rows, with the player removing the last android losing (or winning, depending on interpretation). The computer opponent uses a Nim-value algorithm based on binary decomposition: each row’s count is broken into base-2 digits across a 6×3 matrix, and a position is deemed “safe” (winning for the side that just moved) when all column sums are even. Custom UDG characters defined via DATA statements and POKE USR represent the android figures, with four two-character UDG pairs forming the body and three pairs for the legs. The animation subroutines cycle through UDG variants and use BEEP calls to produce sound effects when androids are highlighted or removed.


Program Structure

The program is organized into a main game loop and a set of clearly delineated subroutines. Execution begins at line 10, which initializes the UDG characters and displays the title/explanation screen before entering the game proper.

  1. Lines 10–20: Initialization — RANDOMIZE, call explanation page (line 160), call UDG setup (line 210), then call board setup (line 380).
  2. Lines 30–90: Coin-toss to determine who starts; computer move logic.
  3. Lines 100–150: Human move input and validation loop.
  4. Lines 160–200: Explanation/title display subroutine; also initializes p$.
  5. Lines 210–370: UDG DATA and POKE loop; DIM and assignment of UDG string arrays.
  6. Lines 380–400: Board setup subroutine — randomizes row lengths, draws initial androids.
  7. Lines 410–460: Nim-value (safe/unsafe position) calculation subroutine.
  8. Lines 470–520: Android animation subroutine — animates figures to “look” at the chosen row.
  9. Lines 530–570: Android removal subroutine with animated erasure and sound.
  10. Line 580: SAVE with autostart.

UDG Character Design

Lines 210–350 define 14 UDGs (\a through \n) using 8-byte DATA blocks, POKEd into UDG RAM via POKE USR "\a"+N. The 112 bytes cover 14 characters (14×8=112). These are organized as four two-character “body” pairs (a$(1)a$(4)) representing different android poses, and three two-character “leg” pairs (b$(1)b$(3)). Note the DATA at lines 210–340 contains the letter O (or o) instead of the digit 0 in several places — these will be read as 0 by READ/DATA on this platform but represent a data-entry anomaly.

Nim Strategy Algorithm

The computer’s winning strategy is implemented in the subroutine at lines 410–460. It performs a standard binary (base-2) Nim analysis:

  • Each row’s count is decomposed into its base-2 digits (ones, twos, fours) stored in array b(n,1..3).
  • For each binary column, the subroutine counts how many rows have a 1-bit in that position and stores whether that sum is odd in c(n), using the idiom c(n)=(c(n)<>b(m,n)) which toggles a boolean.
  • c is set to the sum c(1)+c(2)+c(3); a value of 0 means the position is “safe” (the previous player is in a winning position in standard Nim theory).
  • The computer first tries a random row (line 50); if that doesn’t yield a safe position, it systematically searches all rows and removal counts (lines 70–80) until it finds a move that makes c=0.

The subroutine accepts a hypothetical move via parameters d (row) and e (new count after removal), substituting them into the position before evaluation, so no actual board modification is needed during search.

Animation Subroutines

The subroutine at lines 470–520 animates androids to “face” the selected row. It distinguishes three zones: rows above the target (lines 490), the target row itself (line 500), and rows below (line 510). Each zone cycles through different UDG pose indices (q) and leg pairs (p) in different orders, giving a directional appearance. BEEP calls are woven into the animation loops with pitch values computed from loop variables, producing a cascading sound effect.

The removal subroutine (lines 530–570) cycles through all four body UDGs and all three leg UDGs per android being erased, accompanied by a sliding BEEP pitch, finishing with a FLASH display and a long 1-second BEEP before blanking the cell.

Key BASIC Idioms

  • p$= CHR$ 22+ CHR$ 21+ CHR$ 0 (line 200) constructs an AT 21,0 control sequence stored as a string, used throughout to reposition the cursor to the bottom of the screen for status messages.
  • PAUSE VAL "100" uses VAL on a string literal as a minor memory optimization to encode the numeric argument.
  • Input validation at lines 100 and 120 is done inline using chained IF conditions on LINE z$, checking string length and character range before converting with VAL.
  • Row totals are accumulated with LET a=0:FOR n=1 TO 6:LET a=a+a(n):NEXT n to detect end-of-game (all rows empty).

Notable Anomalies and Bugs

  • Letter O in DATA: Lines 210–340 use the letter O (or o) where the digit 0 is clearly intended in the UDG bitmap data. BASIC’s READ will accept these as 0 due to implicit VAL conversion only if the implementation is lenient; on a strict interpreter this would cause a “Invalid argument” error. This is almost certainly a transcription error.
  • Variable name collision: The program uses both an array a() (row counts) and a plain variable a (selected row / temporary accumulator) throughout. Spectrum BASIC treats these as distinct, but the dual use makes the logic harder to follow and could cause confusion if not carefully managed — the code does handle this correctly by context.
  • Computer move randomness: Line 50 picks a random row a (1–6) and checks a(a)>0 (non-empty) and NOT c (unsafe position), but the check is done with the original d=1, e=a(1) evaluation from the GO SUB at the start of line 50, not with the randomly chosen a. The random selection affects the jump to line 90 but the position evaluation is of row 1. This means the “random first try” does not correctly evaluate whether the random row leads to a safe position — the systematic search at lines 70–80 is what actually finds the correct move.
  • Android display orientation: In the board setup (line 390), androids are always drawn with a$(1) and b$(1) (the first pose), while the animation subroutine uses poses 2–4 for body and 1–3 for legs. After animation the board is not reset to pose 1, so the display remains in whichever pose the animation ended on.

Variable Summary

VariableRole
a(6)Array of current android counts per row
aSelected row (player/computer move); also used as temp accumulator
bNumber of androids to remove
b(6,3)Binary digit matrix for Nim evaluation
c(3)Column parity bits for Nim evaluation
cSum of column parities (0 = safe position)
d, eHypothetical row and count for Nim subroutine
p$AT 21,0 control string for status line positioning
a$(4,2)Two-char UDG strings for android body poses
b$(3,2)Two-char UDG strings for android leg poses
z$LINE input buffer for user responses
sRandom INK color offset (0–2) for android coloring

Content

Appears On

Related Products

Related Articles

Related Content

Image Gallery

Source Code

  10 RANDOMIZE :GO SUB 160:GO SUB 210:PRINT "Press any key to play":PAUSE 0
  20 GO SUB 380
  30 IF RND <=.5 THEN PRINT p$; PAPER 2;"\{18}\{1}         YOU TO START",\{18}\{0}:PAUSE VAL "100":GO TO 100
  40 PRINT p$; PAPER 1;"\{18}\{1}       COMPUTER TO START",\{18}\{0}:PAUSE VAL "100"
  50 PRINT p$; PAPER 1;"\{18}\{1}      COMPUTER'S MOVE ",\{18}\{0}:LET d=1:LET e=a(d):GO SUB 410:LET a=1+ INT (6* RND):IF NOT c AND a(a) THEN LET b=1:GO TO 90
  60 IF NOT c THEN GO TO 50
  70 FOR d=1 TO 6:FOR e=a(d)-1 TO 0 STEP -1:GO SUB 410:IF c THEN NEXT e:NEXT d
  80 LET a=d:LET b=a(a)-e
  90 GO SUB 470:GO SUB 530:LET a=0:FOR n=1 TO 6:LET a=a+a(n):NEXT n:IF a=0 THEN PRINT p$; PAPER 1;"\{18}\{1}    TIMEX 2068 IS THE WINNER",\{18}\{0}:INPUT "Press enter to play again"; LINE z$:GO TO 20
 100 PRINT p$; PAPER 2;"\{18}\{1}         YOUR MOVE ",\{18}\{0}:INPUT AT 0,0; INK 2; PAPER 7;"Which row will you attack ? Typea number followed by enter. "; LINE z$:IF z$ <>"" THEN IF (LEN z$=1) AND (z$>"0") AND (z$ <="6") THEN LET a= VAL z$:IF a(a)>0 THEN GO SUB 470:GO TO 120
 110 GO TO 100
 120 INPUT AT 0,0; PAPER 2;"How many will you remove ? Type a number followed by enter."; LINE z$:IF z$ <>"" THEN IF (LEN z$=1) AND (z$>"0") AND (z$ <="7") THEN LET b= VAL z$:IF b <=a(a) THEN GO TO 140
 130 GO TO 120
 140 GO SUB 530:LET a=0:FOR n=1 TO 6:LET a=a+a(n):NEXT n:IF a=0 THEN PRINT p$; PAPER 2;"\{18}\{1}YOU WIN",,\{18}\{0}:INPUT AT 0,0;"Press enter to play again"; LINE z$:GO TO 20
 150 GO TO 50
 160 REM explanation page
 170 INK 7:PAPER 0:BORDER 0:CLS :PRINT PAPER 2;"     C.D.S SOFTWARE SYSTEMS",,,"          ANDROID  NIM",,,'' PAPER 1;"   Nim is a game for two, your",,,"   partner is the TIMEX 2068.",
 180 PRINT '' PAPER 1;"   You take turns at removing",,,"any number of androids from one",,,"         of six rows",
 190 PRINT '' PAPER 6; INK 0;"   The Winner removes the last",,,"       Android",,
 200 PRINT '':LET p$= CHR$ 22+ CHR$ 21+ CHR$ 0:RETURN 
 210 DATA 7,31,21,17,O,15,7,1
 220 DATA 7,31,17,25,17,15,7,1
 230 DATA 7,31,17,19,17,15,7,1
 240 DATA 7,31,17,o,21,15,7,1
 250 DATA 224,248,168,136,o,240,224,128
 260 DATA 224,248,136,200,136,240,224,128
 270 DATA 224,248,136,152,136,240,224,128
 280 DATA 224,248,136,o,168,240,224,128
 290 DATA 31,19,31,3,o,1,o,3
 300 DATA 15,11,27,19,o,2,6,12
 310 DATA 255,131,3,o,31,16,48,0
 320 DATA 248,200,248,192,o,128,o,192
 330 DATA 240,208,216,200,o,64,96,48
 340 DATA 255,193,192,o,248,8,12,0
 350 RESTORE 210:FOR n=0 TO 111:READ o:POKE USR "\a"+N,o:NEXT n
 360 DIM a$(4,2):DIM b$(3,2):LET a$(1)="\a\e":LET a$(2)="\b\f":LET a$(3)="\c\g":LET a$(4)="\d\h":LET b$(1)="\i\l":LET b$(2)="\j\m":LET b$(3)="\k\n"
 370 RETURN 
 380 REM sub to set up board
 390 CLS :PRINT AT 0,0; PAPER 1;"          ANDROID  NIM          ":DIM a(6):FOR n=1 TO 6:LET a(n)=1+ INT (RND*7):FOR m=1 TO a(n):PRINT AT 3*n,3*m; INK 4+ INT (RND*3);a$(1); AT 3*n+1,3*m; INK 3;b$(1); AT 3*n,0; INK 7;n:NEXT m:NEXT n
 400 RETURN 
 410 REM sub to determine safe  or unsafe (c=0)
 420 DIM b(6,3)
 430 FOR n=1 TO 6:LET a=a(n):IF n=d THEN LET a=e
 440 LET b(n,3)= INT (a/4):LET a=a-4* INT (a/4):LET b(n,2)= INT (a/2):LET a=a-2* INT (a/2):LET b(n,1)= INT a:NEXT n
 450 DIM c(3):FOR n=1 TO 3:LET c(n)=0:FOR m=1 TO 6:LET c(n)=(c(n) <>b(m,n)):NEXT m:NEXT n:
 460 LET c=c(1)+c(2)+c(3):RETURN 
 470 REM sub to point  androids in right direction
 480 PRINT AT 3*a,0; FLASH 1;a
 490 FOR n=1 TO a-1:FOR m=1 TO a(n):LET s= INT (3* RND):FOR p=2 TO 1 STEP -1:FOR q=3 TO 4:PRINT AT 3*n,3*m; INK 4+s;a$(q); AT 3*n+1,3*m; INK 3;b$(p):BEEP .02,2*(q+m+n+p):NEXT q:NEXT p:NEXT m:NEXT n
 500 FOR m=1 TO a(a):LET s= INT (RND*3):FOR p=1 TO 3:FOR q=2 TO 3:PRINT AT 3*a,3*m; INK 4+s;a$(q); AT 3*a+1,3*m; INK 3;b$(p):BEEP .02,10+2*(q+p+m):NEXT q:NEXT p:NEXT m
 510 FOR n=a+1 TO 6:FOR m=1 TO a(n):LET s= INT (RND*3):FOR p=1 TO 2:FOR q=2 TO 1 STEP -1:PRINT AT 3*n,3*m; INK 4+s;a$(q); AT 3*n+1,3*m; INK 3;b$(p):BEEP .02,20+2*(q+p+m+n):NEXT q:NEXT p:NEXT m:NEXT n
 520 RETURN 
 530 REM sub to remove androids a=row, b= no. to be removed
 540 FOR n=a(a) TO a(a)-b+1 STEP -1:FOR p=2 TO 3:FOR q=1 TO 3:FOR r=1 TO 4:PRINT AT 3*a,3*n; INK 7;a$(r); AT 3*a+1,3*n; INK 2;b$(q):BEEP .1-.03*p,40+r+p+q:NEXT r:NEXT q:NEXT p:
 550 PRINT AT 3*a,3*n; INK 5; FLASH 1;a$(4); AT 3*a+1,3*n; INK 2; FLASH 1;b$(3):BEEP 1,60:PRINT AT 3*a,3*n;"  "; AT 3*a+1,3*n;"  ":NEXT n
 560 LET a(a)=a(a)-b
 570 PRINT AT 3*a,0;a:RETURN 
 580 SAVE "ANDROID NI" LINE 10

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

People

No people associated with this content.

Scroll to Top