COMMENT This is the complete version of the Scrabble program. COMMENT ( print(("Input a large integer to initialize the random number generator" , newline)) ; read((lastrandom, newline)) ; LOC INT boardsize := 0 ; WHILE NOT ODD boardsize DO print(("Input the boardsize (an odd number, please)", newline)) ; read((boardsize, newline)) OD # **** Now declare 'board' with 'boardsize+2' rows and columns # # Note that all arrays in this program will have their lower bounds (LWB) equal to 1 # ; LOC [1:boardsize+2,1:boardsize+2] CHAR board ; FOR i FROM 1 TO 2 UPB board DO board[1, i] := " " OD ; FOR j FROM 1 TO 1 UPB board DO board[j, ] := board[1, ] OD # **** Now write ".scrabble." at the centre of the board # ; LOC INT centre := (1 UPB board)%2 + 1 ; board[centre,centre-5:centre+4]:=".scrabble." ; PROC print board = VOID: ( print(newline) ; TO 1 UPB board +2 DO print("*") OD ; print(newline) ; FOR i FROM 2 TO 1 UPB board -1 DO print(("* ",board[i, 2 : 2 UPB board -1 ], " *",newline)) OD ; TO 1 UPB board +2 DO print("*") OD ; print((newline,newline)) ) ; print board ; LOC FILE vocabulary ; open(vocabulary, "/usr/dict/words", stand in channel) ; on logical file end(vocabulary, (REF FILE f)BOOL: GOTO stop) ; PROC get word = STRING: ( LOC STRING s ; FOR i TO ROUND (next random(lastrandom)*1000) DO get(vocabulary, newline) OD ; WHILE get(vocabulary, (s, newline)) ; s[1]>="A" AND s[1]<="Z" DO SKIP OD ; s ) ; PRIO MAX = 4 ; OP MAX = (INT a, b)INT: IF a>b THEN a ELSE b FI ; PROC firstchar = ([] CHAR piece)INT: ( # **** Scans 'piece' looking for the first non-space CHAR, and returns its index. If 'piece' is all spaces, it returns 'UPB piece +1' (as if there were a space immediately after it). # LOC INT index := 1 ; WHILE piece[index] = " " AND index < UPB piece DO index +:=1 OD ; IF piece[index]/=" " THEN index ELSE index+1 FI ) ; PROC fits = ([] CHAR word, piece)BOOL: ( # **** Tests whether 'word' will fit over the initial part of 'piece'. First find the first non-space CHAR of 'piece'. If this is beyond the end of 'word' with one to spare, then the fit is possible; if it is beyond, but with none to spare, then it is NOT possible; otherwise, the corresponding CHAR in 'word' must match the CHAR from 'piece', in which case we call 'fit' recursively on the remainder of 'word' together with the remainder of 'piece'. It may be asssumed that the LWBs of 'word' and of 'piece' are both 1 # LOC INT first := firstchar(piece) ; LOC INT gap := first - UPB word ; IF gap > 1 THEN TRUE ELIF gap = 1 THEN FALSE ELIF word[first] = piece[first] THEN IF UPB word > 1 THEN fits(word[first+1: ],piece[first+1: ]) ELSE TRUE FI ELSE FALSE FI ) ; PROC put word = (STRING word, REF [] CHAR piece)BOOL: ( # **** 'piece' is a complete row or column of 'board' (including the border spaces at each end). The REF gives the effect of a VAR parameter in PASCAL. We try to 'fit' 'word' starting at every allowed position within 'piece', but using 'firstchar' to skip quickly past long stretches of spaces. So, initialize an index 'start' to 1 and, each time round the loop, increment it so that, if 'word' started there it would end on the first non-space CHAR (increment it by 1 if this is NOT possible - the infix operator MAX has been provided to assist with this). Exit the loop if the new position of 'start' leaves no room for 'word' at the end of 'piece'. Otherwise, check that there is a space at [start-1] and see if 'word' 'fits' in the part of 'piece' starting at 'start' (NOTe that slicing out a part of 'piece' for this purpose automatically gives a [] CHAR with its LWB=1, as assumed in 'fits'). Also, it is quite in order to provide the STRING 'word' where 'fits' is expecting a [] CHAR. If this works, you may assign 'word' to the found place within 'piece', with a '.' before and after it to protect its ends. 'put word' should then return TRUE. # LOC INT start := 1 ; LOC BOOL fitted := FALSE ; WHILE (UPB word < (UPB piece - start - 1) AND NOT fitted) DO start := (firstchar(piece[start: ]) + start - (UPB word) - 2) MAX (start+1) ; fitted := firstchar(piece[start: ]) <= UPB word AND fits(word,piece[start: ]) AND piece[start-1]=" " OD ; IF fitted THEN piece[start-1:start + UPB word] := "." + word + "." ; TRUE ELSE FALSE FI ) ; LOC STRING word := get word ; LOC INT row, column ; LOC BOOL done ; DO # **** Use 'put word' to try to put 'word' into each odd-numbered row and each odd-numbered column (the ones with even indices actually, because of the border). As soon as a word has been placed, use 'get word' to obtain aNOTher one and 'print board'. Then continue your scan from where it left off. # row:=column:=2 ; WHILE done := put word(word, board[row, ]) ; NOT done AND row < boardsize + 1 DO row +:=2 OD ; IF NOT done THEN WHILE done := put word(word, board[ ,column]) ; NOT done AND column < boardsize + 1 DO column +:=2 OD FI ; IF done THEN print((word," has been added.",newline)) ;print board ELSE print(("failure to add the word ",word,newline)) FI ; word := get word OD )