Jack5

This file is part of and ISTUG Public Domain Library 6. Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 2068
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.

Content

Appears On

Library tape of the Indiana Sinclair Timex User’s Group.

Related Products

Related Articles

Related Content

Image Gallery

Jack5

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+(lf<lj)-(lf>lj)
  940 LET q=q-1
  950 LET o=(lf>lj)*(da=-1)+(lf<lj)*(da=1)
  960 IF o=y THEN LET o=-1*(dt=y)
  970 LET o=o*2
  980 LET t=t+o
  990 LET o=(lf>lj)*(dt=1)+(lf<lj)*(dt=-1)
 1000 LET lf=tf
 1010 IF o=y THEN LET o=-1*(da=y)
 1020 LET o=o*2
 1030 LET a=a+o
 1040 RETURN 
 1050 RESTORE 1170
 1060 FOR a=1 TO 4
 1070 READ b$: READ r
 1080 PRINT AT u,p;b$
 1090 BEEP 1,r: NEXT a
 1100 FOR a=1 TO 64
 1110 BEEP .01,25: NEXT a
 1120 PRINT AT 17,11;"GAME OVER"
 1130 IF h<s THEN INPUT "You have passed highest score:  Please type in your name and     press ENTER. ";e$: IF LEN e$>11 THEN PRINT AT u,p-5;"Too long.......": GO TO 1050 
 1140 IF h<s THEN LET h=s
 1150 IF INKEY$="" THEN GO TO 1150
 1160 GO TO 50
 1170 DATA "\f",30,"\e",20,".",10," ",0
 1180 IF du=y THEN LET du=-dp: LET dp=y: LET c=c+1: GO TO 520
 1190 IF dp=y THEN LET dp=du: LET du=y: LET c=c+1: IF c=148 THEN LET c=144
 1200 GO TO 520
 1210 IF da=y THEN LET da=dt: LET dt=y: GO TO 590
 1220 IF dt=y THEN LET dt=-da: LET da=y: GO TO 590
 1230 RESTORE 1260
 1240 LET s2=y
 1250 GO SUB 1390
 1260 DATA 7,1,7,.5,7,.5,10,1,12,1,14,.5,12,1.5,10,1.5,12,.5,7,1,7,.5,7,.5,10,1,12,1,7,2
 1270 DATA 7,1,7,1,10,1,12,1,14,.5,12,1.5,10,1,12,1,7,1,7,1,5,.5,4,1.5,y,2
 1280 FOR a=1 TO 26
 1290 READ w,x
 1300 BEEP x/10,w: NEXT a
 1310 IF h<s THEN LET h=s
 1320 PRINT AT 11,9; PAPER z; INK y;"High score: ";h;AT 12,9;"by ";e$
 1330 RETURN 
 1340 FOR d=24 TO y STEP -1
 1350 BEEP .01,d: NEXT d
 1360 BEEP .1,36
 1370 LET s=s+5
 1380 LET l$=","
 1390 LET ba=INT (RND*20)+1
 1400 LET rl=INT (RND*29)+1
 1410 LET b$=SCREEN$ (ba,rl)
 1420 IF b$="'" OR b$="X" OR (b$="," AND s2<112) OR (ba>8 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

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

People

No people associated with this content.

Scroll to Top