--- title: "Tournament" id: 56739 type: "computer_media" slug: "tournament" url: "http://localhost/computer_media/tournament/" markdown_url: "http://localhost/computer_media/tournament.md" published_at: "2024-09-22T23:31:59+00:00" modified_at: "2026-04-03T07:58:52+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/09/SCR-20241102-kmiz.png" excerpt: "A full tournament bracket manager handles up to 20 teams, randomised seedings, automatic byes, tie detection, and mid-competition tape saves across multiple rounds." 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 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" indiv: - name: "Jeffrey Moore" slug: "jeffrey-moore" taxonomy: "indiv" url: "http://localhost/indiv/jeffrey-moore/" genre: - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_type: "Program" programmers: - name: "Jeffrey Moore" slug: "jeffrey-moore" taxonomy: "indiv" url: "http://localhost/indiv/jeffrey-moore/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Tournament%20%281983%29%28Moore%2C%20Jeffrey%29%28TS1000%29%28US%29%28Cassette%29.zip" mediadate: "1983" images: - url: "http://localhost/wp-content/uploads/2024/09/SCR-20241102-kmiz.png" - url: "http://localhost/wp-content/uploads/2024/09/SCR-20241102-klnz.png" - url: "http://localhost/wp-content/uploads/2024/09/Tournament.jpg" media_type_tags: "Game" --- 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: 1. Lines 1–30: Variable and array initialisation 2. Lines 50–80: Static string constants (menu bar templates, error messages) 3. Lines 100–230: Team name entry loop 4. Lines 300–340: Subroutine to clear the round-order array `R()` 5. Lines 400–590: Shuffle routine — randomises draw order into `R()` 6. Lines 600–740: Tie-detection subroutines for 2nd/3rd place 7. Lines 1000–1180: Display/menu — shows bracket, handles keypress commands 8. Lines 2000–2980: Post-results entry — records winners and losers for the round 9. Lines 3000–3220: New bracket generation — builds next round from W$ and L$ 10. Lines 4000–4050: Tape save routine 11. 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 on `R` may satisfy both the `RND` increment test (line 1135) and the `GOTO 400` reshuffle (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 checking `X$(1)` as done at line 2320), so any input other than exactly `"Y"` will re-prompt, which is slightly inconsistent with line 2320’s `X$(1)` check. - Lines 420–480: The `RND<=2` guard limits reshuffling to two times. If `RND` exceeds 2, the program prints an “ILLEGAL FUNCTION” error message (stored in `B$(4)`) and falls through to the menu at line 1120, which is an intentional design choice to limit bracket reshuffling. - The variable name `RND` shadows the built-in `RND` function on the ZX81. All random number generation uses the system’s `RND` keyword, but because a numeric variable named `RND` is declared and used, expressions like `INT (RND*NT)+1` (line 510) reference the variable, not the built-in — meaning the shuffle is entirely deterministic based on whatever the system `RND` function returns, unaffected by the round counter stored in the variable. This works correctly in context but is a potential source of confusion. ## 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 ```