--- title: "Micro Mouse" id: 56856 type: "computer_media" slug: "micro-mouse" url: "http://localhost/computer_media/micro-mouse/" markdown_url: "http://localhost/computer_media/micro-mouse.md" published_at: "2024-09-29T07:54:12+00:00" modified_at: "2026-04-03T07:58:49+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/09/324_MM.png" excerpt: "A randomly built maze and a roaming cursor that sniffs out blank cells by peeking directly at display RAM — watch it hunt for the exit and count every step." 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: "Demo" slug: "demo" taxonomy: "genre" url: "http://localhost/type/demo/" media_contents: - id: 56738 title: "Timex Sinclair Public Domain Library Tape 1007" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1007/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/09/324_MM.png" media_type_tags: "Demo" --- MICROMOUSE is a maze-navigation simulation in which a cursor character wanders through a randomly generated obstacle field, trying to reach a fixed goal at screen position (20,30). The maze is built using a combination of inverse-video block characters and block graphics printed at randomised AT positions during a setup loop (lines 30–60). The mouse’s movement logic tests eight compass directions by issuing a PRINT AT to the candidate cell, then reading the display RAM address stored in system variables G (16398) and H (16399) to determine whether that cell is blank (character code 0), implementing a collision-detection technique entirely through PEEK of the display file pointer. A move counter Q accumulates attempts, and on reaching the goal the score is displayed briefly before the maze is regenerated. *** ## Program Analysis ### Program Structure The program divides into four logical phases: 1. **Initialisation (lines 1–6):** Stores the display-file pointer address in `G` (16398) and `H` (16399). 2. **Maze generation (lines 10–68):** Draws top and bottom border rows of inverse spaces, then randomises wall segments and block graphics across the interior in a `FOR A=1 TO 20` loop. A second short loop (lines 61–68) animates the goal marker at (20,30) by flashing it. 3. **Movement engine (lines 70–390):** Places the mouse at a random starting position, then repeatedly probes adjacent cells in up to eight directions using the display-RAM PEEK technique. A counter `Q` records total moves. 4. **Win condition & restart (lines 2000–2070):** On reaching (20,30), prints the move count, pauses with a `FOR/NEXT` delay, redraws the border, and branches back to line 30 to regenerate the maze. ### Display-RAM Collision Detection The most technically interesting feature is the use of system variables at addresses 16398–16399, which together form the two-byte pointer to the current cursor position in the display file. The idiom used throughout is: - `PRINT AT A+1,B;` — moves the display cursor to a candidate cell without printing anything visible. - `IF PEEK (PEEK G+256*PEEK H)=0 THEN LET T=1` — dereferences the cursor pointer to read the character code at that cell. A value of 0 means a space (empty), so the move is valid. This avoids the need for any array-based map representation; the screen itself is the maze data structure. The technique exploits the ZX81/TS1000 system variable *DF_CC* (display file current character address), held at 16398–16399 in little-endian format, making `PEEK G + 256*PEEK H` the full 16-bit address. ### Movement Logic Eight candidate directions are attempted, selected partly randomly via `Y=RND*7+1` and partly through a fallback chain. The direction codes and their target lines are: | Y value | Direction | Line | Delta (row,col) | | --- | --- | --- | --- | | 1 / default | Down | 120 | (+1, 0) | | 2 | Up-right diagonal | 154 | (−1,+1) | | 3 | Down-right diagonal | 200 | (+1,+1) | | 4 | Up | 250 | (−1, 0) | | 5 | Left | 290 | (0,−1) | | 6 | Right | 169 | (0,+1) | | 7 | Up-left diagonal | 330 | (−1,−1) | If none of the tested directions is free, `T` remains 0 and the engine loops back to line 110 to retry with a new random direction. The previous position `E,F` is erased with a space at line 1000 before stamping the mouse character at the new position. ### Key BASIC Idioms - **RND scaling:**`RND*N+1` is used throughout for integer ranges, relying on ZX81 BASIC’s 0–0.9999… RND range truncated via multiplication without an explicit INT call — valid because PRINT AT accepts fractional row/column arguments by truncating internally. - **Delay loop:**`FOR N=1 TO 3000: NEXT N` at lines 2010–2020 provides a pause after the win message without using PAUSE. - **Flicker animation:** The goal marker at (20,30) is toggled between a space and `%*` (inverse asterisk) six times in lines 61–68 to draw attention to the target before the mouse is released. ### Maze Generation Technique The maze interior is built entirely from random PRINT AT statements placing inverse spaces (`%`), block graphics (`\##`, `\;;\!!`), and short wall segments (`% %`). There is no guarantee of solvability; the mouse may occasionally become fully trapped. The left and right borders are drawn per-row inside the setup loop (lines 42 and 50), while top and bottom solid borders are printed before the loop (lines 10 and 20). ### Bugs and Anomalies - Line 106 tests `IF RND>-2476`, which is always true (RND is never negative). This means the random-direction branch at line 110 is never skipped, and the “straight down” default at line 120 is never taken as a direct fall-through from line 95. The conditional is effectively dead code. - Similarly, lines 152 (`RND>-2`), 165 (`RND<-2`), 195 (`RND<-6`), and 245 (`RND<-1`) use impossible RND values (always positive), making those fallback branches either always-taken or never-taken. The movement priorities are therefore fixed rather than truly probabilistic. - Line 2050 `CLEAR` and lines 2060–2070 are unreachable dead code following the `GOTO 30` at line 2040. - The boundary check at line 154 tests `B=30` (the goal column) rather than `B=31`, which could prevent rightward diagonal moves unnecessarily near the right edge. ## Source Code ``` 1 REM **MICROMOUSE** 5 LET G=16398 6 LET H=G+1 10 PRINT AT 0,0;"% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % " 20 PRINT AT 21,0;"% % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % " 30 FOR A=1 TO 20 35 PRINT AT 2+RND**18,1+RND**27;" " 36 PRINT AT 2+RND**18,1+RND**27;" " 37 PRINT AT 2+RND**18,1+RND**27;" " 42 PRINT AT A,0;"% " 43 PRINT AT 3+RND**15,2+RND**22;"% % " 44 PRINT AT 2+RND**18,1+RND**27;" " 45 PRINT AT 2+RND**18,2+RND**24;"% " 46 PRINT AT 3+RND**15,2+RND**27;"##" 47 PRINT AT 2+RND**15,2+RND**24;" ;;!!" 50 PRINT AT A,31;"% " 57 PRINT AT 2+RND**16,2+RND**26;" " 60 NEXT A 61 FOR Z=1 TO 13 62 PRINT AT 20,30;" " 63 PRINT AT 20,30;"%*" 64 PRINT AT 20,30;"%*" 65 PRINT AT 20,30;" " 66 PRINT AT 20,30;"%*" 67 PRINT AT 20,30;" " 68 NEXT Z 70 LET A=RND**6+1 75 LET Q=0 80 LET B=RND**15+1 85 PRINT AT 20,30;" " 90 LET E=A 95 LET Q=Q+1 100 LET F=B 101 IF A=20 AND B=30 THEN GOTO 2000 105 LET T=0 106 IF RND>-2476 THEN GOTO 120 110 LET Y=RND**7+1 111 IF Y=1 THEN GOTO 120 112 IF Y=6 THEN GOTO 169 113 IF Y=3 THEN GOTO 200 114 IF Y=4 THEN GOTO 250 115 IF Y=5 THEN GOTO 290 116 IF Y=2 THEN GOTO 154 117 IF Y=7 THEN GOTO 330 120 PRINT AT A+1,B; 130 IF PEEK (PEEK G+256*PEEK H)=0 THEN LET T=1 140 IF T=1 THEN LET A=A+1 150 IF T=1 THEN GOTO 1000 152 IF RND>-2 THEN GOTO 169 154 IF A=0 OR B=30 THEN GOTO 169 155 PRINT AT A-1,B+1; 156 IF PEEK (PEEK G+256*PEEK H)=0 THEN LET T=1 157 IF T=1 THEN LET B=B+1 158 IF T=1 THEN LET A=A-1 159 IF T=1 THEN GOTO 1000 165 IF RND<-2 THEN GOTO 110 169 PRINT AT A,B+1; 170 IF PEEK (PEEK G+256*PEEK H)=0 THEN LET T=1 180 IF T=1 THEN LET B=B+1 190 IF T=1 THEN GOTO 1000 195 IF RND<-6 THEN GOTO 290 200 PRINT AT A+1,B+1; 210 IF PEEK (PEEK G+256*PEEK H)=0 THEN LET T=1 220 IF T=1 THEN LET A=A+1 230 IF T=1 THEN LET B=B+1 240 IF T=1 THEN GOTO 1000 245 IF RND<-1 THEN GOTO 110 250 PRINT AT A-1,B; 260 IF PEEK (PEEK G+256*PEEK H)=0 THEN LET T=1 270 IF T=1 AND A>0 THEN LET A=A-1 280 IF T=1 THEN GOTO 1000 290 PRINT AT A,B-1; 300 IF PEEK (PEEK G+256*PEEK H)=0 THEN LET T=1 310 IF T=1 AND B>0 THEN LET B=B-1 320 IF T=1 THEN GOTO 1000 330 IF B=0 OR A=0 THEN GOTO 110 340 PRINT AT A-1,B-1; 350 IF PEEK (PEEK G+256*PEEK H)=0 THEN LET T=1 360 IF T=1 THEN LET A=A-1 370 IF T=1 THEN LET B=B-1 380 IF T=1 THEN GOTO 1000 390 GOTO 110 1000 PRINT AT E,F;" " 1010 PRINT AT A,B;"%*" 1020 GOTO 90 2000 PRINT AT 0,15;"% ";Q;"% " 2010 FOR N=1 TO 3000 2020 NEXT N 2030 PRINT AT 0,15;"% % % % % % " 2040 GOTO 30 2050 CLEAR 2060 SAVE "1032%4" 2070 RUN ```