--- title: "Boggle" id: 64749 type: "computer_media" slug: "boggle" url: "http://localhost/computer_media/boggle/" markdown_url: "http://localhost/computer_media/boggle.md" published_at: "2026-03-07T14:54:45+00:00" modified_at: "2026-03-30T21:29:15+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/03/boggle-1.png" excerpt: "A three-language Boggle clone that generates random letter dice, times two players simultaneously, and scores words by length—with full Spanish and German character support via custom UDGs." 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_contents: - id: 64715 title: "Miscellaneous Programs" type: "computer_media" url: "http://localhost/computer_media/miscellaneous-programs/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2026/03/boggle-1.png" - url: "http://localhost/wp-content/uploads/2026/03/boggle-2.png" media_type_tags: "Game" --- # Boggle This program implements a multiplayer word game (Boggle) in three languages—English, German, and Spanish—selectable at runtime via the variable L(R). The game generates a 4×4 grid of letter dice, displays a countdown timer from 9000 units, and accepts words from two players in turn, scoring them by length (4-letter words score 1, up to 10+ letters scoring 39). Custom UDG characters are defined for accented and special letters (Ñ, Ü, Ö, LL, RR, Ch) needed for the Spanish and German modes, with the Spanish Ñ represented internally as CHR$ 157. Multiple rounds are supported, with scores tracked across rounds in arrays X(R) and Y(R). *** ## Program Structure The program is organised into functional blocks rather than a strict top-down flow. The entry point at line 5 sets round number `R=1` and jumps to line 800 (not shown, but expected to handle initialisation and the title/setup sequence). Key functional regions are: 1. **Lines 5–40:** Display routines for the word grid and timer (`GO SUB 20`, `GO SUB 25`). 2. **Lines 50–85:** Dice generation — shuffle and assign letter faces using arrays `A()`, `B()`, `D$()`, and `C$()`. 3. **Lines 90–165:** Main game loop — countdown timer `T` from 9000 to 0, keypress detection, and switching between OVER modes for display. 4. **Lines 200–240:** Word entry and validation for the active player. 5. **Lines 250–260:** Grid line drawing using `PLOT`/`DRAW`. 6. **Lines 300–325:** Player 2 setup after Player 1’s turn ends. 7. **Lines 330–435:** Score reduction input in each language (English 330–355, German 370–395, Spanish 410–435). 8. **Lines 550–565:** Word list swap/restore routine using sentinel value `"QKZ"`. 9. **Lines 600–739:** Scoring, display, and round management. 10. **Lines 810–855:** UDG definition via `POKE USR` and `READ`/`DATA`. 11. **Lines 920–2500:** Variable initialisation, language string setup, and dimension statements. ## UDG Definitions Eight custom UDGs are defined at line 810 by poking 8-byte bitmaps into `USR "x"` addresses. These provide special characters needed for multilingual play: | UDG Char | Purpose | | --- | --- | | `"N"` | Ñ (Spanish) | | `"C"` | Ç or Ch digraph indicator | | `"O"` | Ö (German) | | `"U"` | Ü (German) | | `"J"` | Horizontal rule / separator glyph | | `"H"` | Ch digraph symbol | | `"R"` | RR digraph symbol | | `"L"` | LL digraph symbol | In the `A$` letter distribution string for Spanish (line 1950), the digraphs Ch, LL, RR are encoded as `\h`, `\l`, `\r` (UDGs H, L, R), and Ñ as `\n` (UDG N). Line 32 handles display of these multi-character tiles specially, printing “Ch”, “LL”, or “rr” in place of the single UDG code. ## Dice Generation Algorithm The shuffling routine (lines 50–85) uses a probabilistic chain to assign one of 16 dice positions to each of 16 slots without replacement. Array `B()` tracks which slots have been assigned, and `A()` accumulates the order. This is not a standard Fisher-Yates shuffle; it relies on repeated random probing and may theoretically loop for long periods if many collisions occur, though in practice convergence is fast for 16 elements. Each die face is then selected at line 85: `D$(I)` holds a 6-character face string for die `I`, and a random face index `N` is chosen, setting `C$(I)` to one letter. ## Scoring System Words are scored by length at lines 618 and 654 using a cumulative `AND` idiom: - 4 letters → 1 point - 5 letters → 2 points - 6 letters → 3 points - 7 letters → 7 points - 8 letters → 17 points - 9 letters → 36 points - 10 letters → 75 points (cumulative sum) Words found by both players (joint words) are marked with sentinel `"QKZ"` in `F$()` and excluded from scoring. The penalty input (lines 330–435) allows referees to deduct from either player’s score for invalid words. ## OVER Technique for Two-Player Hiding During Player 1’s turn, words are printed with `PRINT OVER 1` to XOR them onto the screen, making them invisible against a pre-drawn background. `OVER 0` writes normally. The variable `O` toggles between these modes, and `GO SUB 550` toggles the visibility of all entered words so Player 2 cannot see Player 1’s words and vice versa. ## Multilingual String Construction All UI strings are assembled at lines 1960–1990 using the Sinclair BASIC idiom of string multiplication by boolean condition, e.g.: `LET W$=("WORDS" AND L(R)=1)+("PALABRAS" AND L(R)=3)+("Wo"+CHR$ 8+"\oRTER" AND L(R)=2)` Only one condition is true at a time (returns the string), the others return `""`. `CHR$ 8` (cursor left) combined with a UDG is used to simulate an umlaut by overprinting. ## Timer and Beep The countdown runs as a `FOR T=9000 TO 0 STEP -1` loop (line 100). At `T=1800` (line 105), a one-second beep at pitch -30 warns players of 20% time remaining. The displayed time is `INT(T/60)`, giving an approximate seconds display since each loop iteration is not exactly 1/60th of a second (BASIC overhead makes this an approximation). At timeout, `BEEP 2,0` signals end of turn. ## Word List and Sentinel Values Player 1’s words are stored in `S$()` with lengths in `T()`. Words also found by Player 2 are marked by overwriting with `"QKZ"` (lines 235, 305). Player 2’s confirmed unique words go into `J$()` with lengths in `K()`. The sentinel `"QXZ"` is used at line 305 to blank Player 1’s entries before Player 2’s turn. The display routine at line 557 checks for `"QKZ"` to skip joint words. ## Notable Bugs and Anomalies - The word validation loop at lines 214–216 checks both `S$()` and `J$()` but uses `TO LEN X$` slicing without bounds checking, which could cause an error if `LEN X$` exceeds the dimension of the string arrays (11 characters for `S$`, 10 for `J$`). ## Round and Match Management The variable `R` tracks the current round. Scores are stored in `X(R)` and `Y(R)`. After scoring, line 735 prompts for the next round number; if the entered value matches `R`, the game loops to line 1100 (language selection for the new round). Otherwise, “END OF MATCH” is printed and execution falls to line 3000 (not shown, likely a final display or halt). Each round can independently select a language, enabling mixed-language matches. ## Source Code ``` 5 LET R=1: GO TO 800 15 PRINT OVER O;AT 20,16-(4 AND L(R)=2)-(3 AND L(R)=3);INT (T/60) 20 PRINT OVER 1-O;AT 1,0+(3 AND L(R)=1);K$: PRINT ''' 25 LET J=1 27 IF L(R)=3 THEN GO TO 32 30 PRINT OVER 1;" ";: FOR I=J TO J+3: PRINT OVER 1-O;C$(I);"u" AND C$(I)="Q"; OVER 1;(" " AND C$(I)<>"Q"); OVER 1;" ";: NEXT I: PRINT ''' 31 GO TO 35 32 PRINT OVER 1;" ";: FOR I=J TO J+3: PRINT OVER 1-O;(C$(I) AND C$(I)<>"\h" AND C$(I)<>"\r" AND C$(I)<>"\l");"u" AND C$(I)="Q";"Ch" AND C$(I)="\h";"LL" AND C$(I)="\l";"rr" AND C$(I)="\r"; OVER 1;(" " AND C$(I)<>"Q" AND C$(I)<>"\h" AND C$(I)<>"\r" AND C$(I)<>"\l"); OVER 1;" ";: NEXT I: PRINT ''' 35 LET J=J+4: IF J=17 THEN RETURN 40 GO TO 27 50 LET N=INT (RND*16)+1: LET A(16)=N 55 LET B(N)=INT (RND*16)+1 60 LET N=B(N): IF B(N)<>0 THEN GO TO 55 65 LET A(C)=N: LET C=C+1: IF C=16 THEN GO TO 80 70 GO TO 55 80 FOR I=1 TO 16: LET D$(I)=A$(6*A(I)-5 TO 6*A(I)) 85 LET N=INT (RND*6)+1: LET C$(I)=D$(I,N): NEXT I 90 PRINT OVER 1;AT 1,2+(2 AND L(R)=1);P$+I$(1) 95 PRINT OVER 1;AT 20,3;W$: PRINT AT 20,16;T$ 96 GO SUB 20 100 FOR T=9000 TO 0 STEP -1 105 IF T=1800 THEN BEEP 1,-30 110 IF INKEY$<>"" THEN GO TO 160 115 NEXT T 120 BEEP 2,0: IF SW=0 THEN GO TO 300 125 GO TO 600 150 LET O=1: GO SUB 550 154 GO SUB 15 155 GO TO 115 160 LET O=0: GO SUB 15 165 GO SUB 550 200 IF L(R)=1 THEN INPUT "ENTER WORD (""0"" TO SEE LETTERS)";X$ 201 IF L(R)=3 THEN INPUT "ENTER SU PALABRA, O ""0"" PARA VERLAS LETRAS ";X$ 202 IF L(R)=2 THEN INPUT "ENTER WORT (""0"" UM DAS BRETT ZUSEHEN ";X$ 204 IF X$="0" THEN GO TO 150 205 IF LEN X$<4 OR LEN X$>10 THEN GO TO 200 210 FOR I=1 TO LEN X$: 211 IF X$(I)=" " THEN GO TO 200 212 IF L(R)=3 AND X$(I)="W" THEN LET X$(I)="\n" 213 NEXT I 214 FOR I=1 TO (S AND S>B)+(B AND B>=S): IF X$=S$(I, TO LEN X$) AND S$(I,LEN X$+1)=" " THEN GO TO 200: 215: IF X$=J$(I, TO LEN X$) AND J$(I,LEN X$+1)=" " THEN GO TO 200: 216 NEXT I 218 LET T(S)=LEN X$: PRINT AT 20,0;S: LET S$(S)=X$ 220 PRINT AT S-1-(20 AND S>20)-(20 AND S>40),0+(22 AND S>20)-(11 AND S>40);S$(S, TO T(S)) 222 LET S=S+1: IF SW=0 THEN GO TO 200 224 FOR I=1 TO F: IF S$(S-1, TO 10)=F$(I) THEN GO TO 235 225 NEXT I 230 GO TO 200 235 LET K(B)=LEN X$: LET J$(B)=X$: LET S$(S-1)="QKZ": LET F$(I)="QKZ": LET B=B+1 240 GO TO 200 250 FOR I=64 TO 176 STEP 24: PLOT I,143: DRAW 0,-96: NEXT I 255 FOR I=47 TO 143 STEP 24: PLOT 64,I: DRAW 94,0: NEXT I 260 RETURN 300 LET O=0: GO SUB 20 305 FOR I=1 TO S-1: LET F$(I)=S$(I): LET S$(I)="QXZ": LET G(I)=T(I): NEXT I 310 LET F=S-1: LET S=1: LET SW=1 312 PRINT AT 20,0;" " 315 IF L(R)=1 THEN INPUT "PLAYER 2: ENTER INITIALS ";I$(2) 316 IF L(R)=3 THEN INPUT "JUGADOR 2: ENTER INICIALES";I$(2) 317 IF L(R)=2 THEN INPUT "SPIELER 2 ANFANGBUCHSTABEN";I$(2) 319 IF R=1 THEN LET H$=I$(2) 325 GO TO 96 330 IF SW=1 THEN GO TO 345 335 INPUT "REDUCE PLAYER"; BRIGHT 1;" 1 "; BRIGHT 0;"SCORE BY ? (99 TO SEE BOARD AND/OR UNSCROLL ";A1 340 IF A1=99 THEN GO TO 740 345 LET SW=1: INPUT "REDUCE PLAYER"; BRIGHT 1;" 2 "; BRIGHT 0;"SCORE BY ? (99 TO SEE BOARD AND/OR UNSCROLL ";A2 350 IF A2=99 THEN GO TO 740 355 RETURN 370 IF SW=1 THEN GO TO 385 375 INPUT OVER 1;"ZIEHEN ? AUS SPIELER"; BRIGHT 1;" 1 "; BRIGHT 0;"AB (99 ZUM BRETT U/O ZURu";CHR$ 8;"\oCKROLLEN) ";A1 380 IF A1=99 THEN GO TO 740 385 LET SW=1: INPUT OVER 1;"ZIEHEN ? AUS SPIELER"; BRIGHT 1;" 2 "; BRIGHT 0;"AB (99 ZUM BRETT U/O ZURu";CHR$ 8;"\oCKROLLEN) ";A2 390 IF A2=99 THEN GO TO 740 395 RETURN 410 IF SW=1 THEN GO TO 425 415 INPUT OVER 1;"QUITAR \c \a\l JUGADOR"; BRIGHT 1;" 1 "; BRIGHT 0;"? (99 PARAEL TABLERO Y/O ARROLLAR ATRA";CHR$ 8;"\u\s) " ;A1 420 IF A1=99 THEN GO TO 740 425 LET SW=1: INPUT OVER 1;"QUITAR \c \a\l JUGADOR"; BRIGHT 1;" 2 "; BRIGHT 0;"? (99 PARAEL TABLERO Y/O ARROLLAR ATRA";CHR$ 8;"\u\s) " ;A2 430 IF A2=99 THEN GO TO 740 435 RETURN 550 LET BB=1:: FOR I=1 TO S-1 552 IF S$(I, TO 3)="QKZ" THEN LET S$(I)=J$(BB) 557 PRINT OVER O;AT I-1-(20 AND I>20)-(20 AND I>40),0+(22 AND I>20)-(11 AND I>40);S$(I, TO T(I)) 558 IF S$(I, TO 10)=J$(BB) THEN LET S$(I)="QKZ" 559 IF S$(I, TO 3)="QKZ" THEN LET BB=BB+1 560 NEXT I 562 GO SUB 250 565 RETURN 600 LET SW=0 601 CLS : LET J=1 602 FOR I=J TO J+3: PRINT AT (J-1)/4,28+I-J;C$(I);: NEXT I: PRINT AT (J-1)/4,27;"▌": LET J=J+4: IF J=17 THEN GO TO 604 603 GO TO 602 604: PRINT OVER 1;AT 0,0;(B$+W$ AND L(R)<>3)+(W$+B$ AND L(R)=3)+": "; 605 LET SCR1=0: FOR I=1 TO B-1: PRINT J$(I, TO K(I));" ";: IF PEEK 2368819 THEN PRINT 606 NEXT I 607 PRINT : PRINT : PRINT TAB 12;I$(1): IF B<4 THEN PRINT 609 FOR I=1 TO F 615 IF F$(I, TO 3)="QKZ" THEN GO TO 632 618 LET X=1+(1 AND G(I)=5)+(2 AND G(I)=6)+(4 AND G(I)=7)+(10 AND G(I)=8)+(19 AND G(I)=9)+(39 AND G(I)=10) 628 LET SCR1=SCR1+X 630 PRINT F$(I);X, 632 NEXT I 636 PRINT TAB 26;"\j": PRINT TAB 26;SCR1 637 PRINT : PRINT : PRINT TAB 12;I$(2): IF B<4 THEN PRINT 638 LET SCR2=0 639 FOR I=1 TO S-1 640 IF S$(I, TO 3)="QKZ" THEN GO TO 665 654 LET Y=1+(1 AND T(I)=5)+(2 AND T(I)=6)+(4 AND T(I)=7)+(10 AND T(I)=8)+(19 AND T(I)=9)+(39 AND T(I)=10) 657 LET SCR2=SCR2+Y 660 PRINT S$(I, TO 10);Y, 665 NEXT I 670 PRINT TAB 26;"\j": PRINT TAB 26;SCR2 672 IF PEEK 23689<4 THEN PRINT ' 675 GO SUB 290+40*L(R) 720 LET SCR1=SCR1-A1: LET SCR2=SCR2-A2 725 CLS : PRINT TAB 13;G$;TAB 23;H$ 726 IF I$(1)=G$ THEN GO TO 729 727 LET Z=SCR1: LET SCR1=SCR2: LET SCR2=Z 729 LET X(R)=SCR1: LET Y(R)=SCR2: LET Z$="EGSEDSIAE" 730 FOR I=1 TO R: PRINT AT I+1,0;R$(LEN R$-5 TO LEN R$-1);" ";I;"(";Z$(3*L(R)+L(I)-3);") ";X(I);: PRINT AT I+1,23;Y(I): NEXT I 735 PRINT OVER 1;AT 21,0;R$: INPUT RR 736 PRINT OVER 1;AT 21,0;R$ 737 LET R=R+1: IF RR=R THEN GO TO 1100 739 PRINT "END OF MATCH": GO TO 3000 740 CLS : PRINT '''': GO SUB 25 745 IF INKEY$<>"" THEN GO TO 601 746 GO TO 745 810 FOR I=1 TO 8: READ V$: FOR J=0 TO 7: READ Y: POKE USR V$+J,Y: NEXT J: NEXT I 815 DATA "N",60,66,98,82,74,70,66,0 820 DATA "C",16,0,16,48,64,68,68,56 825 DATA "O",36,36,0,0,0,0,0,0 830 DATA "U",2,4,8,0,0,0,0,0 835 DATA "J",0,255,0,0,0,0,0,0 840 DATA "H",4,20,100,134,133,69,53,0 842 DATA "R",0,119,204,136,136,136,136,0 845 DATA "L",0,132,132,132,132,132,231,0 850 DATA 16,-1,0,15,1,1,12,-1,1,7,1,1,4,0,-1,8,-1,1,3,-1,-1 855 DATA 6,0,1,2,-1,0,1,0,-1,5,0,-1,9,1,-1,14,-1,0,13,0,0 920 DIM R(20): DIM L(20): DIM X(20): DIM Y(20) 925 DIM I$(2,3) 1000 LET O=0: LET C$="NURLDOGEOGOGENOB": GO SUB 250 1005 PRINT ''''': GO SUB 25 1010 PAUSE 120: LET A=148: LET B=60: PRINT BRIGHT 1;AT 14,18;"B": FOR I=1 TO 14: READ X: READ Y: READ Z 1015 REM NEXT I: GO TO 1050 1020 FOR J=1 TO 24: LET A=A+Y: LET B=B+Z: PLOT A,B: NEXT J: PLOT BRIGHT 1;A,B: PLOT PAPER 0;148,60 1025 FOR J=0 TO 4: PLOT 140-16*J,39-8*J: DRAW 10,0: NEXT J 1030 FOR J=0 TO 5: PRINT AT 16+J,18-2*J;C$(X) 1035 PAUSE 10: PRINT OVER 1;AT 16+J,18-2*J;C$(X): NEXT J 1040 PRINT AT 21,I+1+(8 AND I>6)+(1 AND I>11);C$(X) 1045 PLOT PAPER 0;A,B: NEXT I 1100 PAUSE 120: CLS : PRINT ''': PRINT TAB 10;"1 ENGLISH"''TAB 10;"2 DEUTSCH"''TAB 10;"3 ESPA\nOL" 1110 INPUT L(R): IF L(R)<>1 AND L(R)<>2 AND L(R)<>3 THEN GO TO 1110 1150 DIM A(16): DIM B(16): DIM F(60): DIM G(60): DIM S(60): DIM T(60): DIM J(50): DIM K(50) 1160 DIM S$(60,11): DIM F$(60,10): DIM J$(50,10): DIM D$(16,6): DIM C$(16,1) 1940 IF L(R)=1 THEN LET A$="BTLAYISHrOmACASLErCDPAmEvGTINEGUYKELOQABmJTLSEPUGLrUwIEHYEFIUTODKNOrIXFBDANvEZACIATOINEHPSNwDOSE" 1945 IF L(R)=2 THEN LET A$="BTLANIEHrOMACASLErCOPAmEvGTINEGUrKELOQABmJTLSEPUGLrUwIEHYEFIUTODKNOrIXFBDANVEZACIATOINEHPSNwDOSE" 1950 IF L(R)=3 THEN LET A$="BTLAYISHrOmACASLOrCDPAmOvGTINEGUA\hELOQABmJTLSEPUGLrU\nIEH\rEFIUTOD\hNOrIXFBDANvEZACIATOINEHPSN\lDOSE" 1960 LET W$=("WORDS" AND L(R)=1)+("PALABRAS" AND L(R)=3)+("Wo"+CHR$ 8+"\oRTER" AND L(R)=2) 1965 LET T$=(" SECONDS LEFT" AND L(R)=1)+(" SEGUNDOS QUEDAN" AND L(R)=3)+("SEKUNDEN BLEIBEN" AND L(R)=2) 1970 LET K$=(" TOUCH ANY KEY TO WRITE" AND L(R)=1)+(" CUALQUIER TECLA PARA ESCRIBIR" AND L(R)=3)+("IRGENDEINE TASTE UM ZU SCHREIBEN" AND L(R)=2) 1975 LET P$=("ONE MOMENT PLEASE, " AND L(R)=1)+("EIN AUGENBLICK BITTE, " AND L(R)=2)+("UN MOMENTO POR FAVOR, " AND L(R)=3) 1980 LET B$=("JOINT " AND L(R)=1)+("GEMEINSAME " AND L(R)=2)+(" EN COMu"+CHR$ 8+"\uN" AND L(R)=3) 1990 LET R$=("NUMBER OF NEXT ROUND?" AND L(R)=1)+("NUMMER DER NA"+CHR$ 8+"\oCHSTE RUNDE?" AND L(R)=2)+("\cNu"+CHR$ 8+"\uMERO DE LA PRo"+CHR$ 8+"\uXIMA TANDA?" AND L(R)=3) 2400 LET F=1: LET S=1: LET B=1: LET SW=0: LET C=1: LET O=0: 2425 IF L(R)=1 THEN INPUT "PLAYER 1: ENTER INITIALS ";I$(1) 2430 IF L(R)=3 THEN INPUT "JUGADOR 1 ENTER INICIALES ";I$(1) 2435 IF L(R)=2 THEN INPUT "SPIELER 1 ANFANGBUCHSTABEN";I$(1) 2450 CLS : GO SUB 250 2455 IF R=1 THEN LET G$=I$(1) 2460 PRINT AT 1,2+(2 AND L(R)=1);P$;I$(1) 2475 GO TO 50 2500 REM "BOGGLE 3" ```