--- title: "Android Nim" id: 71157 type: "computer_media" slug: "android-nim" url: "http://localhost/computer_media/android-nim/" markdown_url: "http://localhost/computer_media/android-nim.md" published_at: "2026-08-31T07:41:36+00:00" modified_at: "2026-08-31T07:41:36+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/android-nim.png" alt: "Android Nim screen" excerpt: "A strategic Nim game pitting you against an AI opponent that uses binary decomposition to calculate winning moves, with animated UDG android figures on a colorful board." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 2068" slug: "ts2068" taxonomy: "post_tag" url: "http://localhost/tag/ts2068/" model: - name: "Timex/Sinclair 2068" slug: "ts-2068" taxonomy: "model" url: "http://localhost/model/ts-2068/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Android%20Nim%20%28198x%29%28-%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/08/android-nim.png" alt: "Android Nim screen" - url: "http://localhost/wp-content/uploads/2026/08/android-nim-2.png" alt: "Android Nim screen" media_type_tags: "Game" --- # Android Nim 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 | Variable | Role | | --- | --- | | `a(6)` | Array of current android counts per row | | `a` | Selected row (player/computer move); also used as temp accumulator | | `b` | Number of androids to remove | | `b(6,3)` | Binary digit matrix for Nim evaluation | | `c(3)` | Column parity bits for Nim evaluation | | `c` | Sum of column parities (0 = safe position) | | `d, e` | Hypothetical 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 | | `s` | Random INK color offset (0–2) for android coloring | ## 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 ```