This program manages a single-elimination tournament bracket for up to 20 teams on a ZX81/TS1000. It shuffles teams into randomised matchups using a Fisher-Yates-style RND-seeded loop, tracks wins and losses across multiple rounds using two-character team codes packed into string arrays, and supports mid-tournament tape saves for continuation. The bracket display uses a two-column layout with TAB and comma print positioning, and inverse-video characters in the menu bar (B$(2)) are updated each round by adding 128 to character codes to produce inverse digits showing the current round number. A “bye” is automatically awarded to the odd team out when the field is not a power of two, and special logic at lines 600–740 detects and flags potential second/third-place tie situations.
Program Analysis
Program Structure
The program is divided into clearly labelled REM-delimited sections, broadly following this flow:
- Lines 1–30: Variable and array initialisation
- Lines 50–80: Static string constants (menu bar templates, error messages)
- Lines 100–230: Team name entry loop
- Lines 300–340: Subroutine to clear the round-order array
R() - Lines 400–590: Shuffle routine — randomises draw order into
R() - Lines 600–740: Tie-detection subroutines for 2nd/3rd place
- Lines 1000–1180: Display/menu — shows bracket, handles keypress commands
- Lines 2000–2980: Post-results entry — records winners and losers for the round
- Lines 3000–3220: New bracket generation — builds next round from W$ and L$
- Lines 4000–4050: Tape save routine
- Lines 9000–9010: Bootstrap SAVE and auto-RUN
Array and Variable Layout
| Variable | Dimensions | Purpose |
|---|---|---|
N$() | 22 × 16 | Team records; rows 1–20 are teams, 21–22 are message strings. Each row: cols 1–2 = two-char index, col 3 = status flag, cols 4–14 = team name padded to 11 chars. |
B$() | 6 × 32 | Static display strings: blank line, menu bar, header, error, results/new-bracket prompt, tie-team buffer. |
R() | 20 | Shuffled draw order — indices into N$() for the current round. |
W$() | 20 chars | Packed two-character team codes for round winners. |
L$() | 20 chars | Packed two-character team codes for round losers. |
RND | scalar | Round counter (1–3), also used to gate reshuffle availability. |
Team Encoding Scheme
At line 200, each team name is stored with a computed prefix: N$(N)=(STR$ (N+100))(2 TO )+" "+X$. Adding 100 to N guarantees a three-digit decimal string; slicing from position 2 gives a two-digit decimal index (e.g. team 1 → "01"). This two-digit code occupies columns 1–2, column 3 holds a status flag, and columns 4–14 hold the padded name. The status flag in column 3 is used to track elimination state: "%*" marks a first loss, "%E" marks elimination (second loss, if a double-elimination variant is implied), though the program generally implements single elimination.
Shuffle Algorithm
Lines 490–560 implement a rejection-sampling shuffle: for each position N in R(), a random integer in [1, NT] is drawn; the inner loop at 520–540 checks whether that value has already been assigned to any earlier slot. If a collision is found, the outer loop retries via GOTO 510. This is correct but has worst-case unbounded runtime for large NT; in practice, with at most 20 teams, it is acceptable.
Packed String Transport (W$ and L$)
Winners and losers are not stored as indices but as concatenated two-character strings in W$ and L$, each pre-initialised to 20 spaces (line 570–580). At lines 3040–3130, these are unpacked by repeatedly slicing the leading two characters and appending non-blank entries to X$, which is then split back into numeric indices for R() at lines 3150–3180. This avoids needing a second set of numeric arrays and keeps the data structures simple for the ZX81’s limited memory.
Inverse-Video Round Number in Menu Bar
Lines 1100–1110 update B$(2) in-place to show the current round number in inverse video. X$ is set to the two-digit string of RND+100 sliced as before, then CHR$ (CODE X$+128) and CHR$ (CODE X$(2)+128) write inverse-video digit characters directly into positions 10 and 11 of the menu bar string — an efficient way to embed live data into a pre-formatted display template without redrawing the whole line.
GOTO Arithmetic at Line 2650
The expression GOTO (4000 AND X=1)+(1000 AND X=2) uses the ZX81’s Boolean convention (true = 1, false = 0) to compute the destination line arithmetically. If X=1, the result is 4000+0=4000; if X=2, the result is 0+1000=1000. This is a well-known ZX81 BASIC idiom for conditional branching without IF.
Bye Handling
When NT is odd, the last team in the shuffled order (R(NT)) receives a bye. Line 1080 prints N$(21) (“DRAWS A “”BY”””) after the last unpaired team in the bracket display. Lines 2500–2570 detect the odd case and automatically award a win by default to that team, appending it directly to W$ before proceeding.
Tie and Special-Case Detection (Lines 600–740)
Subroutine 600 is called when NT=4 or NT=3 (lines 1085) to check whether two teams sharing the same elimination status (flag "%E") are paired in the draw, indicating a necessary third-place playoff. For exactly three teams remaining, lines 700–740 check all three pairings and print a note if any two have the same status, warning that a loser’s bracket match may be required to determine 2nd place.
Notable Bugs and Anomalies
- Line 1130–1180: The keypress loop reads
INKEY$multiple times in succession without a wait for key-up between tests. This means a single keypress onRmay satisfy both theRNDincrement test (line 1135) and theGOTO 400reshuffle (line 1140) in the same pass, or may be missed due to timing. The loop relies on the program being in SLOW mode, which helps. - Line 2420: The confirmation check is
IF X$<>"Y"(without checkingX$(1)as done at line 2320), so any input other than exactly"Y"will re-prompt, which is slightly inconsistent with line 2320’sX$(1)check. - Lines 420–480: The
RND<=2guard limits reshuffling to two times. IfRNDexceeds 2, the program prints an “ILLEGAL FUNCTION” error message (stored inB$(4)) and falls through to the menu at line 1120, which is an intentional design choice to limit bracket reshuffling. - The variable name
RNDshadows the built-inRNDfunction on the ZX81. All random number generation uses the system’sRNDkeyword, but because a numeric variable namedRNDis declared and used, expressions likeINT (RND*NT)+1(line 510) reference the variable, not the built-in — meaning the shuffle is entirely deterministic based on whatever the systemRNDfunction returns, unaffected by the round counter stored in the variable. This works correctly in context but is a potential source of confusion.
Content
Source Code
1 REM COPYRIGHT (C) SEPT.1983BY JEFFREY MOORE
10 DIM N$(22,16)
15 DIM B$(6,32)
20 DIM R(20)
25 DIM W$(20)
30 DIM L$(20)
50 LET RND=1
55 LET B$(1)=" "
60 LET B$(2)="% %R%N%D%.%N%O%.%:% % % % %R%=%R%E%S%H%U%F%F%L%E% %C%=%C%O%P%Y% "
65 LET B$(3)="% % % % %T%E%A%M% % % % % %V%S%.% % % % % %T%E%A%M% % % % % % % "
70 LET B$(4)="ILLEGAL FUNCTION"
75 LET B$(5)="% % %P%=%P%O%S%T% %R%E%S%U%L%T%S% %N%=%N%E%W% %B%R%A%C%K%E%T% % "
80 LET N$(21)="DRAWS A ""BY"""
85 LET N$(22)="NAME TOO LONG"
100 REM ENTER TEAM DATA
105 CLS
110 PRINT AT 19,0;"ENTER THE NUMBER OF TEAMS COM- PETING IN THIS TOURNAMENT. (20 TEAMS MAX.)"
115 INPUT NT
120 IF NT<0 OR NT>20 THEN GOTO 115
125 CLS
130 PRINT AT 18,0;"ENTER TEAM NAMES AS PROMPTED (11 LETTERS MAX.)"
135 PRINT
140 PRINT "% %T%E%A%M% %N%O%.% % % % % % %T%E%A%M% %N%A%M%E% % % % % % % % "
145 FOR N=1 TO NT
150 SCROLL
155 PRINT " ";N;TAB 12;
160 INPUT X$
165 IF LEN X$<=11 THEN GOTO 200
170 PRINT N$(22)
175 FOR X=1 TO 60
180 NEXT X
185 PRINT AT 21,0;B$(1)
190 PRINT AT 20,31;" "
195 GOTO 155
200 LET N$(N)=(STR$ (N+100))(2 TO )+" "+X$
205 PRINT N$(N,4 TO 14)
210 SCROLL
215 NEXT N
220 FOR N=1 TO 60
225 NEXT N
230 FAST
235 CLS
240 GOTO 400
300 REM CLEAR R
310 FOR N=1 TO NT
320 LET R(N)=0
330 NEXT N
340 RETURN
400 REM SHUFFLE
410 FAST
420 IF RND<=2 THEN GOTO 490
430 SLOW
440 PRINT AT 20,0;B$(4)
450 PRINT B$(1)
460 FOR N=1 TO 60
465 NEXT N
470 LET RND=RND-1
480 GOTO 1120
490 GOSUB 300
500 FOR N=1 TO NT
510 LET X=INT (RND*NT)+1
520 FOR Y=1 TO NT
530 IF R(Y)=X THEN GOTO 510
540 NEXT Y
550 LET R(N)=X
560 NEXT N
570 LET W$=B$(1, TO 20)
580 LET L$=W$
590 GOTO 1000
600 REM 2ND.OR 3RD.PLACE TIE
610 IF NT=3 THEN GOTO 700
620 FOR N=1 TO NT-1
630 FOR X=N+1 TO NT
640 IF N$(R(N),3)="%E" AND N$(R(X),3)="%E" THEN GOTO 680
650 NEXT X
660 NEXT N
670 RETURN
680 LET B$(6)=N$(R(N),4 TO 14)+" AND "+N$(R(X),4 TO 14)
690 PRINT AT 12,0;B$(6);"MUST PLAY EACH OTHER NEXT ROUND TO DETERMINE THIRD PLACE."
695 RETURN
700 IF N$(R(1),3)=N$(R(2),3) OR N$(R(1),3)=N$(R(3),3) THEN GOTO 730
710 IF N$(R(2),3)=N$(R(3),3) THEN GOTO 730
720 RETURN
730 PRINT AT 12,0;"% %N%O%T%E%:% IT MAY BE NECESSARY FOR THE LOSER OF THIS ROUND TO PLAY THE LOSER OF NEXT ROUND TO DETERMINE 2ND.PLACE WINNER."
740 RETURN
1000 REM DISPLAY/MENU
1010 CLS
1020 SLOW
1030 PRINT B$(3)
1040 FOR N=1 TO NT
1050 PRINT N$(R(N), TO 14),
1060 IF INT (N/2)=N/2 THEN PRINT ,,
1070 NEXT N
1080 IF INT (NT/2)<>NT/2 THEN PRINT N$(21)
1085 IF NT=4 OR NT=3 THEN GOSUB 600
1086 IF B$(6)<>B$(1) AND L$=B$(1, TO 20) AND NT=2 THEN PRINT AT 12,0;B$(6);"MUST ""BATTLE"" THIS ROUND TO BREAK THEIR TIE."
1090 LET X$=(STR$ (RND+100))(2 TO )
1100 LET B$(2,10)=CHR$ (CODE X$+128)
1110 LET B$(2,11)=CHR$ (CODE X$(2)+128)
1120 PRINT AT 20,0;B$(2);B$(5)
1130 IF INKEY$="" THEN GOTO 1130
1135 IF INKEY$="R" AND RND<=2 THEN LET RND=RND+1
1140 IF INKEY$="R" THEN GOTO 400
1150 IF INKEY$="P" THEN GOTO 2000
1160 IF INKEY$="N" THEN GOTO 3000
1170 IF INKEY$="C" THEN COPY
1180 GOTO 1130
2000 REM POST RESULTS
2010 CLS
2020 SLOW
2030 LET Y=1
2040 PRINT AT 11,0;"ENTER THE NAME OF THE WINNER OF EACH MATCH AS PROMPTED TO DO SO."
2050 FOR N=1 TO 50
2060 NEXT N
2070 FOR N=1 TO (NT-1) STEP 2
2080 CLS
2090 PRINT B$(3)
2100 PRINT
2110 PRINT N$(R(N), TO 14),N$(R(N+1), TO 14)
2120 PRINT
2130 PRINT "% % %T%H%E% %W%I%N%N%E%R% %O%F% %T%H%I%S% %M%A%T%C%H% %I%S%?% % "
2140 PRINT
2150 INPUT X$
2160 LET X=LEN X$+3
2170 IF X<=14 THEN GOTO 2200
2180 PRINT N$(22)
2190 GOTO 2930
2200 IF X$<>N$(R(N),4 TO X) AND X$<>N$(R(N+1),4 TO X) THEN GOTO 2900
2210 IF X$=N$(R(N),4 TO X) AND X$=N$(R(N+1),4 TO X) THEN GOTO 2930
2220 PRINT
2230 IF X$=N$(R(N+1),4 TO X) THEN GOTO 2400
2300 REM N$(R(N)=WIN
2305 PRINT N$(R(N), TO 14),"IS THE WINNER"
2310 PRINT "OF THIS MATCH. O.K.?(Y/N)"
2315 INPUT X$
2320 IF X$(1)<>"Y" THEN GOTO 2950
2325 LET W$(Y TO Y+1)=N$(R(N), TO 2)
2330 IF N$(R(N+1),3)="%*" THEN LET N$(R(N+1),3)="%E"
2335 IF N$(R(N+1),3)="%E" THEN GOTO 2350
2340 LET N$(R(N+1),3)="%*"
2345 LET L$(Y TO Y+1)=N$(R(N+1), TO 2)
2350 LET Y=Y+2
2355 NEXT N
2360 GOTO 2500
2400 REM N$(R(N+1)=WIN
2405 PRINT N$(R(N+1), TO 14),"IS THE WINNER"
2410 PRINT "OF THIS MATCH. O.K.?(Y/N)"
2415 INPUT X$
2420 IF X$<>"Y" THEN GOTO 2950
2425 LET W$(Y TO Y+1)=N$(R(N+1), TO 2)
2430 IF N$(R(N),3)="%*" THEN LET N$(R(N),3)="%E"
2435 IF N$(R(N),3)="%E" THEN GOTO 2450
2440 LET N$(R(N),3)="%*"
2445 LET L$(Y TO Y+1)=N$(R(N), TO 2)
2450 LET Y=Y+2
2455 NEXT N
2460 GOTO 2500
2500 IF INT (NT/2)=NT/2 THEN GOTO 2580
2510 CLS
2520 PRINT B$(3)
2530 PRINT
2540 PRINT N$(R(NT), TO 14),N$(21)
2550 PRINT
2560 PRINT N$(R(NT),4 TO 14);"IS DECLARED A WINNER BY DEFAULT."
2570 LET W$(Y TO Y+1)=N$(R(NT), TO 2)
2580 PRINT
2590 PRINT
2600 PRINT "YOU MAY DESIRE TO MAKE A TAPE OFYOUR DATA THUS FAR TO CONTINUE LATER OR AS A BACK-UP."
2610 PRINT
2620 PRINT "% % %E%N%T%E%R% %1% %F%O%R% %S%A%V%E% %R%O%U%T%I%N%E%,%2% %T%0% % %R%E%T%U%R%N% %T%O% %T%H%E% %D%I%S%P%L%A%Y% %A%N%D% %M%E%N%U% "
2630 INPUT X
2640 IF X<>1 AND X<>2 THEN GOTO 2630
2650 GOTO (4000 AND X=1)+(1000 AND X=2)
2900 REM ERROR MESS.
2910 PRINT "ENTRY ERROR,PLEASE TRY AGAIN."
2920 GOTO 2960
2930 PRINT "PLEASE ENTER TEAM NAME AS IT APPEARS ON THE CRT SREEN."
2940 GOTO 2960
2950 PRINT ,,"O.K.,WE""LL TRY AGAIN."
2960 FOR X=1 TO 60
2970 NEXT X
2980 GOTO 2080
3000 REM NEW BRACKET GEN.
3010 FAST
3020 GOSUB 300
3030 LET X$=""
3040 FOR N=1 TO 10
3050 IF W$( TO 2)=" " THEN GOTO 3070
3060 LET X$=X$+W$( TO 2)
3070 LET W$=W$(3 TO )
3080 NEXT N
3090 FOR N=1 TO 10
3100 IF L$( TO 2)=" " THEN GOTO 3120
3110 LET X$=X$+L$( TO 2)
3120 LET L$=L$(3 TO )
3130 NEXT N
3140 LET NT=LEN X$/2
3150 FOR N=1 TO NT
3160 LET R(N)=VAL X$( TO 2)
3170 LET X$=X$(3 TO )
3180 NEXT N
3190 LET W$=B$(1, TO 20)
3200 LET L$=W$
3210 LET RND=RND+1
3220 GOTO 1000
4000 REM SAVE ROUTINE
4010 CLS
4020 PRINT AT 11,0;"MOUNT TAPE IN RECORDER AND SET CONTROLS FOR ""RECORD"".KEY ""S"" WHEN READY TO SAVE."
4030 IF INKEY$<>"S" THEN GOTO 4030
4040 IF INKEY$="S" THEN SAVE "TOURNAMEN%T"
4050 GOTO 1000
9000 SAVE "TOURNAMEN%T"
9010 RUN 1
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.


