Hangcat is a two-player word-guessing game in the Hangman tradition, where one player enters a secret word and the second player attempts to guess it letter by letter within nine lives. The program uses a dash-filled string `g$` as a mask that reveals correctly guessed letters in place, and tracks used letters in the string `u$` for display. A subroutine at line 2000 renders an animated title by copying character ROM data into two UDG slots (`\a` and `\b`) on a per-character basis, effectively scrolling custom-shaped glyphs across two display rows. BEEP sequences signal both incorrect guesses and a winning fanfare, and FLASH 1 highlights the win/loss message.
Program Analysis
Program Structure
The program is organized into three logical phases: setup (lines 5–95), the main game loop (lines 100–330), and a title-screen subroutine (lines 2000–2030). A SAVE statement at line 9999 acts as a standalone utility line for archiving the program with auto-run at line 1 (which falls through to line 5).
- Initialization (lines 10–95): Sets screen colors, calls the title subroutine, prompts Player 1 for a secret word, and builds the dash-mask string
g$. - Game loop (lines 100–330): Displays the current mask, lives remaining, used letters, and solicits a letter guess each iteration.
- Title subroutine (lines 2000–2030): Renders the title string using dynamically loaded UDGs.
Word Masking and Guess Matching
The secret word is stored in a$. The mask string g$ is initialized to a string of dashes equal in length to a$ (lines 60–90). When the player guesses a letter x$, lines 160–200 iterate over each character of a$; a match sets the flag f=1 and writes x$ directly into the corresponding position of g$ via LET g$(z)=x$ (line 190), exploiting Sinclair BASIC’s in-place string character assignment.
Win detection at line 260 is a simple string equality test: g$=a$. When all dashes are replaced by correct letters, the condition becomes true. The lives counter t starts at 9 and decrements by 1 for each incorrect guess (line 250).
UDG-Based Title Rendering (Subroutine 2000)
The most technically distinctive feature is the animated title. Line 2020 loops over every character in the title string m$. For each character, it calculates the ROM font address: c = 15616 + (8 * (CODE m$(a) - 32)). This is the standard TS2068/Spectrum formula for locating the 8-byte bitmap of a character in the ROM font (font starts at address 15616).
It then copies those 8 bytes into two UDG slots simultaneously — UDG \a and UDG \b — by writing each byte to both USR "a" + (b*2) and USR "a" + (b*2) + 1. This doubles the width of each glyph horizontally, producing a wider, blocky title character. Each character is then printed at row y and row y+1 using INVERSE 1, giving a two-row tall inverse-video title.
Key BASIC Idioms and Techniques
- In-place string assignment:
LET g$(z)=x$replaces a single character within a string, a Spectrum/TS2068 BASIC feature not available on all platforms. - Used-letters tracking:
u$starts as a single space and grows by concatenation each turn (line 145). The conditionIF u$<>" "at line 125 suppresses display until at least one guess has been made. - Centered text:
TAB 15-(LEN g$/2)(line 220) andAT y, (32-LEN m$)/2(line 2010) perform runtime centering calculations. - BEEP fanfares: A losing sequence uses three discrete
BEEPcalls (line 270); a winning sequence uses aFORloop ascending in pitch (line 280). - Input validation: Both the word input (line 55) and the letter input (line 142) reject empty strings with a redirect back to the
INPUTline.
Variable Summary
| Variable | Role |
|---|---|
a$ | Secret word entered by Player 1 |
g$ | Current guess mask (dashes replaced by correct letters) |
u$ | Accumulated string of letters guessed so far |
x$ | Current letter guess |
t | Lives remaining (starts at 9) |
f | Flag: 1 if last guess was correct, 0 otherwise |
m$ | Title string used in subroutine 2000 |
c | ROM font address for current title character |
d | UDG destination address during title rendering |
Bugs and Anomalies
- Used-letters string grows unbounded:
u$appends every guess including repeats, so a player who guesses the same letter twice will see it listed twice. There is no duplicate-guess check. - Line 290 layout: The comma after
a$beforeTAB(PRINT "..."-,;TAB...) produces a comma-separator print, which may cause unexpected column advancement depending on the current print position; the semicolon modifier on,;is redundant but harmless. - Play-again loop: Lines 320–330 poll
INKEY$in a tight loop but only check for"y"; pressing"n"does nothing and the loop continues indefinitely. The program can only be exited by pressingBREAKor entering"y". - UDG byte stride: The title subroutine writes each font byte to
USR "a" + (b*2), stepping by 2 rather than 1 through the UDG area. This means only bytes at even offsets fromUSR "a"are written, which spans into UDG\bterritory — intentional, as it populates both UDGs, but the approach is unusual and tightly coupled to UDG memory layout.
Content
Source Code
5 REM hangcat Version 3.0
10 BORDER 1: PAPER 1: INK 7: CLS
15 GO SUB 2000
20 PRINT AT 16,4;"Player 1 - Type in a word."
25 PRINT AT 18,5;"(Player 2 - DON'T LOOK!)"
50 INPUT a$
55 IF a$="" THEN GO TO 50
60 LET g$="-"
70 FOR z=1 TO LEN a$-1
80 LET g$=g$+"-"
90 NEXT z
95 LET u$=" "
100 LET t=9
110 CLS
111 PRINT : PRINT : PRINT AT 4,(32-LEN g$-1)/2;g$: PRINT : PRINT : PRINT
115 IF t<2 THEN PRINT " You have 1 life.": GO TO 123
120 PRINT " You have ";t;" lives."
123 PRINT : PRINT
125 IF u$<>" " THEN PRINT "Letters used=";u$
127 PRINT : PRINT
130 PRINT " GUESS A LETTER OF THE WORD."
140 INPUT x$
142 IF x$="" THEN GO TO 140
143 CLS
145 LET u$=u$+x$
150 LET f=0
160 FOR z=1 TO LEN a$
170 IF x$<>a$(z) THEN GO TO 200
180 LET f=1
190 LET g$(z)=x$
200 NEXT z
210 PRINT : PRINT : PRINT : PRINT
220 PRINT TAB 15-(LEN g$/2);g$
225 PRINT : PRINT : PRINT
230 IF f=0 THEN PRINT TAB 11;"INCORRECT"
240 IF f=1 THEN PRINT TAB 12;"CORRECT"
245 PRINT : PRINT
250 IF f=0 THEN LET t=t-1
260 IF t>0 AND g$<>a$ THEN GO TO 115
270 IF g$<>a$ THEN PRINT " "; FLASH 1;"YOU'RE A GONER.": BEEP 0.7,1: BEEP 0.5,1: BEEP 1,6
280 IF g$=a$ THEN PRINT " "; FLASH 1;"CONGRATULATIONS!": FOR x=1 TO 30: BEEP .01,x: NEXT x
285 PRINT : PRINT
290 PRINT " The correct word was-",;TAB 15-(LEN g$/2);a$
300 PRINT
310 PRINT " Want to play again? (y/n)"
320 IF INKEY$="y" THEN RUN
330 GO TO 320
2000 REM title
2010 LET m$=" H A N G C A T ": LET y=8: LET x=((32-LEN m$)/2)
2020 FOR a=1 TO LEN m$: LET c=15616+(8*(CODE m$(a)-32)): FOR b=0 TO 7: LET d=(USR "a"+(b*2)): POKE d,PEEK (c+b): POKE (d+1),PEEK (c+b): NEXT b: PRINT AT y,x; INVERSE 1;"\a";AT y+1,x;"\b": LET x=x+1: NEXT a
2030 RETURN
9999 SAVE "hangcat" LINE 1: BEEP 1,33
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
