HIDDENWORD

This file is part of and CATS Library Tape 1. Download the collection to get this file.
Developer(s): George Mockridge
Date: 198x
Type: Program
Platform(s): TS 2068
Tags: Game

This program is a hidden-word puzzle generator that places user-supplied words into a grid in all eight directions (horizontal, vertical, and four diagonals, both forward and backward). It begins by loading and executing a machine code routine from “DRIVER.BIN” at address 60000, then collects grid dimensions (up to 22 rows by 16 columns) and up to 50 words from the user. Words are sorted by length in a custom insertion-sort routine before placement, and unused cells are filled with random uppercase letters using CHR$(INT(RND*26)+65). Output can be directed to the screen, printer, or both, with the word list printed alongside the completed grid.


Program Analysis

Program Structure

The program is organized into several well-defined phases, each occupying a range of line numbers:

  1. Lines 10–20: Load and execute the machine code binary “DRIVER.BIN” at address 60000.
  2. Lines 50–1000: Initialization — set system flags, collect grid size, allocate arrays.
  3. Lines 1700–2700: Word entry loop — collect up to 50 words, track total letter count, exit on blank entry.
  4. Lines 2700–3600: Pre-processing — read direction vectors, estimate completion time, randomize cell order, sort words by length.
  5. Lines 3600–7098: Grid-filling engine — attempt to place each word in each of the eight directions at each candidate cell; fill unplaced cells with random letters.
  6. Lines 7100–9990: Output section — display and/or print the puzzle grid and word list.
  7. Line 9999: Save the program with an auto-run entry point.

Machine Code Usage

Lines 10 and 20 use CAT "DRIVER.BIN",60000 and RANDOMIZE USR 60000 to load and call an external machine code routine. This routine likely handles low-level tasks such as fast screen output or keyboard handling that would be too slow in BASIC. The address 60000 sits in high RAM, well above the normal BASIC program area.

Array Allocation

ArrayDimensionsPurpose
M$(MR,MC)up to 22×16The puzzle grid, one character per cell
W$(50,16)50×16Stored words (max 16 chars each)
R(50,2)50×2Word index and length, used for sorting
D(8,2)8×2Direction vectors (row delta, column delta)
S(NC)up to 352Randomized cell visit order
U(50)50Flag: word successfully placed
Q(50)50Working queue of unplaced word indices

Direction Vectors

Eight direction vectors are read from DATA at line 2800 into the D(8,2) array. Each pair represents a (row delta, column delta):

  • (0,1) — right
  • (1,1) — down-right diagonal
  • (1,0) — down
  • (1,-1) — down-left diagonal
  • (0,-1) — left
  • (-1,-1) — up-left diagonal
  • (-1,0) — up
  • (-1,1) — up-right diagonal

Word Sorting

Lines 3400–3564 implement an insertion sort on the R array, ordering words from longest to shortest. Placing longer words first improves placement success rate by consuming more of the grid before shorter, more flexible words are attempted. The sorted order is copied into the working queue array Q.

Cell Randomization

Lines 3150–3300 build a Fisher-Yates-style shuffle of cell indices into array S by repeatedly picking a random unfilled slot. This ensures the placement engine visits grid cells in a pseudo-random order each run, giving varied puzzle layouts. RANDOMIZE at line 3100 re-seeds the RNG without a seed argument, using the system clock for true randomness.

Placement Engine

For each unvisited cell (line 3700 loop), the engine tries all eight directions (the DI/DK loop at lines 4000–6900). For each direction, it computes the full run of cells from one edge of the grid to the other passing through the candidate cell (lines 4050–5250), extracts the string of current cell contents as X$, then tries every substring of X$ at lines 5500–6800 to see whether any queued word fits — matching either a blank marker P$ (“-“), a free slot marker K$ (“*”), or the correct letter already in that cell.

Inline String Search Subroutine

Lines 5452–5476 implement a simple substring-search subroutine parameterized by Q$ (the string to search), R$ (the pattern), and QO (start offset), returning the match position in QF. It is used at lines 5454–5460 specifically to locate the first free-slot marker K$ (“*”) within the candidate run, to confirm that at least one truly empty cell is present before committing a word.

Fill Characters

Cells that remain unoccupied after all placement attempts receive a random uppercase ASCII letter via CHR$(INT(RND*26)+65) at line 6950. The count of such fill characters is tracked in FU and individual fill events are reported with a scroll-prevention POKE 23692,255.

Time Estimation

Line 2905 computes a rough estimated completion time in minutes using the formula INT(((NC/100)*.6)+(6.7^(NC*(E/NC)/100+.6))), where NC is total cell count and E is total letters in all words. The exponential term (6.7^x) models the dramatically increasing time for denser grids, and the warning at line 2910 explicitly flags grids over 50% full.

Output Section

Lines 8200–9990 handle three output modes selected by the user: screen only (A$="1"), printer only (A$="2"), or both (A$="3"). Printer output at lines 8850–8862 builds each row as a spaced string V$ before issuing LPRINT. The word list section at lines 9964–9976 uses a GOTO-driven loop (jumping back to NEXT I at line 9970 after printing) rather than a structured FOR/NEXT block, which is an unusual but functional idiom.

Notable Bugs and Anomalies

  • Lines 2551 and 2552 are identical (LET R(NW,1)=NW: LET R(NW,2)=LEN E$), so the assignment is redundantly executed twice — harmless but wasteful.
  • The check at line 2600 (IF W$(NW,1)=" " THEN GO TO 2700) tests whether the first character of the stored word string is a space, which is the condition for an empty (blank) input, since W$(NW) is zero-padded with spaces. This is a correct but indirect blank-entry detection idiom for string arrays.
  • At line 5702, the loop FOR K=1 TO 15 / IF W$(W,K)<>" " THEN NEXT K finds the position of the first trailing space to determine word length, then checks K-1<>CL. This relies on the string array padding behavior and breaks if a word is exactly 15 characters (the loop exits without finding a space and K will be 16 after the loop, giving length 15 by coincidence). Words up to 15 characters are handled, consistent with the 16-column W$ array declaration.
  • The RESTORE at line 2700 is called before reading the direction DATA at line 2750, which is correct since the DATA statement at line 2800 is the only data block; without RESTORE a second puzzle run would fail to re-read it.

Content

Appears On

Capital Area Timex Sinclair User Group’s Library Tape.

Related Products

Related Articles

Related Content

Image Gallery

HIDDENWORD

Source Code

   10 CAT "DRIVER.BIN",60000
   20 RANDOMIZE USR 60000
   50 POKE 23658,8
   55 REM Remove line 50 if you   want the hidden words to appear in small letters.  This will    help in debugging program.
  100 CLS 
  150 PRINT "          HIDDEN WORD"
  200 PRINT "        PUZZLE GENERATOR"
  250 PRINT 
  300 PRINT 
  350 PRINT 
  400 INPUT "HOW MANY ROWS HIGH? 22 MAX. ";MR
  402 IF MR>22 THEN GO TO 400
  450 INPUT "HOW MANY COLUMNS WIDE? 16 MAX.";MC
  452 IF MC>16 THEN GO TO 450
  500 LET NC=MR*MC
  550 LET P$="-"
  600 LET K$="*"
  650 LET D$="+"
  700 LET NW=0
  750 DIM M$(MR,MC)
  800 DIM W$(50,16)
  825 DIM R(50,2)
  850 DIM D(8,2)
  900 DIM S(NC)
  950 DIM U(50)
 1000 DIM Q(50)
 1700 CLS 
 2300 LET NW=0
 2350 LET E=0
 2400 LET NW=NW+1
 2450 CLS 
 2452 PRINT "GRID SIZE IS ";MR;" BY ";MC
 2454 PRINT "YOU HAVE HIDDEN ";NW-1;" WORDS."
 2456 PRINT "GRID IS ";INT (E/NC*100);" % FULL."
 2498 PRINT "PRESS ENTER WHEN FINISHED."
 2500 PRINT "ENTER WORD # ";NW
 2525 IF NW>50 THEN PRINT : PRINT : PRINT "MAX.50 WORDS USED, PRESS ENTER."
 2540 INPUT E$
 2545 IF NW>50 AND E$="" THEN GO TO 2700
 2550 IF NW>50 AND E$<>"" THEN CLS : PRINT AT 10,0;"YOU MAY NOT USE MORE THAN 50    WORDS.  RUN THE PROGRAM AGAIN.": STOP 
 2551 LET R(NW,1)=NW: LET R(NW,2)=LEN E$
 2552 LET R(NW,1)=NW: LET R(NW,2)=LEN E$
 2553 LET W$(NW)=E$
 2554 LET E=E+LEN (E$)
 2600 IF W$(NW,1)=" " THEN GO TO 2700
 2650 GO TO 2400
 2700 LET NW=NW-1: RESTORE 
 2750 FOR I=1 TO 8: READ D(I,1): READ D(I,2): NEXT I
 2800 DATA 0,1,1,1,1,0,1,-1,0,-1,-1,-1,-1,0,-1,1
 2905 LET EST=INT (((NC/100)*.6)+(6.7^(NC*(E/NC)/100+.6)))
 2910 PRINT : PRINT "THE TIME IT TAKES TO COMPLETE   THE PUZZLE WILL VARY WITH THE   SIZE OF THE GRID AND THE # OF   WORDS USED.  WARNING - TIME     INCREASES GREATLY FOR LARGER    GRIDS OVER 50% FULL."
 2912 PRINT "EST. TIME OF THIS PUZZLE ";EST;" MIN."
 2950 PRINT : PRINT "SETTING UP THE GRID. PLEASE WAIT"
 3000 FOR I=1 TO MR: FOR J=1 TO MC: LET M$(I,J)=P$: NEXT J: NEXT I
 3050 FOR I=1 TO NC: LET S(I)=0: NEXT I
 3100 RANDOMIZE 
 3150 FOR I=1 TO NC
 3200 LET Q=INT (RND*NC)+1: IF S(Q)<>0 THEN GO TO 3200
 3250 LET S(Q)=I
 3300 NEXT I
 3350 FOR I=1 TO NW: LET Q(I)=0: LET U(I)=0: NEXT I
 3400 FOR I=1 TO NW-1
 3402 LET J=I
 3404 LET T1=R(I+1,1): LET T2=R(I+1,2)
 3406 IF T2<R(J,2) THEN GO TO 3414
 3408 LET R(J+1,2)=R(J,2): LET R(J+1,1)=R(J,1)
 3410 LET J=J-1
 3412 IF J>=1 THEN GO TO 3406
 3414 LET R(J+1,2)=T2: LET R(J+1,1)=T1
 3416 NEXT I
 3560 FOR I=1 TO NW
 3562 LET Q(I)=R(I,1)
 3564 NEXT I
 3600 LET MF=0: LET WA=NW: LET FU=0: LET DI=1
 3650 PRINT : PRINT "STARTING TO FILL IN THE GRID"
 3652 PRINT 
 3700 FOR N=1 TO NC
 3750 LET CP=S(N)
 3800 LET CR=INT ((CP-1)/MC)+1: LET CC=CP-(CR-1)*MC
 3850 IF M$(CR,CC)<>P$ THEN GO TO 7000
 3900 IF WA=0 THEN LET MF=0: GO TO 6950
 3950 LET M$(CR,CC)=K$
 4000 LET DK=1
 4050 LET IR=D(DI,1): LET IC=D(DI,2)
 4100 LET RT=1: IF IR<0 THEN LET RT=MR
 4150 IF IR=0 THEN LET RT=CR
 4200 LET CT=1: IF IC<0 THEN LET CT=MC
 4250 IF IC=0 THEN LET CT=CC
 4300 LET BR=CR: LET BC=CC
 4350 IF (BR=RT AND IR<>0) OR (BC=CT AND IC<>0) THEN GO TO 4600
 4400 REM GOTO 1500
 4450 LET BR=BR-IR
 4500 LET BC=BC-IC
 4550 GO TO 4350
 4600 LET RT=1: IF IR>0 THEN LET RT=MR
 4650 IF IR=0 THEN LET RT=CR
 4700 LET CT=1: IF IC>0 THEN LET CT=MC
 4750 IF IC=0 THEN LET CT=CC
 4800 LET ER=CR: LET EC=CC
 4850 IF (ER=RT AND IR<>0) OR (EC=CT AND IC<>0) THEN GO TO 5050
 4900 LET ER=ER+IR
 4950 LET EC=EC+IC
 5000 GO TO 4850
 5050 LET UR=ER: IF BR>ER THEN LET UR=BR
 5100 LET LR=BR: IF ER<BR THEN LET LR=ER
 5150 LET UC=EC: IF BC>EC THEN LET UC=BC
 5200 LET LC=BC: IF EC<BC THEN LET LC=EC
 5250 LET PR=BR: LET PC=BC: LET X$=""
 5300 LET X$=X$+M$(PR,PC)
 5350 LET PR=PR+IR: LET PC=PC+IC: IF PR>=LR AND PR<=UR AND PC>=LC AND PC<=UC THEN GO TO 5300
 5400 LET PL=LEN (X$)
 5452 LET QO=1
 5454 LET Q$=X$
 5456 LET R$=K$
 5458 GO SUB 5462
 5460 LET P=QF
 5461 GO TO 5500
 5462 LET QF=0
 5464 IF LEN (R$)=0 THEN RETURN 
 5466 IF QO+LEN (R$)-1>LEN (Q$) THEN RETURN 
 5468 IF Q$(QO TO QO+LEN (R$)-1)=R$ THEN GO TO 5474
 5470 LET QO=QO+1
 5472 GO TO 5466
 5474 LET QF=QO
 5476 RETURN 
 5500 FOR L=1 TO P: FOR R=PL TO P STEP -1
 5550 LET C$=X$(L TO L+(R-L+1)-1): LET CL=LEN (C$)
 5600 LET Q=1
 5650 LET W=Q(Q)
 5700 FOR K=1 TO 15
 5701 IF W$(W,K)<>" " THEN NEXT K
 5702 IF K-1<>CL THEN LET MF=0: GO TO 6750
 5750 LET MF=1
 5800 FOR C=1 TO CL
 5850 IF C$(C)=P$ OR C$(C)=K$ THEN GO TO 5950
 5900 IF C$(C)<>W$(W,C) THEN LET C=CL: LET MF=0
 5950 NEXT C
 6000 IF MF=0 THEN GO TO 6750
 6050 LET F$=W$(W,1 TO C-1)
 6100 IF L>1 THEN LET F$=D$+F$: LET L=L-1: GO TO 6100
 6150 IF R<PL THEN LET F$=F$+D$: LET R=R+1: GO TO 6150
 6200 LET PR=1: LET R=BR: LET C=BC
 6250 LET Y$=F$(PR TO PR+1-1): IF Y$=D$ THEN GO TO 6350
 6300 LET M$(R,C)=Y$
 6350 IF (R=ER AND IR<>0) OR (C=EC AND IC<>0) THEN GO TO 6450
 6400 LET C=C+IC: LET R=R+IR: LET PR=PR+1: GO TO 6250
 6450 IF Q=WA THEN GO TO 6550
 6500 FOR I=Q TO WA-1: LET Q(I)=Q(I+1): NEXT I
 6550 LET WA=WA-1
 6600 LET U(W)=1
 6650 LET R=P: LET L=P: LET DK=8
 6700 POKE 23692,255: PRINT "USED WORD ";NW-WA;"/";NW;" ";W$(W): GO TO 6800
 6750 LET Q=Q+1: IF Q<=WA THEN GO TO 5650
 6800 NEXT R: NEXT L
 6850 LET DI=DI+1: LET DK=DK+1: IF DI>8 THEN LET DI=1
 6900 IF DK<=8 THEN GO TO 4050
 6950 IF MF=0 THEN LET M$(CR,CC)=CHR$ (INT (RND*26)+65): LET FU=FU+1: POKE 23692,255: PRINT "USED A  FILL  CHAR. ";NC-N: GO TO 7050
 7000 PRINT "CELLS NOT YET EXAM. ";NC-N
 7050 NEXT N
 7098 BEEP 3,30
 7100 CLS 
 7150 PRINT "    PUZZLE COMPLETED"
 7200 PRINT 
 7250 PRINT 
 7300 PRINT "PRINTER AND DISPLAY SECTION"
 7450 PRINT 
 7500 PRINT "WHERE DO YOU WISH THE "
 7550 PRINT "      PUZZLE SENT ?"
 7600 PRINT 
 7650 PRINT 
 7700 PRINT "(1)  SCREEN DISPLAY ONLY"
 7750 PRINT 
 7800 PRINT "(2)  SEND TO PRINTER ONLY"
 7850 PRINT 
 7900 PRINT "(3)  DISPLAY ON SCREEN *AND*"
 7950 PRINT "     SEND TO PRINTER"
 8000 PRINT 
 8050 PRINT "(ENTER 1,2 or 3)"
 8100 PRINT 
 8150 INPUT A$
 8200 IF A$="1" THEN GO TO 8400
 8250 IF A$="2" THEN GO TO 8850
 8300 IF A$="3" THEN GO TO 8400
 8350 GO TO 8050
 8400 CLS 
 8450 FOR T=1 TO MR
 8550 PRINT M$(T,1 TO MC)
 8750 NEXT T
 8754 PRINT 
 8755 IF A$="1" THEN GO TO 9950
 8850 FOR T=1 TO MR
 8851 LET V$=""
 8852 FOR C=1 TO MC
 8854 LET V$=V$+M$(T,C)
 8855 LET V$=V$+" "
 8856 NEXT C
 8860 LPRINT V$
 8862 NEXT T
 8864 IF A$="2" THEN GO TO 9951
 9950 PRINT "The Answers Are Hidden In EIGHT Directions."
 9951 IF A$<>"1" THEN LPRINT : LPRINT "The Answers Are Hidden In EIGHT Directions."
 9952 IF A$<>"2" THEN PRINT "VERT. HORIZ. DIA. BACK. FOR."
 9953 IF A$<>"1" THEN LPRINT : LPRINT "VERT. HORIZ. DIA. BACK. FOR."
 9960 IF A$<>"2" THEN PRINT : PRINT "THE HIDDEN WORDS ARE:"
 9961 IF A$<>"1" THEN LPRINT : LPRINT "THE HIDDEN WORDS ARE:"
 9962 IF A$<>"2" THEN PRINT 
 9963 IF A$<>"1" THEN LPRINT 
 9964 FOR I=1 TO NW
 9966 IF U(I)<>0 THEN GO TO 9974
 9970 NEXT I
 9972 GO TO 9980
 9974 IF A$<>"2" THEN PRINT W$(I);;
 9975 IF A$<>"1" THEN LPRINT W$(I);
 9976 GO TO 9970
 9980 PRINT 
 9981 IF A$<>"1" THEN LPRINT 
 9982 PRINT : PRINT "RETURN TO MENU? (Y OR N)"
 9984 INPUT A$
 9986 IF A$="Y" THEN GO TO 7100
 9988 IF A$<>"N" THEN GO TO 9982
 9990 STOP 
 9999 CLEAR : SAVE "WORD" LINE 50

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

Scroll to Top