Gladiator is a two-player combat game in which two sprite characters fight each other on a bordered arena screen, each controlled by a pair of keys (Z/X for Player 1, N/M for Player 2). The program uses eight-row by six-column string arrays (A$–H$) to store multiple sprite frames for each gladiator: standing poses, attacking poses, and death animations, all drawn using ZX81 block graphics characters. Collision detection is handled by checking whether the two players’ column positions differ by exactly six (line 1240), which triggers the attack subroutine at line 2000. Hit resolution is randomised with a 50% probability (RND*10 > 5), reducing each player’s hit counter from 9 to 0, and when either reaches zero the corresponding death animation and scrolling victory message are displayed.
Program Analysis
Program Structure
The program is organised into clearly separated regions, navigated primarily by GOTO rather than structured subroutines:
- Lines 1–2: REM header and immediate
GOTO 7000to bypass all game code on first run. - Lines 450–999: Screen setup routine — draws the arena border, score display, and title graphic using block graphics strings.
- Lines 1000–1300: Main game loop — renders both gladiator sprites at their current column positions, reads keypresses, enforces boundary limits, and checks for combat range.
- Lines 2000–2500: Attack subroutine — draws attack-pose sprites for both fighters, applies random hit logic, updates scores, and checks for death conditions.
- Lines 3000–4000 / 5000–5100: Death sequences for Player 2 and Player 1 respectively, showing a flashing kill message before jumping to the end screen.
- Lines 7000–7186: Initialisation — sets starting positions and scores, then
DIMs and populates all eight sprite arrays. - Lines 8000–9220: End/title screen — scrolls the display clear, draws a decorated title panel, shows instructions, and waits for a keypress before restarting.
Sprite System
All character graphics are stored as fixed-length string arrays, each row being six characters wide and eight rows tall, using ZX81 block graphic characters embedded directly in string literals. Eight separate arrays cover the different animation states:
| Array | Purpose | Rows used |
|---|---|---|
A$(8,6) | Player 1 standing | 1–8 |
B$(8,6) | Player 2 standing | 1–8 |
C$(6,6) | Player 1 attacking | 4–6 |
D$(6,6) | Player 2 attacking | 4–6 |
E$(8,6) | Player 2 dying | 4–8 |
F$(8,6) | Player 1 dying | 4–8 |
G$(8,6) | Player 1 victory | 1–8 |
H$(8,6) | Player 2 victory | 1–8 |
Only a subset of rows in the attack and death arrays are explicitly populated; the others remain as spaces (the default after DIM), which effectively erases the upper body when those frames are drawn — a deliberate cropping technique rather than a bug.
Main Game Loop
The loop at lines 1000–1300 is tight and unconditional. It redraws both sprites every iteration regardless of input, then samples INKEY$ four times in sequence (lines 1040–1055) for the four movement keys. Because each IF INKEY$= is a separate statement, only one key can be acted upon per pass, and holding multiple keys will only register one. Boundary clamping follows immediately (lines 1200–1260), and the loop ends with an unconditional GOTO 1000.
Collision and Combat Detection
Combat is triggered exclusively by line 1240: IF Y=Z-6 THEN GOSUB 2000. This means the attack subroutine fires only when Player 1’s column position Y is exactly six less than Player 2’s column Z. There is no symmetric check (i.e. Player 2 moving left into Player 1 does not trigger combat), which means combat can only occur with Player 1 to the left of Player 2 at that precise distance — a significant gameplay asymmetry.
Hit Resolution
Within the attack subroutine, each hit is resolved independently with a 50 % probability:
K = INT((RND*10)+1)→ ifK>5, Player 1 loses a hit point (V).N = INT((RND*10)+1)→ ifN>5, Player 2 loses a hit point (J).
Both players start with 9 hit points. The subroutine does not return normally to the main loop — after updating scores and checking for death, control falls through to GOTO 1030 (line 2500), bypassing the boundary check at lines 1200–1260 for that iteration.
Death and End-Game Sequences
When a player’s hit counter reaches zero, the program jumps directly to a death animation block. A FOR N=1 TO 40 loop alternates between printing the kill message in normal and inverse video, creating a flashing effect. Both death routines then jump to line 8000, the shared end screen, which uses SCROLL in a loop to wipe the display before showing the title/instructions screen and waiting on IF INKEY$="" THEN GOTO 9192.
Screen Layout
The arena is drawn with a decorative border using block graphic characters (▀ top, ▄ bottom, ▌ sides) at lines 500–900. The title graphic at lines 911–915 spells out “GLADIATOR” in inverse characters surrounded by block art. Player scores are printed at fixed positions (AT 2,10 and AT 2,26) and refreshed only when a hit occurs, not every game loop iteration.
Notable Techniques and Idioms
- The entry point bypass (
GOTO 7000at line 2) ensures initialisation always runs before game code, while keeping the functional game code at lower line numbers for faster lookup. - The end screen uses
FAST(line 8025) before the heavy block-graphic printing andSLOW(line 8100) before the text display, managing display speed deliberately. - Lines 9200–9220 (
CLEAR/SAVE/RUN) are utility lines for saving the program with auto-run, not part of normal execution flow. - The flashing kill-message technique (alternating normal/inverse strings in a tight loop) is a common ZX81 animation idiom requiring no timing hardware.
Bugs and Anomalies
- Line 9195 jumps to line 3, which does not exist. This is a deliberate technique — on the ZX81,
GOTOto a non-existent line number jumps to the next higher line, which here is line 450 (the screen setup routine), effectively restarting the game without re-initialising scores or positions. - The attack-range check (
Y=Z-6) is one-directional; if Player 2 moves to within range from the left side, no combat occurs. - Line 7178 contains
%"\ :— a literal quote character inside the string literal forH$(4), likely a data entry error producing unexpected graphics in that sprite row. - Line 7180 contains
%$\'and line 7182 contains\##— the$and##sequences are not standard zmakebas escapes, suggesting possible corruption or non-standard characters in those sprite rows. - The attack subroutine is called with
GOSUB 2000but exits viaGOTO 1030rather thanRETURN, making it effectively a jump rather than a true subroutine — theGOSUBstack entry is never cleared, which will cause a stack growth issue over a long game.
Content
Source Code
1 REM %S%E%T% %A%L%L% %V%A%L%U%E%S
2 GOTO 7000
450 REM %S%C%R%E%E%N% %S%E%T% %U%P
500 PRINT AT 0,0;"% ''''''''''''''''''''''''''''% ''''''''''''''''''''''''''''''% "
510 FOR N=1 TO 20
520 PRINT ": :"
530 NEXT N
540 PRINT "% ............................% ..............................% "
550 PRINT AT 20,1;" KEYS:(Z)-(X) : KEYS:(N)-(M)"
600 PRINT AT 3,0;"% ............................% ..............................% "
610 PRINT AT 1,15;" :";AT 2,15;" :"
650 PRINT AT 1,4;"%P%L%A%Y%E%R% %1";AT 1,20;"%P%L%A%Y%E%R% %2";AT 2,4;"HITS= ";V;AT 2,20;"HITS= ";J
900 PRINT AT 19,0;"% ''''''''''''''''''''''''''''% ''''''''''''''''''''''''''''''% "
911 PRINT AT 5,0;": ....% % % % % % % % % % % % % .... :"
912 PRINT AT 6,0;": ....% % % % % % %G%L%A%D%I%A%T%O%R% % % % % % .... :"
913 PRINT AT 7,0;":...% % % % % % % % ''''''''''''''''''''''''% % % % % % % % ...:"
914 PRINT AT 8,0;"% % % % % % % % % % % % % % "
915 PRINT AT 9,0;"% '''''' ''''''% "
999 REM %S%T%A%R%T% %O%F% %G%A%M%E
1000 FOR I=1 TO 8
1010 PRINT AT 10+I,Y;A$(I);AT 10+I,Z;B$(I)
1012 NEXT I
1030 REM %M%O%V%E%M%E%N%T
1040 IF INKEY$="X" THEN LET Y=Y+1
1045 IF INKEY$="Z" THEN LET Y=Y-1
1050 IF INKEY$="M" THEN LET Z=Z+1
1055 IF INKEY$="N" THEN LET Z=Z-1
1100 REM %M%O%V%E%M%E%N%T% %C%H%E%C%K
1200 IF Y<1 THEN LET Y=1
1220 IF Y>19 THEN LET Y=19
1240 IF Y=Z-6 THEN GOSUB 2000
1250 IF Z<1 THEN LET Z=1
1260 IF Z>24 THEN LET Z=24
1300 GOTO 1000
1900 REM %A%T%T%A%C%K% %S%T%A%R%T
2000 FOR I=1 TO 6
2010 PRINT AT 10+I,Y;C$(I);AT 10+I,Z;D$(I)
2020 NEXT I
2021 REM %S%E%E% %I%F% %H%I%T
2022 LET K=INT ((RND*10)+1)
2023 IF K>5 THEN LET V=V-1
2024 PRINT AT 2,10;V
2026 IF V=0 THEN GOTO 5000
2100 LET N=INT ((RND*10)+1)
2109 IF N>5 THEN LET J=J-1
2110 PRINT AT 2,26;J
2120 IF J=0 THEN GOTO 3000
2500 GOTO 1030
3000 REM %P%L%A%Y%E%R% %2% %D%I%E%S
3094 FOR I=1 TO 8
3960 PRINT AT 10+I,Z;E$(I);AT 10+I,Y;G$(I)
3980 NEXT I
3985 FOR N=1 TO 40
3990 PRINT AT 20,2;" PLAYER 1 HAS KILLED YOU "
3994 PRINT AT 20,2;" %P%L%A%Y%E%R% %1% %H%A%S% %K%I%L%L%E%D% %Y%O%U "
3998 NEXT N
4000 GOTO 8000
4999 REM %P%L%A%Y%E%R% %1% %D%I%E%S
5000 FOR I=1 TO 8
5010 PRINT AT 10+I,Y;F$(I);AT 10+I,Z;H$(I)
5030 NEXT I
5040 FOR N=1 TO 40
5050 PRINT AT 20,2;" PLAYER 2 HAS KILLED YOU "
5060 PRINT AT 20,2;" %P%L%A%Y%E%R% %2% %H%A%S% %K%I%L%L%E%D% %Y%O%U "
5070 NEXT N
5100 GOTO 8000
7000 LET Z=20
7002 LET Y=6
7004 LET V=9
7006 LET J=9
7009 REM %D%I%M% %A%L%L% %A%R%R%A%Y%S
7010 DIM A$(8,6)
7012 LET A$(1)=" : "
7014 LET A$(2)=" : "
7016 LET A$(3)=" ':' "
7018 LET A$(4)=" : :: "
7020 LET A$(5)=" '% . "
7022 LET A$(6)=" %K'' "
7024 LET A$(7)=" :'': "
7026 LET A$(8)=" :'' '' "
7030 DIM B$(8,6)
7032 LET B$(1)=" : "
7034 LET B$(2)=" : "
7036 LET B$(3)=" ':' "
7038 LET B$(4)=" :: : "
7040 LET B$(5)=" . % ' "
7042 LET B$(6)=" ''%L "
7044 LET B$(7)=" :'': "
7046 LET B$(8)=" '' '': "
7050 DIM C$(6,6)
7056 LET C$(4)=" % "
7058 LET C$(5)=" ':: . "
7060 LET C$(6)=" ::'~~~~"
7070 DIM D$(6,6)
7072 LET D$(4)=" % "
7074 LET D$(5)=" . ::' "
7076 LET D$(6)="~~~~':: "
7080 REM %D%E%A%T%H% %M%O%V%E%S
7090 DIM E$(8,6)
7095 LET E$(4)="% . "
7097 LET E$(5)=" '% "
7098 LET E$(6)=" '% "
7100 LET E$(7)=" :: "
7102 LET E$(8)=" ' "
7120 DIM F$(8,6)
7122 LET F$(4)=" .% "
7124 LET F$(5)=" % ' "
7126 LET F$(6)=" % ' "
7128 LET F$(7)=" :: "
7130 LET F$(8)=" ' "
7140 REM %H%E%R%O% %M%O%V%E%S
7150 DIM G$(8,6)
7152 LET G$(1)=" : "
7154 LET G$(2)=" : "
7156 LET G$(3)=" . ':' "
7158 LET G$(4)=" : % : "
7160 LET G$(5)=" '%.' "
7162 LET G$(6)=" .%.. "
7164 LET G$(7)=" : : "
7166 LET G$(8)=" '' ' "
7170 DIM H$(8,6)
7172 LET H$(1)=" : "
7174 LET H$(2)=" : "
7176 LET H$(3)=" ':' "
7178 LET H$(4)=" : %" : "
7180 LET H$(5)=" '%$' "
7182 LET H$(6)=" .##. "
7184 LET H$(7)=" : : "
7186 LET H$(8)=" ' '' "
8000 FOR N=1 TO 22
8010 SCROLL
8020 NEXT N
8025 FAST
8030 PRINT AT 0,0;"% % ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;% % "
8040 PRINT AT 1,0;"% % ########################################################% % "
8050 FOR N=1 TO 18
8060 PRINT "% ############################################################% "
8070 NEXT N
8080 PRINT AT 20,0;"% % ########################################################% % "
8090 PRINT AT 21,0;"% % !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!% % "
8100 SLOW
8110 PRINT AT 3,10;"*%W%E%L%C%O%M%E% %T%O*";AT 5,10;"*%G%L%A%D%I%A%T%O%R%S*"
8120 PRINT AT 7,2;"THIS IS A GRAPHICAL GAME OF ";AT 8,2;" COMBAT BETWEEN TWO "
8130 PRINT AT 9,2;"GLADIATORS IT CAN BE PLAYED ";AT 10,2;"IN TWO DIFFERENT WAYS EITHER"
8140 PRINT AT 11,2;"1) YOU AGAINST THE COMPUTER ";AT 12,2;"2) YOU AGAINST A FRIEND "
8150 PRINT AT 13,2;"NOTE YOU CAN CHANGE SIDES AT";AT 14,2;"AT ANY TIME "
8160 PRINT AT 15,2;"TO MOVE GLADIATOR ""K"" USE ";AT 16,2;"KEYS (Z)-(X) "
8170 PRINT AT 17,2;"TO MOVE GLADIATOR ""L"" USE "
8180 PRINT AT 18,2;"KEYS (N)-(M) "
8190 PRINT AT 20,3;"%P%R%E%S%S% %A%N%Y% %K%E%Y% %T%O% %C%O%N%T%I%N%U%E%."
9192 IF INKEY$="" THEN GOTO 9192
9195 GOTO 3
9200 CLEAR
9210 SAVE "1028%4"
9220 RUN 7000
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
