--- title: "Jack5" id: 54975 type: "computer_media" slug: "jack5" url: "http://localhost/computer_media/jack5/" markdown_url: "http://localhost/computer_media/jack5.md" published_at: "2024-06-10T01:44:45+00:00" modified_at: "2026-03-30T21:43:21+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2019/02/20230809-041332.jpg" excerpt: "A two-entity grid game with UDG sprites, SCREEN$-based collision detection, and a high-score name entry system packed into tight BASIC code." 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: 54964 title: "ISTUG Public Domain Library 6" type: "computer_media" url: "http://localhost/computer_media/istug-public-domain-library-6/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/06/SCR-20260329-nqan.png" media_type_tags: "Game" --- This program implements a two-entity maze/grid game featuring a player character and an adversary named “Jack,” with UDG (User Defined Graphics) characters defined via DATA statements at line 1460 onward. The player sprite is drawn using CHR$ 144–148 UDGs loaded at startup, and movement is controlled via keys 5–8 in the Sinclair cursor-key layout. The arena is constructed using a layered PRINT routine with a re-entry flag variable `f` that allows the same PRINT lines to be reused both for initial drawing and partial redraws via GOSUB. Scoring tracks both a session score and a high score stored in variables `s` and `h`, with the high-score holder’s name captured via INPUT and limited to 11 characters. A short melody is played at game start using BEEP with note and duration data in lines 1260–1270, and collision detection relies on SCREEN$ and ATTR reads rather than coordinate arrays. *** ## Program Analysis ### Program Structure The program divides into several functional phases executed in sequence: 1. **Initialization (lines 10–70):** Variables zeroed, player name defaulted to “Jack”, UDGs loaded from DATA. 2. **Arena drawing (lines 80–300):** The play-field border and interior are printed using a layered approach controlled by flag `f`. 3. **Game intro / melody (lines 310–330):** GOSUB to line 1230 plays a startup tune and displays the high score. 4. **Main game loop (lines 340–910):** Handles player movement, adversary movement, collision detection, and scoring. 5. **Subroutines (lines 920–1450):** Adversary steering (920), scoring/game-over (1050), bonus target placement (1340/1390), intro melody (1230). 6. **UDG DATA (lines 1460–1500):** Eight-byte sprite bitmaps for five UDG characters. ### UDG Sprite Loading Lines 30–40 load 64 bytes (8 bytes × 8 UDGs) from DATA starting at line 1460 directly into UDG RAM using `POKE USR "\a"+b,c`. The variable `b` iterates 0–63, so UDGs `\a` through `\h` are all populated. The sprites include directional arrow/ball shapes used for both the player (`\f`) and adversary (`\h`) characters, as well as target markers (`\g`). Note that the DATA at line 1460 contains the literal identifier `C` rather than a numeric constant, and line 1480 also uses `C` and `y` as DATA values. Since `y=0` at startup and `c=144` during the game, this is intentional reuse of variable names inside DATA statements — a known BASIC technique where READ resolves variable names in DATA as their current values. ### Arena Drawing with Re-entry Flag The arena is drawn once at startup by executing lines 90–290 sequentially with `f=0`. After the full draw, `f` is set to 1 at line 300 and a GOSUB to line 250 is issued. The IF-THEN-GO TO and IF-THEN-RETURN guards at lines 100, 120, 140, etc. create a jump table: when re-entered with `f=1`, execution falls through until it hits the matching guard and either RETURNs or jumps to a specific row’s PRINT statement. This allows individual screen rows to be redrawn without reprinting the whole arena. ### Movement and Collision Detection Both the player and the adversary use a direction-vector scheme. The player uses `du` (delta-row) and `dp` (delta-column); the adversary uses `da` and `dt`. Collision with walls is detected by reading `SCREEN$` at the target cell before moving (lines 520, 540). When a wall is hit, the direction vector is rotated 90 degrees by swapping and negating components (lines 1180–1220), implementing simple billiard-ball reflection. The player sprite CHR$ code `c` increments on each wall bounce (lines 1180, 1190) cycling through UDGs 144–148 to show a directional arrow graphic, with wrap-around at 148. ### Adversary Steering Logic The subroutine at line 920 implements rudimentary homing behavior for the adversary (“Jack”). Variables `lj` and `lf` represent “lane” positions for the player and adversary respectively. Each call to the subroutine moves `lf` one step toward `lj` (line 930) and adjusts the adversary’s trajectory components `da`/`dt` accordingly (lines 950–1030). The variable `q` acts as a steering rate limiter — it counts down from `v` (speed factor) before a direction change is applied (line 940). As the game progresses and `v` decreases (line 700), steering becomes more frequent and the adversary more aggressive. ### Scoring and Difficulty Scaling Two score variables are maintained: `s` (total session score) and `s2` (level progress counter). Collecting a bonus target (`\g`) awards 1 point via `s` and `s2` increments (line 690). Reaching the special target at coordinates `(ba,rl)` awards 5 points (line 1370). When `s2` reaches 244, the speed variable `v` is decremented by a small RND amount and the arena is redrawn (line 700), increasing difficulty over time. Bonus targets are placed randomly but are excluded from the score display area rows 8–12 and from cells already occupied by walls or other targets (line 1420). ### Input Handling The main loop polls `INKEY$` repeatedly. Lines 730–850 validate that the pressed key is in the range 53–56 (characters ‘5’–’8′, the Sinclair cursor keys). The movement logic at lines 810–820 computes new coordinates only along the axis perpendicular to current travel: if moving vertically (`dp≠0`), only left/right keys 5/8 are accepted, and vice versa. This enforces grid-aligned movement typical of snake-style games. The expression `((i$="6")-(i$="7"))` computes +1, 0, or -1 inline without branching. ### High Score Persistence The high score `h` and name `e$` are stored in variables that persist across game restarts since the game loops back to line 50 (not line 10) on game over. Line 1130 prompts for a new name only if the session score beats the stored high score, with a length check enforcing a maximum of 11 characters. If the name is too long, the game loops back to line 1050 (the game-over handler) rather than to the name prompt directly, which re-triggers the musical fanfare — a minor behavioral quirk. ### Notable Techniques - `PAPER PI` at line 60 uses the fact that `INT PI = 3` to set PAPER color 3 (magenta) without a numeric literal. - Color strings `y$` and `z$` are prebuilt at line 50 using `CHR$ 16+CHR$ color+char` as embedded control sequences, avoiding repeated INK keywords in the arena-drawing PRINTs. - The DATA at lines 1260–1280 encodes a two-phrase melody (resembling “Happy Birthday”) with note pitch and duration pairs read by the BEEP loop at lines 1280–1300. - `ATTR (a+da,t+dt)>128` at line 560 detects inverse-video cells (ATTR values ≥128 indicate FLASH or BRIGHT with INVERSE), used to identify special screen objects without checking the character itself. - Line 500 uses `BEEP .01,0` triggered on any keypress before the direction-check, providing tactile audio feedback independently of movement validity. ### Potential Bugs and Anomalies - Line 790 reads `j` from DATA at line 750, but the DATA contains the string `"6"` not a number, so `j` will receive 0 (or cause an error) unless the BASIC interpreter coerces the string. The intent appears to be checking whether `du=0` or `dp=0`, using variable names as DATA values similar to the UDG DATA technique, but `du` and `dp` are not in the DATA — only the literal strings `"6"`, `"7"`, `"5"`, `"8"` and the variable name `du`/`dp` written as bare identifiers. This section of movement validation may not function as intended. - At line 1130, if the entered name is too long, `PRINT AT u,p-5` uses the last player position, which may be off-screen or overlap game graphics depending on where the player died. - The variable `i$` is set at line 720 but lines 810–830 also read `INKEY$` directly rather than using `i$`, meaning a key released between line 720 and line 830 could cause `i$` and the inline `INKEY$` reads to disagree. ## Source Code ``` 10 LET y=0: LET h=y 20 LET e$="Jack" 30 RESTORE 1460: FOR b=y TO 63 40 READ C: POKE USR "\a"+b,c: NEXT B 50 LET s=y: LET v=1: LET y$=CHR$ 16+CHR$ y+"\g": LET z$=CHR$ 16+CHR$ 4+"X" 60 LET z=4: PAPER PI: INVERSE 1 70 LET f=y 80 CLS 90 PRINT INK z;"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" 100 IF f=1 THEN RETURN 110 PRINT z$;y$;"\g\g\g\g\g\g\g\g\g\g\g\g''''\g\g\g\g\g\g\g\g\g\g\g\g\g";z$ 120 IF f=1 THEN GO TO 90 130 PRINT z$;y$; INK z;"\g\g\g\g\g\g\g\g\g\g\g\g"; INK y;"''''"; INK z;"\g\g\g\g\g\g\g\g\g\g\g\g";y$;z$ 140 IF f=1 THEN GO TO 110 150 PRINT z$;y$;z$;y$;"\g\g\g\g\g\g\g\g\g\g''''\g\g\g\g\g\g\g\g\g\g\g";z$;y$;z$ 160 IF f=1 THEN GO TO 130 170 PRINT z$;y$;z$;y$;z$;"XXXXXXXXX"; INK y;"''''";z$;"XXXXXXXXX";y$;z$;y$;z$ 180 IF f=1 THEN GO TO 150 190 PRINT z$;y$;z$;y$;z$;y$;"\g\g\g\g\g\g\g\g''''\g\g\g\g\g\g\g\g\g";z$;y$;z$;y$;z$ 200 IF f=1 THEN GO TO 170 210 PRINT z$;y$;z$;y$;z$;y$;z$;"XXXXXXX"; INK y;"''''";z$;"XXXXXXX";y$;z$;y$;z$;y$;z$ 220 IF f=1 THEN GO TO 190 230 PRINT z$;y$;z$;y$;z$;y$;z$;y$;"\g\g\g\g\g\g''''\g\g\g\g\g\g\g";z$;y$;z$;y$;z$;y$;z$ 240 IF f=1 THEN GO TO 210 250 PRINT z$;y$;z$;y$;z$;y$;z$;y$;z$;"XXXXXXXXXXXXXXX";y$;z$;y$;z$;y$;z$;y$;z$ 260 IF f=1 THEN GO TO 230 270 FOR a=1 TO 4 280 PRINT z$; INK y;"'''''''";z$;"XXXXXXXXXXXXXXX"; INK y;"'''''''";z$ 290 NEXT a 300 LET f=1 310 GO SUB 250 320 INVERSE y 330 GO SUB 1230 340 LET u=20 350 PAPER 0: INK 6 360 LET p=17 370 LET du=y 380 LET lj=4 390 LET dp=1 400 LET lf=4 410 LET a=10 420 LET g$="'" 430 LET t=1 440 LET l$="'" 450 LET da=-1 460 PRINT AT 9,9; PAPER y; INK 4;"Score: " 470 LET dt=0 480 LET q=1 490 LET c=144 500 IF INKEY$<>"" THEN BEEP .01,0 510 PRINT AT u,p; INK 6;CHR$ c 520 IF SCREEN$ (u+du,p+dp)="X" THEN GO TO 1180 530 PRINT AT a,t; INK PI;g$ 540 IF SCREEN$ (a+da,t+dt)="X" THEN GO TO 1210 550 LET g$=SCREEN$ (a+da,t+dt) 560 IF ATTR (a+da,t+dt)>128 THEN LET g$="," 570 IF g$<>"'" THEN LET q=v 580 IF g$<>"'" AND g$<>"," THEN LET g$="\g" 590 LET a=a+da: LET t=t+dt 600 IF a=ba AND t=rl THEN GO SUB 1390 610 IF SCREEN$ (a,t)="'" AND lj<>lf THEN GO SUB 920 620 PRINT AT a,t;"\h" 630 PRINT AT u,p; INK PI;l$ 640 LET u=u+du: LET p=p+dp 650 IF ATTR (u,p)=6 THEN GO TO 1050 660 LET m=0: LET l$=SCREEN$ (u,p): IF l$="" THEN LET l$=",": LET m=1 670 IF u=ba AND p=rl THEN GO SUB 1340 680 PRINT AT u,p;"\f" 690 IF m=1 THEN LET s=s+1: LET s2=s2+1: BEEP .005,-10: BEEP .005,-5: PRINT AT 9,17;s 700 IF s2>=244 THEN LET v=v-RND: GO TO 60 710 IF INKEY$="" OR l$="," THEN GO TO 500 720 LET i$=INKEY$ 730 IF CODE INKEY$<53 OR CODE INKEY$>56 THEN GO TO 500 740 RESTORE 750 750 DATA du,"6","7",dp,"5","8" 760 FOR i=1 TO 2 770 READ j: READ j$: READ k$ 780 IF j=y AND INKEY$<>j$ AND INKEY$<>k$ THEN GO TO 500 790 NEXT i 800 LET u1=u: LET p1=p 810 LET u1=u+((i$="6")-(i$="7"))*(du=y)*2 820 LET p1=p+((i$="8")-(i$="5"))*(dp=y)*2 830 LET n=(i$="6")*(dp=1)+(i$="7")*(dp=-1)+(i$="5")*(du=1)+(i$="8")*(du=-1) 840 IF n=y THEN LET n=-1 850 IF lj+n=y OR lj+n=5 THEN GO TO 500 860 LET lj=lj+n 870 PRINT AT u,p; INK PI;l$ 880 LET u=u1: LET p=p1 890 PRINT AT u,p;"\f" 900 BEEP .01,10 910 GO TO 500 920 IF INT q=y THEN RETURN 930 LET tf=lf+(lflj) 940 LET q=q-1 950 LET o=(lf>lj)*(da=-1)+(lflj)*(dt=1)+(lf11 THEN PRINT AT u,p-5;"Too long.......": GO TO 1050 1140 IF h8 AND ba<13) THEN GO TO 1390 1430 PRINT AT ba,rl; INK 5; PAPER y;"\g" 1440 IF s>=112 THEN LET s2=s2+1 1450 RETURN 1460 DATA 60,126,240,224,C,240,126,60 1470 DATA y,66,195,C,231,255,126,60,C,126,15,7,C,15,126,60 1480 DATA 60,126,255,231,195,C,66,y,C,C,C,C,60,C,C,C 1490 DATA 60,126,255,C,C,C,126,60,y,6,8,60,126,60,24,y 1500 DATA 24,60,24,255,24,C,36,102 1510 SAVE "JACK 5" LINE 10 ```