--- title: "Pangolins" id: 55862 type: "computer_media" slug: "pangolins" url: "http://localhost/computer_media/pangolins/" markdown_url: "http://localhost/computer_media/pangolins.md" published_at: "2024-07-06T17:27:01+00:00" modified_at: "2026-03-30T21:44:37+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/07/SCR-20260329-ofde.png" excerpt: "A self-learning animal guessing game that grows its own binary decision tree at runtime — teach it new creatures one wrong guess at a time." 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_contents: - id: 55879 title: "ISTUG Public Domain Library 7" type: "computer_media" url: "http://localhost/computer_media/istug-public-domain-library-7/" media_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/Pangolins%20%28198x%29%28TS2068%29%28US%29%28Program%29.zip" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/07/SCR-20260329-ofde.png" media_type_tags: "Game" --- # Pangolins This program implements a classic animal-guessing game using a binary decision tree stored in parallel arrays. The tree is represented by a string array `q$` holding questions and animal names, and a two-dimensional integer array `a` mapping each question node to its yes/no child indices. When the program fails to guess the player’s animal, it learns by prompting for the new animal’s name and a distinguishing question, then dynamically inserts both into the arrays and updates the branching pointers. The initial tree is seeded from DATA statements covering four animals (whale, blancmange, pangolin, ant) and three questions, with the tree pre-wired so nodes 1–3 are questions and nodes 4–7 are animals. A subroutine at line 910 trims trailing spaces before printing fixed-length strings from the DIM’d array. *** ## Program Analysis ### Program Structure The program is organized into clearly delineated functional blocks, each introduced by a `REM` comment: 1. **Lines 5–70:** Initialization — dimensions arrays, loads question/answer tree from `DATA` statements. 2. **Lines 100–210:** Main game loop — traverses the decision tree from root node `c=1` until a leaf (animal) node is reached. 3. **Lines 300–380:** Animal guessing — presents the guessed animal name and reads a yes/no response. 4. **Lines 400–410:** Success path — congratulates itself and jumps to the replay prompt. 5. **Lines 500–730:** Learning path — expands the tree with a new animal and distinguishing question. 6. **Lines 800–840:** Replay prompt — asks for another game or halts. 7. **Lines 900–940:** Print subroutine — strips trailing spaces from fixed-length strings. 8. **Lines 1000–1020:** Input subroutine — reads a reply and truncates to the first character. 9. **Lines 2000–2040:**`DATA` — seeds the initial four-animal, three-question tree. ### Data Structures The decision tree is encoded in two parallel structures: - `q$(nq,50)` — a 100×50 character string array holding both question text (internal nodes) and animal names (leaf nodes). - `a(nq,2)` — a 100×2 numeric array where `a(c,1)` is the child index for a “yes” answer and `a(c,2)` is the child index for a “no” answer. A value of `0` in both slots signals a leaf node. The variable `qf` (next free slot) starts at 8. Questions occupy indices 1–3 initially, animals occupy indices 4–7. New animals are appended in pairs (old animal moved to `qf`, new animal at `qf+1`), and `qf` advances by 2 after each learning event. ### Tree Traversal Traversal begins at line 130 with `c=1`. At each step, the program checks `a(c,1)=0` (line 140) to detect a leaf node. For internal nodes, the player’s yes/no response sets `in` to 1 or 2 respectively, and line 210 follows the branch: `LET c=a(c,in)`. This is a clean, minimal binary tree walk entirely in BASIC. ### Learning Mechanism When the program guesses incorrectly (lines 500–720), it performs the following steps: 1. Moves the previously guessed animal’s name string from slot `c` to slot `qf`. 2. Reads the new animal’s name into slot `qf+1`. 3. Reads a distinguishing question into slot `c`, replacing the old animal name and turning the leaf into an internal node. 4. Sets `a(c,in)` to point to the new animal and `a(c,io)` to point to the old animal, based on the player’s answer. 5. Increments `qf` by 2. The old animal at index `c` still has its `a(c,1)` and `a(c,2)` both zero (from the initial `DIM`), so the newly added leaf nodes are automatically recognized as leaves without needing explicit initialization. ### Input Handling The subroutine at line 1000 uses `INPUT r$` and, if the string is non-empty, truncates it to its first character via `r$=r$(1)`. This single-character capture is then compared case-insensitively by testing both `"y"`/`"Y"` and `"n"`/`"N"` at each decision point. The `POKE 23658,8` at line 7 enables caps-lock mode, making lowercase input less likely, though the code still guards against it. ### Print Subroutine Because `DIM q$(nq,50)` pads all strings to exactly 50 characters with spaces, the subroutine at line 910 scans backwards from position 50 to find the last non-space character (`n`) and then prints `p$(TO n)`. Line 905 prefixes a leading space before this trimmed output, while the entry point at line 910 skips the leading space — the two entry points (`GO SUB 900` and `GO SUB 910`) are used in different contexts throughout the program. ### Notable Techniques and Idioms - The `DATA` loader at lines 30–70 uses two separate `FOR` loops: the first (lines 30–50) reads question strings with their two child indices; the second (lines 60–70) reads the initial animal name strings, which have no associated `a()` data since their child slots default to zero from `DIM`. - `qf/2-1` at line 30 computes the count of initial questions (3) from the initial free pointer (8), keeping the data count implicit rather than hardcoded separately. - Line 580 strips a trailing `?` from the player-entered distinguishing question before storing it, since the game appends its own `?` on display. - The replay check at line 830 uses a double conditional idiom: `IF r$="Y" THEN CLS : IF r$="Y" THEN GO TO 100` — the condition is tested twice to allow `CLS` to execute only on the affirmative path before the `GO TO`. ### Bugs and Anomalies - Line 540 contains a spelling error: `"DISTINGUEISHES"` should be `"DISTINGUISHES"`. - Line 630 initializes `in=1` (yes → new animal) and `io=2` (no → old animal) before reading the response, but lines 650–680 only handle `"Y"` and `"N"` (uppercase). If the player types lowercase `"y"` or `"n"`, neither branch is taken, and line 690 fires with “THAT’S NO GOOD.” — inconsistent with the rest of the program which accepts both cases. - Line 830’s replay check only tests for uppercase `"Y"`; a lowercase `"y"` will fall through to `STOP`. - The `SAVE "pangolins" LINE 0` at line 3000 targets line 0, which does not exist; the conventional auto-run target would be line 5 or 7. This may prevent auto-run on load, though the save itself will succeed. ## Source Code ``` 5 REM pangolins 7 CLS : POKE 23658,8 10 LET nq=100: REM number of questions and animals 15 DIM q$(nq,50): DIM a(nq,2): DIM r$(1) 20 LET qf=8 30 FOR n=1 TO qf/2-1 40 READ q$(n): READ a(n,1): READ a(n,2) 50 NEXT n 60 FOR n=n TO qf-1 70 READ q$(n): NEXT n 100 REM start playing 110 PRINT "THINK OF AN ANIMAL"';#1;"Press any key to continue" 120 PAUSE 0 130 LET c=1: REM start with first question 140 IF a(c,1)=0 THEN GO TO 300 150 LET p$=q$(c): GO SUB 910 160 PRINT "?": GO SUB 1000 170 LET in=1: IF r$="y" THEN GO TO 210 180 IF r$="Y" THEN GO TO 210 190 LET in=2: IF r$="n" THEN GO TO 210 200 IF r$<>"N" THEN GO TO 150 210 LET c=a(c,in): GO TO 140 300 REM animal 310 PRINT "ARE YOU THINKING OF" 320 LET P$=Q$(C): GO SUB 900: PRINT "?"' 330 GO SUB 1000 340 IF r$="y" THEN GO TO 400 350 IF r$="Y" THEN GO TO 400 360 IF r$="n" THEN GO TO 500 370 IF r$="N" THEN GO TO 500 380 PRINT "ANSWER ME PROPERLY WHEN I'M","TALKING TO YOU.": GO TO 300 400 REM guessed it 410 PRINT "I THOUGHT AS MUCH.": GO TO 800 500 REM new animal 510 IF qf>nq-1 THEN PRINT "I'M SURE YOUR ANIMAL IS VERY","INTERESTING, BUT I DON'T HAVE","ROOM FOR IT JUST NOW.": GO TO 800 520 LET q$(qf)=q$(c): REM move oldanimal 530 PRINT '"WHAT IS IT THEN?": INPUT q$(qf+1) 540 PRINT '"TELL ME A QUESTION WHICH","DISTINGUEISHES BETWEEN " 550 LET p$=q$(qf): GO SUB 900: PRINT " AND" 560 LET p$=q$(qf+1): GO SUB 900: PRINT "" 570 INPUT s$: LET b=LEN s$ 580 IF s$(b)="?" THEN LET b=b-1 590 LET q$(c)=s$( TO b): REM insert question 600 PRINT '"WHAT IS THE ANSWER FOR" 610 LET p$=q$(qf+1): GO SUB 900: PRINT "?" 620 GO SUB 1000 630 LET in=1: LET io=2: REM answers for old and new animals 650 IF r$="Y" THEN GO TO 700 660 LET in=2: LET io=1 680 IF r$="N" THEN GO TO 700 690 PRINT "THAT'S NO GOOD.": GO TO 600 700 REM update answers 710 LET a(c,in)=qf+1: LET a(c,io)=qf 720 LET qf=qf+2: REM next free animal space 730 PRINT "THAT FOOLED ME." 800 REM again? 810 PRINT '"Do you want another go?": GO SUB 1000 830 IF r$="Y" THEN CLS : IF r$="Y" THEN GO TO 100 840 STOP 900 REM print without trailing spaces 905 PRINT " "; 910 FOR n=50 TO 1 STEP -1 920 IF p$(n)<>" " THEN GO TO 940 930 NEXT n 940 PRINT p$( TO n);: RETURN 1000 REM get reply 1010 INPUT r$: IF r$="" THEN RETURN 1020 LET r$=r$(1): RETURN 2000 REM initial animals 2010 DATA "DOES IT LIVE IN THE SEA",4,2 2020 DATA "IS IT SCALY",3,5 2030 DATA "DOES IT EAT ANTS",6,7 2040 DATA "A WHALE","A BLANCMANGE","A PANGOLIN","AN ANT" 3000 SAVE "pangolins" LINE 0 ```