--- title: "Sub-Niner" id: 57612 type: "computer_media" slug: "sub-niner" url: "http://localhost/computer_media/sub-niner/" markdown_url: "http://localhost/computer_media/sub-niner.md" published_at: "2024-10-06T00:27:34+00:00" modified_at: "2026-04-03T07:58:15+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/10/sub-niner-1.png" excerpt: "Can you zero out all nine digits in the fewest moves? This clever number-reduction puzzle uses a weighted random generator and a borrow-penalty mechanic to keep things tense." 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 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 56724 title: "Synchro-Sette June 1983" type: "computer_media" url: "http://localhost/computer_media/synchro-sette-june-1983/" media_type: "Program" mediadate: "June 1983" images: - url: "http://localhost/wp-content/uploads/2024/10/sub-niner-1.png" media_type_tags: "Game" --- Sub-Niner is a single-player number puzzle game where the player must reduce all nine digits of a number string to zero by repeatedly subtracting a randomly generated value. Each turn, a random number R (1–9, weighted toward smaller values via RND*RND) is displayed, and the player selects which digit position (1–9) to subtract it from; selecting position 0 skips the turn. If a digit would go below zero, a penalty subroutine adds 10 to the digit and increases the move count by R, simulating a borrow/wrap mechanic. The score is calculated as 101 minus the total number of moves taken, and a high-score table tracking the best player name and score persists across rounds within the same session. *** ## Program Analysis ### Program Structure The program is divided into clearly separated functional blocks: 1. **Lines 10–60:** Initialisation and high-score display; player name input. 2. **Lines 100–600:** Screen setup — title, playing grid, column headers, player announcement. 3. **Lines 1000–1140:** Main game loop — random number generation, digit selection, subtraction, and penalty handling. 4. **Lines 1150–1190:** End-of-game display, high-score update trigger, and loop-back to the name/high-score screen. 5. **Lines 2000–2020:** Penalty subroutine — applies a borrow (+10) and adds R to the move counter. 6. **Lines 3000–3020:** High-score update subroutine — records winning player name and score. 7. **Lines 9998–9999:** Save and auto-run block. ### Gameplay Mechanics The game string `A$` is initialised to `"999999999"` (nine nines). Each turn, a random digit R is generated and displayed. The player presses a key (1–9) to subtract R from that digit position. Pressing 0 skips the turn without consuming a move counter increment — though `M` is still incremented at line 1080 before the skip check at line 1110, meaning skipping is not truly free. The game ends when all characters in `A$` equal `"0"` (i.e., `CHR$ 28` in internal representation — see below). ### Character Code Arithmetic This is the program’s most technically interesting idiom. Digits are stored as their Sinclair character codes (e.g., `"9"` = `CHR$ 57` on Spectrum, but on ZX81/TS1000, digit ‘0’ = `CHR$ 28`, ‘1’ = `CHR$ 29`, …, ‘9’ = `CHR$ 37`). The variable `A` is computed as `CODE A$(P) - R`. The zero-check at line 1070 tests `A$="000000000"`, meaning each character must be `CHR$ 28`. The INKEY$ handling at line 1090 uses `CODE INKEY$ - 28` to extract the numeric value of the pressed digit key directly from its character code, confirming ZX81/TS1000 character encoding is in use. ### Penalty / Borrow Subroutine Lines 2000–2020 handle the case where subtracting R from a digit would take it below `CHR$ 28` (i.e., below zero). The fix adds 10 to the character code (`CODE A$(P) + 10 - R`), wrapping the digit around, and penalises the player by adding R to the move count `M`. This subroutine is called both before the player’s choice is applied (line 1075, checking if `A` — still holding the last result — is under 28) and after computing the new value (line 1125). ### Weighted Random Number Generation Line 1050 uses `INT (1 + RND*RND*9)` rather than the simpler `INT (1 + RND*9)`. Multiplying two independent uniform random values produces a triangular distribution skewed toward lower numbers, meaning small values of R appear more frequently. This subtly adjusts game difficulty. ### Key BASIC Idioms - `RAND` (line 1000) without an argument re-seeds the random number generator from the system clock, ensuring different sequences each game. - `PAUSE 0` is not used here; instead, `INKEY$` is polled in a tight loop (lines 1090–1100) to wait for a valid keypress. - The high-score check at line 1180 uses `IF HS < 101-M THEN GOSUB 3000` — a clean conditional subroutine call pattern. - Screen layout uses multiple `AT` clauses within a single `PRINT` statement (line 300) to efficiently draw the board. ### Bugs and Anomalies | Location | Issue | | --- | --- | | Line 1075 | `IF A<28 THEN GOSUB 2000` is executed before the player has chosen a position for the current turn. On the very first iteration `A=28` (set at line 1030), so this is safe initially, but on subsequent loops `A` holds the result from the *previous* turn, potentially triggering an erroneous penalty call. | | Line 1080 | The move counter `M` is incremented before checking if the player pressed 0 to skip (line 1110). Pressing 0 costs a move. | | Line 1030 | `A` is initialised to 28 to prevent a false penalty trigger on the first pass of line 1075, but this value is a bare character code magic number with no comment. | ### Display and Inverse Video The title “SUB-NINER” at line 100 is rendered entirely in inverse video using the `%X` escape for each character, producing a solid highlighted title bar. The board background uses a string of inverse spaces (`B$` at line 200) to draw filled rectangular cells across five rows, creating a visual grid for the nine digit positions. ## Source Code ``` 10 LET C$="" 20 LET HS=0 30 IF LEN C$>0 THEN PRINT AT 5,0;"HIGH SCORE - ";C$;" - ";HS 50 PRINT AT 20,0;"WHAT IS YOUR NAME?" 60 INPUT N$ 100 PRINT AT 2,7;"% %S%U%B%-%N%I%N%E%R% " 200 LET B$="% % % % % % % % % % % " 300 PRINT AT 9,7;B$;AT 10,7;B$;AT 11,7;B$;AT 12,7;B$;AT 13,7;B$ 400 PRINT AT 8,8;"123456789" 600 PRINT AT 20,0;N$;" IS NOW PLAYING." 1000 RAND 1020 LET A$="999999999" 1030 LET A=28 1040 LET M=0 1050 LET R=INT (1+RND*RND*9) 1060 PRINT AT 10,8;A$;AT 12,12;R 1070 IF A$="000000000" THEN GOTO 1150 1075 IF A<28 THEN GOSUB 2000 1080 LET M=M+1 1085 PRINT AT 2,20;"SCORE = ";101-M;" " 1090 LET P=CODE INKEY$-28 1100 IF P<0 OR P>9 THEN GOTO 1090 1110 IF P=0 THEN GOTO 1060 1120 LET A=CODE A$(P)-R 1125 IF A<28 THEN GOSUB 2000 1130 LET A$(P)=CHR$ A 1140 GOTO 1050 1150 PRINT AT 18,0;"FINAL SCORE = ";101-M;AT 20,0;"PRESS ENTER TO PLAY AGAIN" 1160 INPUT Z$ 1170 CLS 1180 IF HS<101-M THEN GOSUB 3000 1190 GOTO 30 2000 LET A=CODE A$(P)+10-R 2010 LET M=M+R 2020 RETURN 3000 LET C$=N$ 3010 LET HS=101-M 3020 RETURN 9998 SAVE "SUB-NINE%R" 9999 RUN ```