--- title: "DODGEM" id: 70775 type: "computer_media" slug: "dodgem" url: "http://localhost/computer_media/dodgem/" markdown_url: "http://localhost/computer_media/dodgem.md" published_at: "2026-08-23T15:08:15+00:00" modified_at: "2026-08-23T17:09:51+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/dodgem-2.png" alt: "DODGEM screen" excerpt: "Steer a custom-drawn sprite through a colorful obstacle field without crossing your own trail — how long can you survive?" 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_type: "Program" download_url: "https://archive.org/download/timex-sinclair-software-archive/DODGEM%20%28198x%29%28-%29%28TS2068%29%28US%29%28Program%29.zip" tsrun_member: "DODGEM (198x)(-)(TS2068)(US)(Program).tap" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/08/dodgem-2.png" alt: "DODGEM screen" - url: "http://localhost/wp-content/uploads/2026/08/dodgems-1.png" alt: "DODGEM screen" media_type_tags: "Game" --- # DODGEM DODGEM is a maze-avoidance game in which the player steers a custom sprite character through a randomly-generated field of colored obstacles, using the cursor keys (5/6/7/8), while surviving as many ticks as possible without crossing their own path or hitting a wall. The player sprite is defined as a user-defined graphic (UDG) via POKE into USR “p”, with binary data supplied through a DATA/READ loop at line 2000 that converts BIN strings using VAL and STR$. Collision detection is handled entirely through ATTR, checking the color attribute of the destination cell at line 90; a cell with attribute value 48 (green paper, black ink — the border color combination for the player’s trail dot) signals a clear path, while any other value triggers the death sequence. The game tracks a high score in variable hs, initialized to 365, and preserves it across rounds within the same session. Difficulty is controlled by adjusting both the delay loop length q and the BEEP pitch step d, making higher difficulty numbers produce faster, more audible gameplay. *** ### Program Structure The program is organized into four logical sections: 1. **Initialization and intro (lines 5, 2000–2050):** Sets the high score, defines the UDG, displays instructions, and waits for a keypress. 2. **Game setup (lines 10–40):** Reads difficulty, resets variables, draws the border and random obstacles, and places the player. 3. **Main game loop (lines 50–90):** Reads input, moves the player, performs collision detection via ATTR, and updates the display. 4. **Death and lives handling (lines 100–150, 1000–1010):** Plays a crash sound sequence, decrements lives, and either restarts the round or ends the game. ### UDG Definition Line 2000 defines the player sprite by poking eight bytes into the address range beginning at `USR "p"`. The data is stored as decimal representations of binary numbers (e.g., `11000`, `111100`), and each is converted with `VAL ("BIN "+STR$ a)` — a technique that avoids storing binary literals directly in DATA statements while still being human-readable as bit patterns. The UDG is then referenced in PRINT statements as `"\p"`. ### Collision Detection via ATTR Rather than maintaining an explicit map array, the game uses the `ATTR` function at line 90 to read the color attribute of the cell the player is about to move into. If the attribute equals `48`, the cell is considered free and play continues. The value 48 corresponds to a specific paper/ink combination used for the trail dot printed at the previous position (`"."` on a default background), making the attribute itself the game-state record. ### Difficulty Scaling Difficulty input `d` (1–6, where 1 is hardest) is used in two ways after line 20: `q=d+5` sets the length of a delay `FOR` loop at line 70 (lower d = shorter delay = faster game), and `d=d/100` repurposes the variable as the BEEP duration argument in line 90. The input validation at line 10 rejects non-integers and out-of-range values with a warning beep and a retry loop. ### Main Loop Mechanics The main loop at lines 50–90 follows this sequence each tick: - Read `INKEY$` and update direction variables `a` (column delta) and `b` (row delta) using Boolean arithmetic: `(i$="6")-(i$="7")`. - Print the time counter at line 60. - Pause via a counted `FOR` loop (line 70). - Emit a BEEP, increment time, update position, read ATTR, print the sprite and leave a trail dot, then branch on the attribute check. Direction is sticky: the variables `a` and `b` are only updated when a valid key in the range “5”–”8″ is detected, so the player continues in the last-pressed direction when no key is held. ### Obstacle and Arena Generation The border is drawn with a `FOR` loop printing `INK 3` (yellow) block characters at line 20. Forty-five random colored patches are then scattered across the interior at line 30 using `PAPER RND*3+1`, creating a varied field of obstacles. These are purely attribute-based; any non-48 attribute in the player’s path triggers a collision. ### Death Sequence and Lives On collision, lines 100–110 play an escalating BEEP sequence (increasing pitch and duration), decrement `l`, and print the remaining lives. If lives remain (`IF L` at line 110, note the uppercase `L` which references the same variable as lowercase `l`), control jumps to line 1000, which flashes a taunt message, redraws a portion of the maze with transparent (`PAPER 8`) patches, then returns to line 40 to restart the round. When all lives are exhausted, the game checks for a new high score and waits for a keypress before looping back to line 10. ### Notable Techniques and Anomalies - The `VAL ("BIN "+STR$ a)` idiom in the UDG loader elegantly converts decimal-encoded binary numbers without requiring the `BIN` keyword directly in DATA statements. - The DATA statement at line 2000 includes a bare `a` (appearing twice) instead of a numeric literal, which will evaluate to whatever the current value of variable `a` is at runtime — this is likely a bug, as those rows should probably be `0` or some specific bit pattern. Since `a` is not initialized before the subroutine is called from line 5, its value is 0 by default, so the UDG rows default to 0 (blank), which may be intentional for padding rows but is fragile. - The case mismatch `IF L THEN` at line 110 vs. the lowercase `l` used elsewhere is valid on this platform (variable names are case-insensitive in the interpreter), but is inconsistent in the listing. - The keypress wait idiom `IF INKEY$<>"" THEN GO TO ...` followed by `IF INKEY$="" THEN GO TO ...` (lines 130–140, 2030–2040) is a standard flush-then-wait pattern ensuring the player must release and re-press a key to proceed. - `RESTORE` at the start of line 2000 ensures the DATA pointer is reset to the beginning regardless of how the subroutine is entered, making the UDG initialization safe to call only once at startup. ## Source Code ``` 5 LET hs=365: GO SUB 2000 10 INK 0: PAPER 6: BORDER 1: CLS : INPUT "DIFFICULTY (1 TO 6-EASIEST)";d: IF d<1 OR d>6 OR d>INT d THEN BEEP 1,0: GO TO 10 20 LET t=0: LET l=5: LET q=d+5: LET d=d/100: FOR f=0 TO 31: PRINT AT 0,f; INK 3;"█";AT 21,f;"█": IF f<22 THEN PRINT INK 3;AT f,0;"█";AT f,31;"█" 30 NEXT f: FOR f=1 TO 45: PRINT PAPER RND*3+1;AT RND*19+1,RND*29+1;" ": NEXT f: PRINT PAPER 3; INK 7;AT 0,16;"HIGH ";hs 40 BEEP 1,0: LET a=1: LET b=0: LET x=15: LET y=10: FOR f=9 TO 13: PRINT AT f,14;" ": NEXT f 50 LET i$=INKEY$: IF i$>"4" AND i$<"9" THEN LET a=(i$="6")-(i$="7"): LET b=(i$="8")-(i$="5"): BEEP .005,x 60 PRINT AT 0,0; INK 7; PAPER 3;"TIME ";t 70 FOR f=1 TO q: NEXT f 90 BEEP d,x: LET t=t+1: LET y=y+a: LET x=x+b: LET c=ATTR (y,x): PRINT AT y,x;"\p";AT y-a,x-b;".": IF c=48 THEN GO TO 50 100 LET du=.005: FOR f=1 TO 20: BEEP du,f: LET du=du+.003: NEXT f 110 LET l=l-1: PRINT PAPER 3;AT y,x;" ";AT 21,0; PAPER 3; INK 7;"LIVES ";l: IF L THEN GO TO 1000 120 PRINT #0; FLASH 1; INK 2; PAPER 6;"YOU ARE OUT OF LIVES": IF t>hs THEN PRINT AT 21,0; FLASH 1; PAPER 3; INK 7;"HIGH SCORE": LET hs=t 130 IF INKEY$<>"" THEN GO TO 130 140 IF INKEY$="" THEN GO TO 140 150 GO TO 10 1000 PRINT AT 21,16; FLASH 1; INK 2; PAPER 6;"HA,HA !": FOR y=1 TO 20: PRINT AT y,1; PAPER 8; INK 0;TAB 31;: NEXT y: FOR y=1 TO 15: PRINT AT RND*19+1,RND*29+1; PAPER RND*5;" ": NEXT y 1010 PRINT AT 21,16; PAPER 3;" ": GO TO 40 2000 RESTORE : FOR f=USR "p" TO USR "p"+7: READ a: POKE f,VAL ("BIN "+STR$ a): NEXT f: DATA 11000,a,111100,1011010,1011000,100100,a,0 2010 BORDER 0: PAPER 0: INK 6: CLS 2020 PRINT TAB 9;"DODGEMS"''" You (\p) must dodge your way through the maze,avoiding all obstacles & you mustn't cross your path."''" Move using the cursor keys." 2030 IF INKEY$<>"" THEN GO TO 2030 2040 IF INKEY$="" THEN GO TO 2040 2050 RETURN ```