--- title: "GRAVE-YARD" id: 71050 type: "computer_media" slug: "grave-yard" url: "http://localhost/computer_media/grave-yard/" markdown_url: "http://localhost/computer_media/grave-yard.md" published_at: "2026-08-27T00:43:04+00:00" modified_at: "2026-08-27T00:54:01+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/08/grave-yard.png" alt: "GRAVE-YARD screen" excerpt: "Guide your ghost through a graveyard arena, dodging a randomly wandering specter before a 60-second timer runs out — 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/GRAVE-YARD%20%28198x%29%28-%29%28TS2068%29%28US%29%28Program%29.zip" tsrun_member: "GRAVE-YARD (198x)(-)(TS2068)(US)(Program).tap" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/08/grave-yard.png" alt: "GRAVE-YARD screen" media_type_tags: "Game" --- # GRAVE-YARD GRAVE-YARD is a two-character chase game in which the player maneuvers a ghost sprite around a bordered arena while a second ghost wanders randomly, and both characters must avoid collision with the arena’s underscore walls. The player moves using keys 5–8 (the standard Spectrum directional keys), with the player sprite changing shape based on the direction of travel using one of four UDGs (A–D). A 60-unit countdown timer drives the game’s pace, displayed at the bottom of the screen alongside a score that increments by three each time the wandering ghost (UDG F) catches the player’s ghost, triggering a respawn at fixed coordinates. Six UDGs are defined at startup via a DATA-driven POKE loop targeting USR “A” through USR “F”, covering directional player sprites, a collision sprite (UDG E), and the enemy sprite (UDG F). The high score is retained across rounds within the same session and displayed on the game-over screen alongside a prompt to restart. *** ## Program Analysis ### Program Structure The program is organized into a main game loop with clearly separated initialization and setup subroutines: - `9000`–`9080`: UDG definition — reads pixel data and POKEs six UDGs (A–F) via a loop over `USR "A"` to `USR "F"+7`. - `8000`–`8990`: Game variable initialization — sets border/paper/ink, positions, score, timer, and initial sprite character. - `7000`–`7060`: Screen setup — draws a bordered arena using INVERSE INK 6 underscores and prints the score line. - `50`–`200`: Main game loop — handles player input, player movement, enemy movement, collision detection, and timer management. - `1000`–`1060`: Score/respawn handler — increments score, plays a beep sequence, and respawns both characters. - `2000`–`2050`: Game-over sequence — displays score, updates high score, and prompts replay. ### UDG Initialization Subroutine `9000` defines six UDGs (A through F) by reading 8 bytes each from DATA statements at lines `9030`–`9080` and POKEing them into the UDG area. The loop iterates from `USR "A"` to `USR "F"+7`, covering all 48 bytes. UDGs A–D are directional player sprites (left, right, down, up), UDG E is a collision/capture sprite, and UDG F is the enemy ghost sprite. ### Player Input and Movement Lines `51`–`70` implement a latching input scheme. `INKEY$` is only accepted if it falls in the range `"5"`–`"8"`; otherwise the last direction stored in `A$` is retained. Movement is computed using Boolean arithmetic: - `V` (row) changes by `(A$="6")-(A$="7")` — key 6 moves down, key 7 moves up. - `H` (column) changes by `(A$="8")-(A$="5")` — key 8 moves right, key 5 moves left. The previous position is saved in `V1`, `H1` before each move. If the new cell contains an underscore (wall), the move is cancelled by restoring `V1`, `H1` and clearing `A$` at line `90`. ### Directional Sprite Switching Line `110` updates the player’s display character `C$` based on the current direction key using concatenated Boolean string expressions — a common Spectrum BASIC idiom where a string multiplied by a true condition yields the string and by a false condition yields an empty string. The four cases map keys 5–8 to UDGs D, A, C, and B respectively. If no direction key is active (`C$=""`), the previous sprite `D$` is retained (line `120`). ### Enemy Ghost Movement The enemy ghost at coordinates `(J,K)` moves pseudo-randomly each frame (lines `150`–`180`). Its row and column each change by `INT(RND*3)-1`, giving a displacement of −1, 0, or +1. Like the player, it saves its prior position and cancels moves into wall cells. Collision with an empty cell (`SCREEN$=""`) at line `175` triggers the catch event at line `1000`. ### Collision Detection via SCREEN$ Both player and enemy use `SCREEN$` to detect the contents of the target cell before moving into it. An empty string result indicates the player and enemy have overlapped, triggering the score event at line `1000`. An underscore character indicates a wall boundary, blocking movement. ### Timer Mechanism The timer variable `TI` is initialized to 60 and decremented by 0.2 each main loop iteration at line `125`. This is a software timer driven by loop speed rather than the system clock. When `TI` drops below zero, the game-over routine at line `2000` is triggered. The display uses `INT TI` to show whole seconds. ### Bugs and Anomalies - Line `1060` sets `K=20` and then immediately overwrites it with `K=16` — the intent was almost certainly `H=16` to reset the player’s column, since `H` is the player’s horizontal coordinate. As written, `H` is never reset here, so the player respawns at whatever column they were at when the catch occurred. - Line `80` checks `SCREEN$(V,H)=""` after the player has already moved, meaning the check fires when the player moves into a cell occupied by the enemy — this is the intended catch condition, but the logic is inverted from what a reader might expect (empty screen = collision rather than a solid obstacle). - The `BEEP .008,SC` at line `130` pitches the movement sound to the current score, so the tone rises throughout the game as the score increases. ### Key Variable Summary | Variable | Purpose | | --- | --- | | `V`, `H` | Player row and column | | `V1`, `H1` | Previous player position (for wall revert) | | `J`, `K` | Enemy ghost row and column | | `J1`, `K1` | Previous enemy position (for wall revert) | | `A$` | Current/latched direction key | | `C$` | Current player UDG sprite character | | `D$` | Previous player UDG sprite (fallback) | | `SC` | Current score | | `HI` | Session high score | | `TI` | Countdown timer (software-driven) | ## Source Code ``` 10 REM "GRAVE-YARD" 20 GO SUB 9000:LET HI=0 30 GO SUB 8000 40 GO SUB 7000 50 PRINT AT V,H;" " 51 IF INKEY$<"5" OR INKEY$>"8" THEN GO TO 55 53 LET A$= INKEY$ 55 LET V1=V:LET H1=H 60 LET v=v+(a$="6")-(a$="7") 70 LET H=H+(A$="8")-(A$="5") 80 IF SCREEN$ (V,H)="" THEN GO TO 1000 90 IF SCREEN$ (V,H)="_" THEN LET V=V1:LET H=H1:LET A$="":GO TO 55 100 PRINT AT V,H; INK 3;C$ 105 LET D$=C$ 110 LET C$=("[UDG-A]" AND A$="7")+("[UDG-B]" AND A$="8")+("[UDG-C]" AND A$="6")+("[UDG-D]" AND A$="5") 120 IF C$="" THEN LET C$=D$ 125 PRINT AT 21,16;"TIME "; INT TI;" " :LET TI=TI-.2:IF TI<0 THEN GO TO 2000 130 BEEP .008,SC 140 PRINT AT J,K;" " 150 LET J1=J:LET K1=K 160 LET J=J+ INT (RND*3)-1:LET K=K+ INT (RND*3)-1 170 IF SCREEN$ (J,K)="_" THEN LET j=j1:LET K=K1:GO TO 150 175 IF SCREEN$ (J,K)="" THEN GO TO 1000 180 PRINT AT J,K; INK 5;"[UDG-F]" 200 GO TO 50 1000 PRINT AT J,K;"[UDG-E]" 1010 FOR A=1 TO 3 1020 LET SC=SC+1 1030 PRINT AT 21,0;"SCORE ";SC 1040 BEEP .7,SC 1050 NEXT A 1060 LET J=4:LET K=20:LET V=10:LET K=16:GO TO 40 2000 PRINT AT 21,16; PAPER 1;"TIME 0":BEEP 2,-10:CLS 2010 PRINT AT 2,12; PAPER 2;"GAME OVER" 2020 PRINT AT 5,10; PAPER 2;"YOU SCORED ";SC 2030 IF SC>HI THEN LET HI=SC 2040 PRINT AT 18,6; PAPER 6;"HIGHEST SCORE TODAY";HI 2050 INPUT PAPER 1;"PRESS "; PAPER 2;"ENTER"; PAPER 1;" TO PLAY AGAIN "; LINE A$:GO TO 30 7000 CLS :PRINT INVERSE 1; INK 6;"________________________________" 7010 FOR A=1 TO 19 7020 PRINT INVERSE 1; INK 6;"_"; AT A,31;"_" 7030 NEXT A 7040 PRINT INVERSE 1; INK 6;"________________________________" 7050 PRINT AT 21,0;"SCORE ";SC 7060 RETURN 8000 BORDER 0:PAPER 0:INK 9:CLS 8010 LET V=10:LET H=16 8020 LET J=4:LET K=20 8030 LET SC=0:LET TI=60 8040 LET C$="[UDG-A]" 8050 LET A$="" 8990 RETURN 9000 FOR A= USR "A" TO USR "F"+7 9010 READ USER:POKE A,USER 9020 NEXT A:RETURN 9030 DATA 60,153,255,153,24,189,255,189 9040 DATA 236,68,229,255,255,229,68,238 9050 DATA 189,255,189,24,153,255,153,60 9060 DATA 119,34,167,255,255,167,34,119 9070 DATA 56,124,238,198,238,238,254,254 9080 DATA 56,56,16,124,16,16,40,68 9998 REM A B C D E F 9999 REM [UDG-A] [UDG-B] [UDG-C] [UDG-D] [UDG-E] [UDG-F] ```