Chomper

This file is part of and SINCUS Exchange Tape 101 - Entertainment. Download the collection to get this file.
Type: Program
Platform(s): TS 2068
Tags: Arcade

Chomper is a Pac-Man-style maze game in which the player navigates a character through a dot-filled maze, eating pellets and special power items while being pursued by two ghosts. The maze layout is stored entirely as a 21×21 string array (B$), with each cell holding a character that encodes both the displayed graphic and the collision type. Twenty-one User Defined Graphics are loaded from DATA statements at startup to render the maze walls, player, and ghost sprites. Ghost movement is driven by a probability-gated chase algorithm that compares ghost row/column coordinates to the player’s position, with boundary checks performed by reading character codes from B$ directly. A “frightened” mode is triggered when the player eats a power pellet (UDG \o), setting a counter that causes ghosts to flash in INK 4 and allows the player to eat them for bonus points; eating a ghost resets its position to the center of the maze.


Program Analysis

Program Structure

The program is organized into a clear set of functional blocks:

  1. Lines 1–5: Initialization — load UDGs from DATA, set up variables, call the title/skill-level subroutine at 7000.
  2. Lines 10–50: DATA statements for 21 UDG definitions (chars 144–164), covering maze wall segments, player sprite, ghost sprite, dot, power pellet, and bonus item.
  3. Lines 55–260: Screen setup and maze construction — B$() rows are assigned as string literals encoding the maze using UDG escape characters.
  4. Lines 270–550: Main game loop — print maze, poll keyboard, move player, handle pickups, move ghosts, check collisions, redisplay.
  5. Lines 1000–1030: Random bonus item placement subroutine.
  6. Lines 1500–1830: Player movement subroutines for keys 8 (right), 5 (left), 7 (up), 6 (down).
  7. Lines 2000–2050: Power-pellet activation — sets C=-1 (frightened mode), resets count timer.
  8. Lines 3000–4030 / 6010–6020: Unused or vestigial ghost-pathfinding helper routines (variables D, F, G, H are never initialized in the active code path).
  9. Lines 5000–5070: Level-complete celebration animation, then loop back to maze reset.
  10. Lines 7000–7090: Title screen and skill-level selection; skill input is converted to a probability value a used in ghost movement.
  11. Lines 8000–8010: Player eats a frightened ghost — awards points, resets ghost position.
  12. Lines 9000–9530: Death handling — checks frightened state before killing player, decrements lives, game-over, high-score update, restart.

Maze Representation

The maze is stored in a DIM B$(21,21) two-dimensional string array. Each element is a single character: a UDG (codes 144–164) for wall segments, "." for a dot, " " for empty space, "/" and "\" for the tunnel wrap-around portals, and special UDGs for power pellets (\o, char 164) and the bonus cherry (\u, char 160). This dual-purpose encoding lets the same array serve as both the display buffer and the collision map, with ghost movement checks reading CODE B$(row,col) directly to determine passability.

Ghost Movement Algorithm

Two ghosts are tracked by coordinates (GX,GY) and (GX1,GY1). At each game loop iteration, the code attempts to move each ghost one cell toward the player’s position (y,x). Each directional move is gated by a random check using RND < a, where a is the skill probability (0.1 for hardest, 0.6 for easiest). A lower value of a means fewer random direction changes and more direct pursuit. Passability is tested by checking that the candidate cell’s character code is not a wall UDG (codes 144–157) and not a tunnel marker (codes 47 for / or 92 for \).

The frightened mode flag is the sign of variable C: C=1 is normal chase; C=-1 is frightened. A counter (COUNT) increments each loop; when it reaches 30 (line 510), C is reset to 1, ending frightened mode.

Collision and Pickup Detection

Line 380 checks if the player’s new cell contains a dot; line 390 checks for a power pellet; line 400 checks for the bonus cherry. Rather than using separate position flags, all state is read directly from B$(Y,X) after the movement subroutine returns and before the cell is cleared to " " at line 405. This means the array is both the display source and the game-state authority.

Ghost–player collision at lines 540–545 compares coordinates. If COUNT <= 30 at death-entry (line 9000), the code branches to line 8000 to treat the collision as the player eating a frightened ghost instead of dying.

UDG Initialization

Lines 1–2 load all 21 UDGs (chars 144–164, i.e., \a through \u) by reading 8 bytes each from the DATA statements at lines 10–55 and POKEing them into USR CHR$ a + b. The RESTORE 0 at line 1 resets the DATA pointer to the very first DATA statement regardless of line order — a reliable way to ensure correct data reading on restart.

Tunnel Wrap-Around

The maze contains left-right tunnel portals. In the rightward movement subroutine (line 1525), if the cell to the right contains "\" (char 92, stored as a literal backslash in the maze row), the player’s x-coordinate is set to 2, wrapping them to the left side. The leftward subroutine (line 1620) mirrors this: detecting "/" wraps the player to column 20. This is a simple hard-coded portal rather than a general mechanism.

Skill Level Selection

At line 7060, the skill level is read via INKEY$ in a tight polling loop. The raw key code minus 49 gives a 0–4 range; then line 7080 adds 1 and divides by 10, yielding values 0.1 through 0.5. This float is stored in variable a, re-using the same variable name as the player sprite character string a$ — but a (numeric) and a$ (string) are distinct in Sinclair BASIC, so there is no conflict. A value of 5 entered by the user yields a = 0.6, making ghosts move more randomly (easier); value 1 yields a = 0.2 for more direct pursuit (harder).

Level Completion and Scoring

The target score threshold is stored in T, initialized to 180 (line 3). Each dot scores 1 point; each power pellet scores 10 and adds 10 to T (line 400); eating a frightened ghost awards 10 points and adds 10 to T. When S = T (line 415), the level-complete routine at 5000 runs a scrolling animation and adds 180 to T before returning to redraw the maze, effectively starting a new level with the same maze refilled.

Bugs and Anomalies

  • Subroutines at lines 3000–3040, 4010–4020, 6010–6020 are never called from the active code path. They appear to be remnants of a more sophisticated pathfinding system using variables D, F, G, H that were replaced by the current inline directional chase logic.
  • Line 430 has a logical precedence ambiguity: d<144 OR d>157 AND d<>47 AND d<>92. Because AND binds tighter than OR in Sinclair BASIC, this reads as d<144 OR (d>157 AND d<>47 AND d<>92), which is probably the intended meaning (allow passage if code is below UDG range, or above UDG range and not a wall character). However, line 470 correctly uses explicit parentheses: (d<144 OR d>157), showing inconsistency between the two ghost movement blocks.
  • The variable a used for skill level probability is never re-initialized on game restart (line 9530 goes to 60, skipping line 7000), so skill level persists across restarts within the same session — this is likely intentional.
  • Line 5000 prints AT 10,31 which places a character at column 31, at the very right edge of a 32-column display, followed by a power-pellet UDG. This is benign but the character may be partially off-screen on some display configurations.

Variable Summary

VariablePurpose
B$()21×21 maze array (display + collision map)
y, xPlayer row, column
a$Current player sprite UDG (directional)
gx, gyGhost 1 row, column
gx1, gy1Ghost 2 row, column
D$, E$Character saved under ghost 1 and 2 (restore on move)
CGhost mode: 1 = normal, -1 = frightened
COUNTFrightened mode timer
sCurrent score
hsHigh score
TScore target for level completion
livesRemaining lives
aGhost randomness probability (skill level)

Content

Appears On

Pure fun — land on the moon, herd sheep, hunt submarines, race cricketers, eat dots as Pac-Man, and dodge alien swarms across a nighttime skyline. SINCUS Tape 101 is a deep bench of TS 2068 arcade action, card games, and sports simulations.

Related Products

Related Articles

Related Content

Image Gallery

Source Code

    1 RESTORE 0: FOR a=144 TO 164
    2 FOR b=0 TO 7: READ c: POKE USR CHR$ a+b,c: NEXT b: NEXT a
    3 LET s=0: LET hs=0: LET T=180: DIM B$(21,21): LET COUNT=30
    4 LET lives=3
    5 GO SUB 7000
   10 DATA 0,255,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0,255,0,0,0,0,255,0,0,248,4,2,2,2,2,2
   20 DATA 0,31,32,64,64,64,64,64,0,63,64,128,128,64,63,0,0,252,2,1,1,2,252,0
   30 DATA 2,2,2,2,2,4,248,0,64,64,64,64,64,32,31,0,2,2,2,2,2,2,2,2
   40 DATA 0,24,36,66,66,66,66,66,66,66,66,66,66,36,24,0,64,64,64,64,64,64,64,64,66,66,66,66,66,66,66,66,0,0,16,56,124,56,16,0
   50 DATA 24,60,30,15,30,60,24,0,0,0,34,119,127,62,28,8,0,24,60,120,240,120,60,24,16,56,124,254,238,68,0,0,56,126,90,219,255,255,255,147,2,6,10,20,36,68,238,68
   55 PAPER 1: BORDER 1: CLS : INK 7: PAPER 0
   60 LET B$(1)="\e\a\a\a\a\a\a\a\a\a\n\a\a\a\a\a\a\a\a\a\d"
   70 LET B$(2)="\m.........\n.........\j"
   80 LET B$(3)="\m.\e\a\d.\e\a\d.\n.\e\a\d.\e\a\d.\j"
   90 LET B$(4)="\m\o\m \j.\m \j.\n.\m \j.\m \j\o\j"
  100 LET B$(5)="\m.\i\b\h.\i\b\h.\l.\i\b\h.\i\b\h.\j"
  110 LET B$(6)="\m...................\j"
  120 LET B$(7)="\m.\f\c\g.\k.\f\c\a\c\g.\k.\f\c\g.\j"
  130 LET B$(8)="\m.....\n...\n...\n.....\j"
  140 LET B$(9)="\i\b\b\b\b.\m\c\g.\l.\f\c\j.\b\b\b\b\h"
  150 LET B$(10)="    \j.\n.......\n.\m    "
  160 LET B$(11)="\b\b\b\b\h.\l.\e---\d.\l.\i\b\b\b\b"
  170 LET B$(12)="/.......\m   \j.......\"
  180 LET B$(13)="\a\a\a\a\d.\k.\i---\h.\k.\e\a\a\a\a"
  190 LET B$(14)="    \j.\n.... ..\n.\m    "
  200 LET B$(15)="\b\b\b\b\h.\l.\f\c\a\c\g.\l.\i\b\b\b\b"
  210 LET B$(16)="\m.........\n.........\j"
  220 LET B$(17)="\m\o\f\c\d.\f\c\g.\l.\f\c\g.\e\c\g\o\j"
  230 LET B$(18)="\m...\n...........\n...\j"
  240 LET B$(19)="\a\a\d.\l.\f\c\c\c\c\c\c\c\g.\l.\e\a\a"
  250 LET B$(20)="  \j...............\m  "
  260 LET B$(21)="   \a\a\a\a\a\a\a\a\a\a\a\a\a\a\a   "
  270 FOR N=1 TO 21: PRINT AT N,1;B$(N): BEEP .05,N+15: NEXT N
  280 LET y=14: LET x=12: LET a$="\r"
  290 LET gx=12: LET gy=12: LET gx1=12: LET gy1=11: LET C=1: LET D$=" ": LET E$=" "
  295 PRINT AT 0,19;"HI-SCORE=";hs
  300 PRINT AT gx,gy; INK 3;"\t"
  310 PRINT AT gx1,gy1; INK 5;"\t"
  320 PRINT AT y,x; INK 6;a$
  330 IF INT (RND*100)=0 THEN GO SUB 1000
  340 IF INKEY$="8" THEN GO SUB 1500
  350 IF INKEY$="5" THEN GO SUB 1600
  360 IF INKEY$="7" THEN GO SUB 1700
  370 IF INKEY$="6" THEN GO SUB 1800
  380 IF B$(Y,X)="." THEN LET s=s+1: BEEP .05,25
  390 IF B$(Y,X)="\o" THEN LET s=s+10: BEEP .1,25: BEEP .1,39: GO SUB 2000
  400 IF B$(Y,X)="\u" THEN LET s=s+10: LET T=T+10: BEEP .1,28: BEEP .1,36
  401 PRINT AT 0,0;"LIVES=";lives;"  SCORE=";S
  405 LET B$(Y,X)=" "
  410 PRINT AT y,x; INK 6;a$
  415 IF S=T THEN GO TO 5000
  420 PRINT AT GX,GY;D$;AT GX1,GY1;E$
  425 IF RND<a THEN GO TO 440
  430 IF gy<x THEN LET d=CODE b$(gx,gy+c): IF d<>47 AND d<>92 AND d<144 OR d>157 AND d<>47 AND d<>92 THEN LET gy=gy+c: GO TO 470
  435 IF RND<a THEN GO TO 450
  440 IF gy>x THEN LET d=CODE b$(gx,gy-c): IF d<>47 AND d<>92 AND d<144 OR d>157 AND d<>47 AND d<>92 THEN LET gy=gy-c: GO TO 470
  445 IF RND<a THEN GO TO 460
  450 IF gx<y THEN LET d=CODE b$(gx+c,gy): IF d<>47 AND d<>92 AND d<144 OR d>157 AND d<>47 AND d<>92 THEN LET gx=gx+c: GO TO 470
  455 IF RND<a THEN GO TO 470
  460 IF gx>y THEN LET d=CODE b$(gx-c,gy): IF d<>47 AND d<>92 AND d<144 OR d>157 AND d<>47 AND d<>92 THEN LET gx=gx-c
  465 IF RND<a THEN GO TO 475
  470 IF gy1<x THEN LET d=CODE b$(gx1,gy1+c): IF d<>47 AND d<>92 AND (d<144 OR d>157) THEN LET gy1=gy1+c: GO TO 500
  472 IF RND<a THEN GO TO 480
  475 IF gy1>x THEN LET d=CODE b$(gx1,gy1-c): IF d<>47 AND d<>92 AND d<144 OR d>157 AND d<>47 AND d<>92 THEN LET gy1=gy1-c: GO TO 500
  477 IF RND<a THEN GO TO 490
  480 IF gx1<y THEN LET d=CODE b$(gx1+c,gy1): IF d<>47 AND d<>92 AND d<144 OR d>157 AND d<>47 AND d<>92 THEN LET gx1=gx1+c: GO TO 500
  485 IF RND<a THEN GO TO 500
  490 IF gx1>y THEN LET d=CODE b$(gx1-c,gy1): IF d<>47 AND d<>92 AND d<144 OR d>157 AND d<>47 AND d<>92 THEN LET gx1=gx1-c
  500 LET COUNT=COUNT+1
  510 IF COUNT>=30 THEN LET C=1
  520 IF C=-1 THEN PRINT AT GX,GY; INK 4; FLASH 1;"\t"
  525 IF C=1 THEN PRINT AT GX,GY; INK 3;"\t"
  530 IF C=-1 THEN PRINT AT GX1,GY1; INK 4; FLASH 1;"\t"
  535 IF C=1 THEN PRINT AT GX1,GY1; INK 5;"\t"
  540 IF GX=y AND GY=x THEN GO TO 9000
  545 IF GX1=y AND gy1=X THEN GO TO 9000
  550 FLASH 0: LET D$=B$(GX,GY): LET E$=B$(GX1,GY1): GO TO 330
 1000 IF B$(14,11)="\u" THEN RETURN 
 1010 PRINT AT 14,11; INK 2;"\u": BEEP .5,15: IF B$(14,11)="." THEN LET T=T-1
 1020 LET B$(14,11)="\u"
 1030 RETURN 
 1500 PRINT AT Y,X;" "
 1505 IF B$(y,x+1)="." THEN LET x=x+1: GO TO 1530
 1510 IF B$(y,x+1)=" " THEN LET X=X+1: GO TO 1530
 1520 IF b$(y,x+1)="\u" THEN LET x=x+1
 1525 IF B$(y,x+1)="\" THEN LET x=2
 1530 LET a$="\r"
 1540 RETURN 
 1600 PRINT AT Y,X;" "
 1605 IF B$(y,x-1)="." THEN LET x=X-1: GO TO 1630
 1610 IF B$(y,x-1)=" " THEN LET x=X-1: GO TO 1630
 1615 IF B$(y,x-1)="\o" THEN LET x=X-1: GO TO 1630
 1620 IF B$(y,x-1)="/" THEN LET x=20: GO TO 1630
 1625 IF B$(y,x-1)="\u" THEN LET x=X-1
 1630 LET a$="\p"
 1640 RETURN 
 1700 PRINT AT Y,X;" "
 1705 IF B$(y-1,x)="." THEN LET y=y-1: GO TO 1725
 1710 IF B$(y-1,x)="\o" THEN LET y=y-1: GO TO 1725
 1720 IF B$(y-1,x)=" " THEN LET y=y-1
 1725 LET a$="\q"
 1730 RETURN 
 1800 PRINT AT y,x;" "
 1805 IF B$(y+1,x)="." THEN LET y=y+1: GO TO 1825
 1810 IF B$(y+1,x)="\o" THEN LET y=y+1: GO TO 1825
 1815 IF B$(y+1,x)=" " THEN LET y=y+1
 1825 LET a$="\s"
 1830 RETURN 
 2000 LET C=-1
 2010 LET B$(Y,X)=" "
 2020 PRINT AT Y,X;A$
 2030 LET T=T+10
 2040 LET COUNT=0
 2050 RETURN 
 3000 IF Y>D THEN LET D=D+G
 3010 IF X>F THEN LET F=F+H
 3020 IF X<F THEN LET F=F-H
 3030 IF Y<D THEN LET D=D-G
 3040 RETURN 
 4010 LET D=GX: LET F=GY: RETURN 
 4020 LET D=GX1: LET F=GY1
 4030 RETURN 
 5000 CLS : PRINT AT 10,31;"\o"
 5010 FOR N=1 TO 27
 5020 PRINT AT 10,N; INK 5;" \t  "; INK 7;"\r"
 5030 BEEP .05,N: NEXT N
 5040 FOR N=27 TO 1 STEP -1: PRINT AT 10,N; FLASH 1; INK 4;"\t"; FLASH 0; INK 7;"  \p "
 5050 BEEP .05,N: NEXT N
 5060 LET T=T+180
 5070 GO TO 60
 6010 LET GX=D: LET GY=F: RETURN 
 6020 LET GX1=D: LET GY1=F: RETURN 
 7000 PRINT AT 0,12;"CHOMPER";TAB 11;" \''\''\''\''\''\''\'' "
 7010 PRINT AT 9,0;"\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a"
 7020 PRINT AT 11,3;"SELECT SKILL LEVEL (1-5)     "
 7030 PRINT TAB 7;"(5 is the easiest)"
 7040 PRINT AT 14,0;"\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a"
 7060 LET a=CODE INKEY$-49
 7070 IF a>5 OR a<0 THEN GO TO 7060
 7080 LET a=a+1: LET a=a/10
 7090 RETURN 
 8000 IF gy=x AND gx=y THEN LET s=s+10: LET t=t+10: LET gy=12: LET gx=12: BEEP .05,20: BEEP .05,10: BEEP .1,10: GO TO 550
 8010 IF gy1=x AND gx1=y THEN LET s=s+10: LET t=t+10: LET gy1=11: LET gx1=12: BEEP .1,10: GO TO 550
 9000 IF count<=30 THEN GO TO 8000
 9005 LET LIVES=LIVES-1
 9010 PRINT AT Y,X; FLASH 1;A$: FOR N=50 TO 0 STEP -1: BEEP .05,N: NEXT N
 9020 IF lives<=0 THEN GO TO 9500
 9030 GO TO 270
 9500 IF HS<S THEN LET HS=S
 9510 PRINT AT 10,0;"HIT ANY KEY TO RESTART"
 9520 IF INKEY$="" THEN GO TO 9520
 9530 PAPER 1: LET S=0: CLS : PAPER 0: LET T=180: LET lives=3: GO TO 60
 9998 SAVE "Chomper" LINE 1: BEEP .4,15
 9999 VERIFY ""

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

People

No people associated with this content.

Scroll to Top