program newscrab(Input, Output, Board);
{

  Compressed dict? Also, do hsort on fly when checking?
  5-bit coding?
  record for 'bestxxx' then keep best .
  Undo - for scrabble-challenge games. Or read board off net...

  Encrypt. See list in jacket pocket!
  Seems to have left-most bias on initial move - doesn't take advantage
  of 2*L squares on right? [try ntolsey on an empty board...]
}
const
  XMIN = 1; XMAX = 15;   { The Scrabble board is 15 by 15 }
  YMIN = 1; YMAX = 15;

  ALETTER = 'a'; ZLETTER = 'z';

  MAXTILES = 7; { A player's rack can hold this many tiles }

  wc2 = 92; (* have another 13 somewhere *)
  wc3 = 966;
  wc4 = 4146;
  wc5 = 8991;
  wc6 = 15452;
  wc7 = 22144;
  wc8 = 25269;
  wc9 = 24268;
  wc10 = 21092;
  wc11 = 15674;
  wc12 = 10605;
  wc13 = 6539;
  wc14 = 3692;
  wc15 = 1896;

type
  direction = (horizontal, vertical);
                        { This program uses the fact that Scrabble
                          boards are symmetrical in X=Y. This lets
                          use write all the code to place words as
                          if we only ever place them horizontally;
                          When we want to place vertical words we
                          simply flip the board over and start again. }

  letter = ALETTER..{ZLETTER+2}'~'; { | }

  tile = letter; tileset = set of letter;
  score = -1..maxint;
  freq = 0..12;
  tileindex = 0..maxtiles;
                        { This is used as an index into the player's rack.
                          0 means no tiles are held/remain to be placed. }

  xtilepos = 1..15;     { Although the board is 15 by 15...       }
  ytilepos = 1..15;
  xtilerange = 0..16;   { We keep our data in a 17 by 17 array,   }
  ytilerange = 0..16;   { to simplify the coding of certain cases }
                        { at the edge of the board.               }
  xytilearray = array [xtilerange, ytilerange] of tile;
  xyletterarray = array [xtilerange, ytilerange] of letter;

  rack = array [1..MAXTILES] of tile;
  letterrack = packed array [1..MAXTILES] of letter;
  allowed = array [xtilerange, ytilerange] of xtilerange;
  { Pre-compute how many tiles may be placed starting at each position,
    and store them in min[] and max[] }
  choicearray = array [xtilerange, ytilerange] of tileset;
  scorearray = array [xtilerange, ytilerange] of score;
  { Choice[x,y] says whether a tile can be placed there - constrained
    by vertical abutment; VScore[x,y] gives the score of the word
    going through x,y vertically except for bonuses AND THE LETTER
    AT x,y ITSELF!
  }
{
  t2  = packed array [1..2] of char;
  t3  = packed array [1..3] of char;
  t4  = packed array [1..4] of char;
  t5  = packed array [1..5] of char;
  t6  = packed array [1..6] of char;
  t7  = packed array [1..7] of char;
  t8  = packed array [1..8] of char;
  t9  = packed array [1..9] of char;
  t10 = packed array [1..10] of char;
  t11 = packed array [1..11] of char;
  t12 = packed array [1..12] of char;
  t13 = packed array [1..13] of char;
  t14 = packed array [1..14] of char;
  t15 = packed array [1..15] of char;

  r15 = 0..15;
}



  t2  = packed array [1..4] of char;
  t3  = packed array [1..6] of char;
  t4  = packed array [1..8] of char;
  t5  = packed array [1..10] of char;
  t6  = packed array [1..12] of char;
  t7  = packed array [1..14] of char;
  t8  = packed array [1..16] of char;
  t9  = packed array [1..18] of char;
  t10 = packed array [1..20] of char;
  t11 = packed array [1..22] of char;
  t12 = packed array [1..24] of char;
  t13 = packed array [1..26] of char;
  t14 = packed array [1..28] of char;
  t15 = packed array [1..30] of char;

  r15 = 0..30;




  factor = 0..3;
  factors = record
    letterfactor: factor;
    wordfactor: factor;
  end;
  factorarray = array [xtilerange, ytilerange] of factors;

var
  Dict,
  Board: text;
  Axis: direction;
  move: integer;
  realmove: integer;
  empty: boolean;
  cantmove: boolean;
  BestScore: score;
  bestaxis: direction;
  bestt: rack;
  besttl: letterrack;
  bestcount: tileindex;
  bestlength: xtilerange;
  bestx: xtilepos;
  besty: ytilepos;
  bestword: t15;
  HeldTile: rack;
  HeldLetter: letterrack;
  LastTile: tileindex;
  VScore: scorearray;
  Special: factorarray;
  Min, Max: allowed;

  BoardTile: xytilearray;   { The main playing area, which holds the game
                              so far. }
  ApparentBoardLetter: xyletterarray;
  { **** Note the vital distinction between the tile board and the letter
    board.  If a letter played is a blank, the tile board will record the
    letter it has been defined to be.  The letter board will record a
    BLANKLETTER.
  }

  Choice: choicearray;
  BlankTileHeld: boolean;
  BLANKLETTER: letter;
  FREELETTER: letter;
  BLANKTILE: tile;      { Arbitrarily defined to be the letter after Z }
  FREETILE: tile;
  ATILE: tile;
  EVERYHELDTILE, EVERYTILE: tileset;
                        { Dynamically initialised set-constant containing all
                          letters A to Z.  Used when placing a BLANKTILE. }

  letterscore: array [letter] of score;
  letterfreq: array [letter] of freq;

   s2: t2;    s3:  t3;   s4: t4;    s5: t5;    s6: t6;    s7:  t7;
   s8: t8;    s9:  t9;  s10: t10;  s11: t11;  s12: t12;  s13: t13;
  s14: t14;  s15: t15;

  a2:  packed array [1 ..  wc2 ] of t2;
  a3:  packed array [1 ..  wc3 ] of t3;
  a4:  packed array [1 ..  wc4 ] of t4;
  a5:  packed array [1 ..  wc5 ] of t5;
  a6:  packed array [1 ..  wc6 ] of t6;
  a7:  packed array [1 ..  wc7 ] of t7;
  a8:  packed array [1 ..  wc8 ] of t8;
  a9:  packed array [1 ..  wc9 ] of t9;
  a10: packed array [1 ..  wc10 ] of t10;
  a11: packed array [1 ..  wc11 ] of t11;
  a12: packed array [1 ..  wc12 ] of t12;
  a13: packed array [1 ..  wc13 ] of t13;
  a14: packed array [1 ..  wc14 ] of t14;
  a15: packed array [1 ..  wc15 ] of t15;

procedure hsort(var word: t15; len: r15); {Mindless bubble sort!}
var
  i, j: r15;
  c: char;
begin
  for i := 1 to len do begin
    for j := i to len do begin
      if word[j] < word[i] then begin
        c := word[i]; word[i] := word[j]; word[j] := c;
      end;
    end;
  end;
end {hsort};

procedure InitDict;
var
  x: t15;
  c, len: r15;
  w: integer;
begin
    reset(dict, '2 ');
    len := 1;
    { No 1-letter words! }
    len := len+1;
    for w := 1 to wc2 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a2[w][c] := x[c];
    end;
    writeln('dict a2 read from ', a2[1], ' to ', a2[wc2]);
    close(dict);
    reset(dict, '3 ');
    len := len+1;
    for w := 1 to wc3 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a3[w][c] := x[c];
    end;
    writeln('dict a3 read from ', a3[1], ' to ', a3[wc3]);
    close(dict);
    reset(dict, '4 ');
    len := len+1;
    for w := 1 to wc4 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a4[w][c] := x[c];
    end;
    writeln('dict a4 read from ', a4[1], ' to ', a4[wc4]);
    close(dict);
    reset(dict, '5 ');
    len := len+1;
    for w := 1 to wc5 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a5[w][c] := x[c];
    end;
    writeln('dict a5 read from ', a5[1], ' to ', a5[wc5]);
    close(dict);
    reset(dict, '6 ');
    len := len+1;
    for w := 1 to wc6 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a6[w][c] := x[c];
    end;
    writeln('dict a6 read from ', a6[1], ' to ', a6[wc6]);
    close(dict);
    reset(dict, '7 ');
    len := len+1;
    for w := 1 to wc7 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a7[w][c] := x[c];
    end;
    writeln('dict a7 read from ', a7[1], ' to ', a7[wc7]);
    close(dict);
    reset(dict, '8 ');
    len := len+1;
    for w := 1 to wc8 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a8[w][c] := x[c];
    end;
    writeln('dict a8 read from ', a8[1], ' to ', a8[wc8]);
    close(dict);
    reset(dict, '9 ');
    len := len+1;
    for w := 1 to wc9 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a9[w][c] := x[c];
    end;
    writeln('dict a9 read from ', a9[1], ' to ', a9[wc9]);
    close(dict);
    reset(dict, '10');
    len := len+1;
    for w := 1 to wc10 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a10[w][c] := x[c];
    end;
    writeln('dict a10 read from ', a10[1], ' to ', a10[wc10]);
    close(dict);
    reset(dict, '11');
    len := len+1;
    for w := 1 to wc11 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a11[w][c] := x[c];
    end;
    writeln('dict a11 read from ', a11[1], ' to ', a11[wc11]);
    close(dict);
    reset(dict, '12');
    len := len+1;
    for w := 1 to wc12 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a12[w][c] := x[c];
    end;
    writeln('dict a12 read from ', a12[1], ' to ', a12[wc12]);
    close(dict);
    reset(dict, '13');
    len := len+1;
    for w := 1 to wc13 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a13[w][c] := x[c];
    end;
    writeln('dict a13 read from ', a13[1], ' to ', a13[wc13]);
    close(dict);
    reset(dict, '14');
    len := len+1;
    for w := 1 to wc14 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a14[w][c] := x[c];
    end;
    writeln('dict a14 read from ', a14[1], ' to ', a14[wc14]);
    close(dict);
    reset(dict, '15');
    len := len+1;
    for w := 1 to wc15 do begin
      for c := 1 to len do begin
        read(dict, x[c]); x[c+len] := x[c];
      end;
      readln(dict);
      hsort(x, len);
      for c := 1 to len*2 do a15[w][c] := x[c];
    end;
    writeln('dict a15 read from ', a15[1], ' to ', a15[wc15]);
    close(dict);
end {InitDict};

procedure InitBoard;
var
  x: xtilerange; y: ytilerange;
  n: integer; {xtilerange Union ytilerange}
  {lfact, wfact: integer;}
  ch: char;
  t: tileindex;

  procedure SetScoreAndFreq(ch: char; sc: score; fr: freq);
  begin
    letterscore[ch] := sc; letterfreq[ch] := fr;
  end;

begin
  SetScoreAndFreq('a', 1, 9);
  SetScoreAndFreq('b', 3, 2);
  SetScoreAndFreq('c', 3, 2);
  SetScoreAndFreq('d', 2, 4);
  SetScoreAndFreq('e', 1, 12);
  SetScoreAndFreq('f', 4, 2);
  SetScoreAndFreq('g', 2, 3);
  SetScoreAndFreq('h', 4, 2);
  SetScoreAndFreq('i', 1, 9);
  SetScoreAndFreq('j', 8, 1);
  SetScoreAndFreq('k', 5, 1);
  SetScoreAndFreq('l', 1, 4);
  SetScoreAndFreq('m', 3, 2);
  SetScoreAndFreq('n', 1, 6);
  SetScoreAndFreq('o', 1, 8);
  SetScoreAndFreq('p', 3, 2);
  SetScoreAndFreq('q', 10, 1);
  SetScoreAndFreq('r', 1, 6);
  SetScoreAndFreq('s', 1, 4);
  SetScoreAndFreq('t', 1, 6);
  SetScoreAndFreq('u', 1, 4);
  SetScoreAndFreq('v', 4, 2);
  SetScoreAndFreq('w', 4, 2);
  SetScoreAndFreq('x', 8, 1);
  SetScoreAndFreq('y', 4, 2);
  SetScoreAndFreq('z', 10, 1);
  SetScoreAndFreq(BLANKLETTER, 0, 2);

  for x := XMIN-1 to XMAX+1 do
    for y := YMIN-1 to YMAX+1 do begin
      ApparentBoardLetter[x, y] := FREELETTER;
      BoardTile[x, y] := FREETILE;
      special[x, y].letterfactor := 1;
      special[x, y].wordfactor := 1;
    end;

  special[8, 8].wordfactor := 2;


  special[1, 1].wordfactor := 3;
  special[8, 1].wordfactor := 3;
  special[15, 1].wordfactor := 3;

  special[1, 8].wordfactor := 3;
  special[15, 8].wordfactor := 3;

  special[1, 15].wordfactor := 3;
  special[8, 15].wordfactor := 3;
  special[15, 15].wordfactor := 3;

  for n := 1 to 4 do begin
    special[1+n, 1+n].wordfactor := 2;
    special[15-n, 1+n].wordfactor := 2;
    special[1+n, 15-n].wordfactor := 2;
    special[15-n, 15-n].wordfactor := 2;
  end;

  special[6, 2].letterfactor := 3;
  special[10, 2].letterfactor := 3;

  special[2, 6].letterfactor := 3;
  special[6, 6].letterfactor := 3;
  special[10, 6].letterfactor := 3;
  special[14, 6].letterfactor := 3;

  special[2, 10].letterfactor := 3;
  special[6, 10].letterfactor := 3;
  special[10, 10].letterfactor := 3;
  special[14, 10].letterfactor := 3;

  special[6, 14].letterfactor := 3;
  special[10, 14].letterfactor := 3;

  special[ 1,  4].letterfactor := 2;
  special[ 1, 12].letterfactor := 2;
  special[15,  4].letterfactor := 2;
  special[15, 12].letterfactor := 2;

  special[ 4,  1].letterfactor := 2;
  special[12,  1].letterfactor := 2;
  special[ 4, 15].letterfactor := 2;
  special[12, 15].letterfactor := 2;

  special[ 7,  7].letterfactor := 2;
  special[ 7,  9].letterfactor := 2;
  special[ 9,  7].letterfactor := 2;
  special[ 9,  9].letterfactor := 2;

  special[ 8,  4].letterfactor := 2;
  special[ 8, 12].letterfactor := 2;
  special[ 4,  8].letterfactor := 2;
  special[12,  8].letterfactor := 2;

  special[ 7,  3].letterfactor := 2;
  special[ 9,  3].letterfactor := 2;
  special[ 3,  7].letterfactor := 2;
  special[ 3,  9].letterfactor := 2;
  special[13,  7].letterfactor := 2;
  special[13,  9].letterfactor := 2;
  special[ 7, 13].letterfactor := 2;
  special[ 9, 13].letterfactor := 2;
{
  for y := YMIN to YMAX do begin
    for x := XMIN to XMAX do begin
      lfact := special[x, y].letterFactor;
      if lfact > 1 then Write(lfact:1) else begin
        wfact := special[x, y].wordFactor;
        if wfact = 2 then Write('D') else if wfact = 3 then Write('T')
        else Write('.')
      end;
    end;
    WriteLn;
  end;
  WriteLn;
}
  LastTile := 0;
  if not eof(Board) then begin
    if Axis = Horizontal then begin
    for y := YMIN to YMAX do begin
      for x := XMIN to XMAX do begin
        Read(Board, ch); {Write(ch);}
        if ch = '.' then ch := FREELETTER else empty := false;
        if (ch in ['A'..'Z']) then begin
          ch := CHR(ORD(ch)+32);
          BoardTile[x,y] := ch;
          ApparentBoardLetter[x, y] := BLANKLETTER;
          if letterfreq[BLANKLETTER] = 0 then begin
            WriteLn('ILLEGAL BOARD! Too many letter blanks (', ch, ''')!');
          end else begin
            letterfreq[BLANKLETTER] := letterfreq[BLANKLETTER]-1;
          end;
        end else if ch = FREELETTER then begin
          BoardTile[x,y] := FREELETTER;
          ApparentBoardLetter[x, y] := FREETILE;
        end else begin
          BoardTile[x,y] := ch;
          ApparentBoardLetter[x, y] := ch;
          if letterfreq[ch] = 0 then begin
            WriteLn('ILLEGAL BOARD! Too many letter ', ch, '''s!');
          end else begin
            letterfreq[ch] := letterfreq[ch]-1;
          end;
        end;
        if ch <> FREELETTER then begin
          special[x, y].letterfactor := 1;
          special[x, y].wordfactor := 1;
        end;
      end;
      ReadLn(Board); {WriteLn;}
    end;
    end else begin
    for x := XMIN to XMAX do begin
      for y := YMIN to YMAX do begin
        Read(Board, ch); {Write(ch);}
        if ch = '.' then ch := FREELETTER else empty := false;
        if (ch in ['A'..'Z']) then begin
          ch := CHR(ORD(ch)+32);
          BoardTile[x,y] := ch;
          ApparentBoardLetter[x, y] := BLANKLETTER;
        end else if ch = FREELETTER then begin
          BoardTile[x,y] := FREELETTER;
          ApparentBoardLetter[x, y] := FREETILE;
        end else begin
          BoardTile[x,y] := ch;
          ApparentBoardLetter[x, y] := ch;
        end;
        if ch <> FREELETTER then begin
          special[x, y].letterfactor := 1;
          special[x, y].wordfactor := 1;
        end;
      end;
      ReadLn(Board); {WriteLn;}
    end;
    end;
    if not eof(Board) then begin
      while not eoln(Board) do begin
        Read(Board, ch);
        LastTile := LastTile+1;
        if ch = ' ' then ch := BLANKLETTER;
        HeldLetter[LastTile] := ch;
      end;
      ReadLn(Board);
    end;
    if not eof(Board) then begin
      Readln(Board, move);
    end;
  end else begin
    { Empty board. (Must place first word over centre square) }
  end;

  {write('Tiles = ');}
  EVERYHELDTILE := [];
  if LastTile > 0 then for t := 1 to LastTile do begin
    HeldTile[t] := HeldLetter[t];
    EVERYHELDTILE := EVERYHELDTILE + [HeldTile[t]];
    {Write(HeldLetter[t]);}
  end;
  {WriteLn;}

end {InitBoard};


function CheckW(var userword: t15; len: r15; anag: boolean;
                var where: integer): boolean;
var
  lo, mid, hi: integer; Check2: boolean;
  i, alen: r15; s, a: char;
begin
    Check2 := false;
    alen := len; mid := -1;
    if (not(anag)) then len := len*2;
    case alen of
    1: begin end;
    2: begin
         for lo := 1 to len do s2[lo] := userword[lo];
         lo := 1; hi := wc2;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s2[i]; a := a2[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
    3: begin
         for lo := 1 to len do s3[lo] := userword[lo];
         lo := 1; hi := wc3;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s3[i]; a := a3[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
    4: begin
         for lo := 1 to len do s4[lo] := userword[lo];
         lo := 1; hi := wc4;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s4[i]; a := a4[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
    5: begin
         for lo := 1 to len do s5[lo] := userword[lo];
         lo := 1; hi := wc5;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s5[i]; a := a5[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
    6: begin
         for lo := 1 to len do s6[lo] := userword[lo];
         lo := 1; hi := wc6;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s6[i]; a := a6[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
    7: begin
         for lo := 1 to len do s7[lo] := userword[lo];
         lo := 1; hi := wc7;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s7[i]; a := a7[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
{  ------------------------------------------ Speed up failed searches
           i := 0;
           repeat
             i := i+1;
             ch := a7[lo][i];                This can all be done
             if ch = a7[hi][i] then begin    much more efficiently in C
               if ch <> s7[i] then begin     using pointers...
                 check2 := false;
                 goto 999; ->%return
               end;
             end else begin
               i := len; ->%exit
             end;
           until i = len;
----------------------------------------------------------------------- }
         end;
       end;
    8: begin
         for lo := 1 to len do s8[lo] := userword[lo];
         lo := 1; hi := wc8;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s8[i]; a := a8[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
    9: begin
         for lo := 1 to len do s9[lo] := userword[lo];
         lo := 1; hi := wc9;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s9[i]; a := a9[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
   10: begin
         for lo := 1 to len do s10[lo] := userword[lo];
         lo := 1; hi := wc10;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s10[i]; a := a10[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
   11: begin
         for lo := 1 to len do s11[lo] := userword[lo];
         lo := 1; hi := wc11;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s11[i]; a := a11[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
   12: begin
         for lo := 1 to len do s12[lo] := userword[lo];
         lo := 1; hi := wc12;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s12[i]; a := a12[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
   13: begin
         for lo := 1 to len do s13[lo] := userword[lo];
         lo := 1; hi := wc13;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s13[i]; a := a13[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
   14: begin
         for lo := 1 to len do s14[lo] := userword[lo];
         lo := 1; hi := wc14;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s14[i]; a := a14[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
   15: begin
         for lo := 1 to len do s15[lo] := userword[lo];
         lo := 1; hi := wc15;
         while (lo <= hi) do begin
           mid := (lo + hi) div 2;

           i := 0;
           repeat
            i := i+1;
            s := s15[i]; a := a15[mid][i];
           until (s <> a) or (i = len);

           if (s < a) then begin
             hi := mid-1;
           end else if (s > a) then begin
             lo := mid+1;
           end else begin
             check2 := true; lo := 1; hi := 0;
           end;
         end;
       end;
  end;
  where := mid;
{
  if Check2 then begin
    writeln('Successful check of ', userword:len);
  end;
}
  CheckW := Check2
end {CheckW};

function Check(var userword: t15; len: integer): boolean;
var
  word: t15;
  i: r15;
  where: integer;
begin
  for i := 1 to len do begin
    word[i+len] := userword[i];
    word[i] := userword[i];
  end;
  hsort(word, len);
  Check := CheckW(word, len, false, where);
end {Check};

function CheckAnag(var w: t15; len: r15; var w1, w2: integer;
                   var outword: t15): boolean;
{TO DO - this is a bodge: outword is assigned here; it shouldn't
 be -- there may be multiple 'outword's so they should
 be extracted outside. }
var CheckAnag2: boolean; i: r15;
begin
  { Returns first one. We then have to search back and forward
    for extra matches. TO DO...}
  hsort(w, len);
  CheckAnag2 := CheckW(w, len, true, w1); w2 := w1;
  if CheckAnag2 then case len of
  2: for i := 1 to len do outword[i] := a2[w1][i+len];
  3: for i := 1 to len do outword[i] := a3[w1][i+len];
  4: for i := 1 to len do outword[i] := a4[w1][i+len];
  5: for i := 1 to len do outword[i] := a5[w1][i+len];
  6: for i := 1 to len do outword[i] := a6[w1][i+len];
  7: for i := 1 to len do outword[i] := a7[w1][i+len];
  8: for i := 1 to len do outword[i] := a8[w1][i+len];
  9: for i := 1 to len do outword[i] := a9[w1][i+len];
  10: for i := 1 to len do outword[i] := a10[w1][i+len];
  11: for i := 1 to len do outword[i] := a11[w1][i+len];
  12: for i := 1 to len do outword[i] := a12[w1][i+len];
  13: for i := 1 to len do outword[i] := a13[w1][i+len];
  14: for i := 1 to len do outword[i] := a14[w1][i+len];
  15: for i := 1 to len do outword[i] := a15[w1][i+len];
  end;
  CheckAnag := CheckAnag2;
end {CheckAnag};

function SpellCheckVertical(x: xtilepos; YPos: ytilepos; Try: tile): boolean;

{ Places letter Try at (x,y) and sees if it forms a valid word.  If so,
  returns TRUE and the score of the rest of the word NOT COUNTING the
  placed letter itself.  This allows the caller to calculate double-letter/
  double-word scores more efficiently than we could. }

var w: t15; y, y1, y2: ytilepos; yy: 1..16;
begin
  y1 := YPos; y2 := YPos; yy := 1;
  while ApparentBoardLetter[x,y1-1] <> FREELETTER do y1 := y1-1;
  while ApparentBoardLetter[x,y2+1] <> FREELETTER do y2 := y2+1;
  if y1 < YPos then for y := y1 to YPos-1 do begin
    w[yy] := ApparentBoardLetter[x,y]; yy := yy+1;
  end;
  w[yy] := Try; yy := yy+1;
  if y2 > YPos then for y := YPos+1 to y2 do begin
    w[yy] := ApparentBoardLetter[x,y]; yy := yy+1;
  end;
  SpellCheckVertical := Check(w, yy-1);
end {SpellCheckVertical};


procedure ConstrainPlacements(Board: xytilearray;
                              BoardLetter: xyletterarray;
                              var Choice: choicearray;
                              var VScore: scorearray;
                              var Min, Max: allowed;
                              HeldTile: rack;
                              LastTile: tileindex);

{ This procedure is the high-level harness which takes the board before a
  horizontal move and marks all the positions which are restricted by the
  adjacent vertical words, i.e. certain tiles cannot be placed in certain
  free squares because they abut with other tiles above or below to form a
  non-existent word.  This level of pruning dramatically reduces the search
  time when generating possible words. }

{ It also works out how many letters may be placed horizontally starting
  at each position.  The info learned about vertical abutment is used to
  help clip long words short! Answers written to min[], max[]. }

{ STILL TO DO: When placing wild letters next to fixed letters, check
  in a di-/tri-graph table whether they are valid combinations.  Will
  trim even more off round the edges... }

var
  ThisTile: tile; ThisLet: letter;
  DebugTileSet: tileset; {}t: tileindex;{} {Also debug}
  WildLetter: letter;
  XPos: xtilepos; YPos: ytilepos;
  EachTile: tileindex;

  c: -1..7; xx: 0..16; { For horizontal placement ode }
  x: xtilepos; y: ytilepos;
  minxy, maxxy: -1..7;

  { VScore records the score of words formed by vertical abutment when
    placing a word horizontally.  It would be simpler if VScore were a
    3-D array, also indexed by the letters A-Z, recording the individual
    scores of placing each of those letters in the particular square.
       However, a vast space-saving can be had by omitting this index
    and calculating the score on the fly each time.  To do this reasonably
    efficiently, VScore holds the total value of all the other tiles in
    the vertical word except the tile being placed.  The tile can be
    placed only if Choice[x,y] says it can.       As double-word and
    double-letter scores are only significant for tiles being placed
    (not tiles already down) we can calculate the full value of such
    vertical words as follows:

       1) Take VScore[x,y] which already contains the sum of all the
          other letters in the vertical word.

       2) Add the score for the tested letter at x,y multiplied by
          any letter bonus allowed for the single letter at x,y.

       3) Multiply the result by any word bonus allowed for x,y.
  }

  function ScoreVertical(x: xtilepos; YPos: ytilepos): Score;
  var y, y1, y2: ytilepos; vtot: score;
  begin
    vtot := 0;
    y1 := YPos; y2 := YPos;
    while ApparentBoardLetter[x,y1-1] <> FREELETTER do y1 := y1-1;
    while ApparentBoardLetter[x,y2+1] <> FREELETTER do y2 := y2+1;
    if y1 < ymin then writeln('y1 < ymin');
    if y2 > ymax then writeln('y2 > ymin');
    if y1 < YPos then for y := y1 to YPos-1 do begin
      vtot := vtot + letterscore[ApparentBoardLetter[x,y]];
    end;
    if y2 > YPos then for y := YPos+1 to y2 do begin
      vtot := vtot + letterscore[ApparentBoardLetter[x,y]];
    end;
    ScoreVertical := vtot;
  end {ScoreVertical};

begin
  BlankTileHeld := FALSE;
  DebugTileSet := [];
  for EachTile := 1 to LastTile do begin
    if HeldTile[EachTile] = BLANKTILE then BlankTileHeld := TRUE;
    DebugTileSet := DebugTileSet+[HeldTile[EachTile]];
  end;

  if (move = 1) and empty then begin
    { Initial board - lets start horizontally only! }
    for YPos := YMIN to YMAX do begin
      for XPos := XMIN to XMAX do begin
        Choice[XPos,YPos] := [];
        VScore[XPos,YPos] := -1; { -1 means illegal to place a tile here }
        Min[XPos,YPos] := 0;
        Max[XPos,YPos] := 0;
      end;
    end;
    for x := 8 downto 2 do begin
      Min[x,8] := 9-x; Max[x,8] := 7;
      VScore[x,8] := 0;
      VScore[8,8] := 0;
      if BlankTileHeld then begin
        Choice[x,8] := EVERYHELDTILE;
        { ??? Looking at this today (1999), this looks the wrong way round... }
      end else begin
        Choice[x,8] := EVERYTILE;
      end;
    end;
  end else begin
    for YPos := YMIN to YMAX do begin {Try every Y position}
      for XPos := XMIN to XMAX do begin {Try every X Position}
        Choice[XPos,YPos] := [];
        VScore[XPos,YPos] := -1; { -1 means illegal to place a tile here }
        ThisTile := Board[XPos,YPos];
        if ThisTile=FREETILE then begin {Tile present test}
          if (Board[XPos,YPos-1]=FREETILE)
          and (Board[XPos,YPos+1]=FREETILE) then begin {Adjacency test}
                          { Unconstrained }
            { When holding blank can place any VALID letter here }
            if blanktileheld then begin
              Choice[XPos,YPos] := EVERYTILE;
            end else begin
              Choice[XPos,YPos] := EVERYHELDTILE;
            end;
            VScore[XPos,YPos] := 0;  { 0 means doesn't form vertical word }
          end else begin {Adjacency test}
            { Will abut vertically to form a word... }
            VScore[XPos,YPos] := ScoreVertical(XPos, YPos);
            { Evaluate once as it is the same for all 'ch' }
            if BlankTileHeld then begin {Constrained, but holding a blank test}
              { Loop over 'a' to 'z' if blank held. }
              for WildLetter := ALETTER to ZLETTER do begin {Try all if blank}
                ThisTile := WildLetter;
                if SpellCheckVertical(XPos, YPos, ThisTile) then begin
                  Choice[XPos,YPos] := Choice[XPos,YPos]+[ThisTile];
                end;
              end {Try all if blank};
            end else begin {Constrained, but holding a blank test}
              for EachTile := 1 to LastTile do begin {Try each tile}
                ThisTile := HeldTile[EachTile];
                ThisLet := HeldLetter[EachTile];
                if ThisTile<>BLANKTILE then begin {Placing blank test}
                  if SpellCheckVertical(XPos, YPos, ThisTile) then begin
                                                                   {Test word}
                    Choice[XPos,YPos] := Choice[XPos,YPos]+[ThisTile];
                    {Note: this means that
                    VerticalScore(x,y)=(VScore[XPos,YPos]+tileval*lfact)*wfact
                    where tileval can be 0 if a blank is being used,
                    and lfact & wfact are the bonus scores for the tile
                    (x, y) on which the character is to be placed.
                    }
                  end {Test word};
                end else begin {Placing blank test}
                  { Done outside the loop for efficiency }
                end {Placing blank test}; 
              end {Try each tile};
            end {Constrained, but holding a blank test};
            { Note that for consistency's sake, we should set the score
              to -1 again if NONE of the letters fitted... - but no-one
              will be looking anyway! }
          end {Adjacency test};
        end else begin {Tile present test}
          Choice[XPos,YPos] := [Board[XPos,YPos]];
          { No choice - tile already down! (should VScore be set to -1?)}
        end {Tile present test};
      end {Try every X position};
    end {Try every Y position};

    {  Now work out horizontal constraints. }
    for y := YMIN to YMAX do begin
      for x := XMIN to XMAX do begin
        xx := x; c:= 0;
        if BoardLetter[x-1,y] <> FREELETTER then begin { Hence why 0 to 16 }
          { This starting point subsumed by one to the left of it. }
          minxy := 0; maxxy := 0;
        end else begin
          minxy := 0; maxxy := -1;
          xx := x;
          while (c >= 0) and (c < LastTile) do begin
            while (xx <= XMAX) and
             (BoardLetter[xx,y] <> FREELETTER) do xx := xx+1;
            { xx points to next free letter }
            if xx > XMAX then begin
              c := -1; { to force loop exit }
            end else begin
              c := c+1; { Place a tile }
              if (minxy = 0) and
               ((BoardLetter[xx-1,y] <> FREELETTER) or
                  {            | <- THIS CASE ONLY SIGNIFICANT ON 1ST TILE
                    (could be neater code!)}
                (BoardLetter[xx,y-1] <> FREELETTER) or
                (BoardLetter[xx,y+1] <> FREELETTER) or
                (BoardLetter[xx+1,y] <> FREELETTER)) then begin
                { As soon as abutment found, note no. of tiles placed. }
                minxy := c;
              end;
              xx := xx+1;
              maxxy := c;
            end;
          end;
        end;
        { TO DO: Remember a max of 1 horizontally with no left or right
          partners is actually a vertical! - and should not be counted. }
        if minxy = 0 then maxxy := 0;
                       { If couldn't place any then max invalid }  
        if maxxy < 0 then maxxy := 0; { -1 flag from above needs patching }
        if (minxy = 1) and (BoardLetter[x,y] = FREELETTER) then begin
          if (BoardLetter[x-1,y] = FREELETTER) and 
           (BoardLetter[x+1,y] = FREELETTER) then begin
            if maxxy > 1 then minxy := 2;
          end;
        end;
        min[x,y] := minxy; max[x,y] := maxxy;
      end;
    end;
  end;
{}
  for y := YMIN to YMAX do begin
    for x := XMIN to XMAX do begin
      write(ApparentBoardLetter[x,y]);
    end;
    write('   ');
    for x := XMIN to XMAX do begin
      write(min[x,y]:1);
    end;
    write('   ');
    for x := XMIN to XMAX do begin
      write(max[x,y]:1);
    end;
    write('   ');
    for x := XMIN to XMAX do begin
      if (Choice[x,y] <> DebugTileSet) then begin
        write('*');
      end else begin
        write(ApparentBoardLetter[x,y]);
      end;
    end;
    writeln
  end;

  for y := YMIN to YMAX do begin
    for t := 1 to 4 do begin
      for x := XMIN to XMAX do begin
        if (Choice[x,y] <> DebugTileSet)
           and (HeldTile[t] in Choice[x,y]) then begin
          write(HeldLetter[t]);
        end else begin
          write(ApparentBoardLetter[x,y]);
        end;
      end;
      Write('  ');
    end;
    writeln;
  end;
  writeln;
  for y := YMIN to YMAX do begin
    for t := 5 to 7 do begin
      for x := XMIN to XMAX do begin
        if (Choice[x,y] <> DebugTileSet)
           and (HeldTile[t] in Choice[x,y]) then begin
          write(HeldLetter[t]);
        end else begin
          write(ApparentBoardLetter[x,y]);
        end;
      end;
      Write('  ');
    end;
    writeln;
  end;
{}
end {ConstrainPlacements};

function Place(var Tiles: rack; var TileLetter: letterrack;
               last: tileindex; x: xtilerange; y: ytilerange;
           var word: t15;
           var length: xtilerange;
           var totscore: score): boolean;
label 99;
var w: r15; t: tileindex; htot, vtot: score; wfact: score;
  place2: boolean;
begin
  length := 0;
  t := 0; w := 0; totscore := 0; vtot := 0; htot := 0; wfact := 1;
{ TODO: Clipping against validity mask goes here! - also di-/tri-gram checks }
  repeat
    if ApparentBoardLetter[x,y] = FREELETTER then begin
      if t = last then begin
        { End of word }
      end else begin
        t := t+1;
        w := w+1;
        word[w] := Tiles[t];
        if not(word[w] in ['a'..'z']) then begin
          WriteLn('Internal error 3: ', word[w]);
        end;
        if not (Tiles[t] in Choice[x,y]) then begin
          Place := false;
          totscore := 0;
          goto 99;
        end;
        if VScore[x,y] > 0 then begin
          { TO DO: Only add vscore if forms a word vertically.
            This is too cheap a test as it would go wrong if
            there were a single blank lying above a letter -
            The score would appear 0 so the value of the
            letter itself would not be added. (best to check word length?)}
          vtot := vtot+(   (VScore[x,y]+(letterscore[TileLetter[t]]
                                        *special[x,y].letterfactor))
                           *special[x,y].wordfactor
                        );
        end;
        wfact := wfact*special[x,y].wordfactor;
        htot := htot + letterscore[TileLetter[t]]*special[x,y].letterfactor;
      end;
    end else begin
      w := w+1;
      word[w] := BoardTile[x,y]; {Was ApparentBoardLetter...}
      if not(word[w] in ['a'..'z']) then begin
        WriteLn('Internal error 2: ', word[w]);
      end;
      htot := htot + letterscore[word[w]];
    end;
    x := x + 1;
  until ((t = last) and (ApparentBoardLetter[x,y] = FREELETTER)) or
          (x > XMAX);
  htot := htot*wfact; if last = 7 then htot := htot + 50;
  totscore := htot+vtot;
  length := w;
  Place2 := Check(word, w);
  Place := Place2;
99:
end {Place};
                   { VAR because temp being updated ..........}
function PlaceAnag(var Tiles: rack; var TileLetter: letterrack;
                   var outrack: rack; var outlet: letterrack;
               last: tileindex; x: xtilerange; y: ytilerange;
           var word: t15;
           var length: xtilerange;
           var totscore: score): boolean;
var
  ix: xtilerange;
  w1, w2: integer;
  i, w: r15;
  t: tileindex;
  outword, wordlet: t15;
  where: array [tileindex] of xtilerange;
begin
  ix := x;
  PlaceAnag := false;
  length := 0; totscore := 0;
  t := 0; w := 0;
{ TODO: Clipping against validity mask goes here! - also di-/tri-gram checks }
  repeat
    if ApparentBoardLetter[x,y] = FREELETTER then begin
      if t = last then begin
        { End of word }
      end else begin
        t := t+1;
        w := w+1;
        word[w] := Tiles[t];
        wordlet[w] := TileLetter[t];
        where[t] := w;
      end;
    end else begin
      w := w+1;
      word[w] := BoardTile[x,y]; {Was ApparentBoardLetter...}
      wordlet[w] := ApparentBoardLetter[x,y];
    end;
    x := x + 1;
  until ((t = last) and (ApparentBoardLetter[x,y] = FREELETTER)) or
          (x = XMAX+1);
  { If anagram of built word exists, try to fit it. If fits,
    return equivalent of 'old' Place() }
  if CheckAnag(word, w, w1, w2, outword) then begin
    if w1<>w2 then begin
      writeln('Warning: words temporarily ignored!');
    end;
    { ***TO DO: assign outword from anagdict[w1]

      word     outword
      abCDe -> DeCab    lowercase: tiles; uppercase: board
t     12  3    12345

      t = 3; where[1] = 4
             where[2] = 5
             where[3] = 2


      tiles in = abe   --- tiles out = eab !!!
    
      ditto outrack - but harder to infer blank pos if present?
      SO: take blanks out a level! }

      t := 0;
      for i := 1 to w do begin
        if ApparentBoardLetter[ix+i-1,y] = FREELETTER then begin
          t := t+1;
          outrack[t] := outword[i];
          outlet[t] := outword[i];
        end;
      end;

    { Do a similar loop to see if already-placed letters have moved... }
    PlaceAnag := Place(outrack, outlet, last, ix, y, word, length, totscore);
  end;
end {PlaceAnag};

procedure CheckOne(var t: rack; var tl: letterrack; N: tileindex;
                   x: xtilepos; y: ytilepos);
var
  ot: rack; otl: letterrack;
  ThisScore: score; length: xtilerange;
  xx: xtilerange; yy: ytilerange; ch: char; nexttl: tileindex;
  word: t15;
begin
      { Try placing words of triallength at (x,y) from TryTile }
      if PlaceAnag(t, tl, ot, otl, N, x, y, word, length, thisscore) then begin
        { Should be... while PlaceAnag() ... }
        { SO CURRENT VERSION SHUFFLES T & TL FOR YOU!!! }
        if ThisScore > BestScore then begin
          bestt := ot; besttl := otl; bestcount := N; bestaxis := axis;
          bestword := word; bestlength := length;
          if Axis = horizontal then begin
            bestx := x; besty := y;
          end else begin
            bestx := y; besty := x;
          end;
          { *** }
          nexttl := 0;
          Write('Placing ');
          for nexttl := 1 to N do begin
            if tl[nexttl] = BLANKLETTER then begin  {BUG: was FREELETTER}
              Write('(Blank ', t[nexttl], ')');
            end else Write(t[nexttl]); 
          end;
          Write(' to give ', word:length);
          if Axis = Horizontal then begin
            Write(' across at ', x, ', ', y);
          end else begin
            Write(' down at ', y, ', ', x);
          end;
          WriteLn(' scoring ', ThisScore);

          nexttl := 0;
          for xx := x to x+length-1 do begin
            if (ApparentBoardLetter[xx,y]=FREELETTER) AND
               ((ApparentBoardLetter[xx,y-1]<>FREELETTER) or
                (ApparentBoardLetter[xx,y+1]<>FREELETTER)) then begin
              Write('  (Also forms ');
              ch := word[xx-x+1];
              yy := y;
              while ApparentBoardLetter[xx,yy-1] <> FREELETTER do yy := yy-1;
              while yy < y do begin
                Write(ApparentBoardLetter[xx,yy]);
                yy := yy+1;
              end;
              Write(CHR(ORD(ch)-32));
              yy := y;
              while ApparentBoardLetter[xx,yy+1] <> FREELETTER do begin
                yy := yy+1;
                Write(ApparentBoardLetter[xx,yy]);
              end;
              WriteLn(')');
            end;
          end;
          { *** }
          BestScore := ThisScore;
        end;
      end else begin
      end;
end {CheckOne};

procedure Perms(var t: rack; var tl: letterrack; N: tileindex);
var x: xtilepos; y: ytilepos;
begin
  for y := YMIN to YMAX do begin
    for x := XMIN to XMAX-N+1 do begin
      if (min[x,y] <= N) and (N <= max[x,y]) then CheckOne(t, tl, n, x, y);
    end;
  end;
end {Perms};

procedure PermNofM(var T: rack; var L: letterrack; N, M: tileindex);
var C0, C1, C2, C3, C4, C5, C6, C7: tileindex; OT: rack; OL: letterrack;
begin
  C0 := 0;
  if N>=1 then for C1 := C0+1 to M do begin
    OT[1] := T[C1];
    OL[1] := L[C1];
    if N>=2 then for C2 := C1+1 to M do begin
      OT[2] := T[C2];
      OL[2] := L[C2];
      if N>=3 then for C3 := C2+1 to M do begin
        OT[3] := T[C3];
        OL[3] := L[C3];
        if N>=4 then for C4 := C3+1 to M do begin
          OT[4] := T[C4];
          OL[4] := L[C4];
          if N>=5 then for C5 := C4+1 to M do begin
            OT[5] := T[C5];
            OL[5] := L[C5];
            if N>=6 then for C6 := C5+1 to M do begin
              OT[6] := T[C6];
              OL[6] := L[C6];
              if N>=7 then for C7 := C6+1 to M do begin
                OT[7] := T[C7];
                OL[7] := L[C7];
                Perms(OT, OL, N);
              end else begin
                Perms(OT, OL, N);
              end;
            end else begin
              Perms(OT, OL, N);
            end;
          end else begin
            Perms(OT, OL, N);
          end;
        end else begin
          Perms(OT, OL, N);
        end;
      end else begin
        Perms(OT, OL, N);
      end;
    end else begin
      Perms(OT, OL, N);
    end;
  end;
end {PermMofN};

procedure PlaceHorizontalWords(var HeldTile: rack;
                               LastTile: tileindex);
var
  N: tileindex;
begin
  { It MIGHT be better to do the x=1 to 15, y=1 to 15 loop out
    here and the combs/perms inside it - then the comb/perm
    generators can do high-level clipping early on when they
    know that a word can't be placed on a site because of limitations
    and thus save a factorial no. of tests rather than just one! -
    it depends on the relative costs of spell-checking vs comb
    generation.  If this were to be done, the blank generation
    should also be done at that level. }

  if LastTile > 0 then begin
    N := 0;
    repeat
      N := N+1;
      PermNofM(HeldTile, HeldLetter, N, LastTile);
    until (N = LastTile);
  end;
end {PlaceHorizontalWords};

procedure Initialise;
var
  XPos: 0..16; YPos: 0..16;
begin

  ATILE    := 'a';
  BLANKLETTER := 123{letter(123)}
               {SUCC(ZLETTER)};    { Constants which I am not willing }
  BLANKTILE := BLANKLETTER;
  FREELETTER := SUCC(BLANKLETTER); { to describe as literals }
  FREETILE := FREELETTER;
  EVERYTILE := ['a'..'z'];

  for YPos := YMIN-1 to YMAX+1 do begin
    for XPos := XMIN-1 to XMAX+1 do begin
      BoardTile[XPos,YPos] := FREETILE;
      ApparentBoardLetter[XPos,YPos] := FREELETTER;
    end {all Xs};
  end {all Ys};

end {Initialise};

procedure PlaceBest;
var
  x: xtilepos; y: ytilepos;
  nexttl, htile: tileindex; ch: char;
  tile: letter;
begin
  { Or... can't move? }
  WriteLn('****************************');
  Write('My move: place ');
  x := bestx; y := besty;
  if bestaxis = vertical then begin

  for nexttl := 1 to bestcount do begin
    while ApparentBoardLetter[x,y] <> FREELETTER do y := y+1;
    ch := besttl[nexttl];
    htile := 0;
    repeat
      htile := htile+1;
    until HeldLetter[htile] = ch;
    {Assert: Never tries to place a letter not held - of course, can't
             trust human players to be so honest... }
    HeldTile[htile] := FREETILE;
    HeldLetter[htile] := FREELETTER;
    if besttl[nexttl] = BLANKLETTER then begin
      ch := CHR(ORD(bestt[nexttl])-32);
      tile := bestt[nexttl];
    end else begin
      ch := bestt[nexttl];
      tile := ch;
    end;
    Write(ch);
    ApparentBoardLetter[x,y] := besttl[nexttl];
    BoardTile[x,y] := tile;
    special[x, y].letterfactor := 1;
    special[x, y].wordfactor := 1;
    { to do: check validity of freq[] }
    letterfreq[besttl[nexttl]] := letterfreq[besttl[nexttl]]-1;
    if y < YMAX then y := y+1;
  end;
  WriteLn(' at ', bestx, ',', besty, ' down to make ', bestword:bestlength,
    ' (scoring ', bestscore, ')');
  end else begin

  for nexttl := 1 to bestcount do begin
    while ApparentBoardLetter[x,y] <> FREELETTER do begin
      x := x+1;
    end;
    ch := besttl[nexttl];
    htile := 0;
    repeat
      htile := htile+1;
    until HeldLetter[htile] = ch;
    {Assert: Never tries to place a letter not held - of course, can't
             trust human players to be so honest... }
    HeldTile[htile] := FREETILE;
    HeldLetter[htile] := FREELETTER;
    if besttl[nexttl] = BLANKLETTER then begin
      ch := CHR(ORD(bestt[nexttl])-32);
      tile := bestt[nexttl];
    end else begin
      ch := bestt[nexttl];
      tile := ch;
    end;
    Write(ch);
    ApparentBoardLetter[x,y] := besttl[nexttl];
    BoardTile[x,y] := tile;
    special[x, y].letterfactor := 1;
    special[x, y].wordfactor := 1;
    if letterfreq[besttl[nexttl]] = 0 then begin
      WriteLn;
      WriteLn('Too many copies of: ', besttl[nexttl]);
      WriteLn;
    end else letterfreq[besttl[nexttl]] := letterfreq[besttl[nexttl]]-1;
    if X < XMAX then x := x+1;
  end;
  WriteLn(' at ', bestx, ',', besty, ' across to make ', bestword:bestlength,
    ' (scoring ', bestscore, ')');
  end;
end {PlaceBest};

procedure TakeTiles;
var
  ch: char;
  nexttl, htile: tileindex;
begin
  if LastTile > 0 then begin
    for nexttl := 1 to LastTile do begin
      if HeldTile[nexttl] = FREETILE then begin
        { Shuffle up the remaining letters }
        htile := nexttl;
        if htile < LastTile then repeat
          htile := htile+1;
          if (HeldTile[nexttl] = FREETILE) and
             (HeldTile[htile] <> FREETILE) then begin
            HeldTile[nexttl] := HeldTile[htile];
            HeldTile[htile] := FREETILE;
            HeldLetter[nexttl] := HeldLetter[htile];
            HeldLetter[htile] := FREELETTER;
          end;
        until htile = LastTile;
      end;
    end;
    LastTile := LastTile - bestcount;
  end;
  if LastTile <> 7 then begin
  Write('May I have ', 7-LastTile, ' tile');
  if LastTile <> 6 then Write('s');
  WriteLn(' please?');
  repeat
    { Pick a tile at random from remaining set }
    if not eoln(input) then begin
      read(input, ch);
      LastTile := LastTile+1;
      if ch = ' ' then begin
        HeldLetter[LastTile] := BLANKLETTER;
        HeldTile[LastTile] := BLANKTILE;
      end else begin
        HeldLetter[LastTile] := ch;
        HeldTile[LastTile] := ch;
      end;
    end;
  until (LastTile = 7) or eoln(input);
  ReadLn(input);
  end;
end {TakeTiles};

procedure WriteBoard;
var
  ch: char;
  x: xtilepos; y: ytilepos;
  htile: tileindex;
begin
  for y := 1 to 15 do begin
    for x := 1 to 15 do begin
      if ApparentBoardLetter[x,y] = BLANKLETTER then begin
        ch := CHR(ORD(BoardTile[x,y])-32);
      end else if ApparentBoardLetter[x,y] = FREELETTER then begin
        ch := '.';
      end else ch := ApparentBoardLetter[x,y];
      Write(Board, ch);
    end;
    WriteLn(Board);
  end;
  if LastTile > 0 then for htile := 1 to LastTile do begin
    ch := HeldLetter[htile];
    if ch = BLANKLETTER then ch := ' ';
    Write(Board, ch);
  end;
  WriteLn(Board);
  WriteLn(Board, move+1);
end {WriteBoard};

begin
  cantmove := false;
  empty := true;
  LastTile := 0;
  move := 0;
  realmove := 0;
  InitDict;
  repeat
    BestScore := 0;
    for Axis := horizontal to vertical do begin
      if (move <> 0) or ((move = 0) and (Axis = horizontal)) then begin
        if realmove = 0 then begin
          Reset(board);
        end else begin
          Reset(board, 'board');
        end;
        Initialise;
        InitBoard;
        if LastTile > 0 then begin
          ConstrainPlacements(BoardTile, ApparentBoardLetter,
                              Choice, VScore, Min, Max,
                              HeldTile, LastTile);
          PlaceHorizontalWords(HeldTile, LastTile);
        end;
      end;
    end;
    Axis := horizontal;
    if realmove = 0 then begin
      Reset(board);
    end else begin
      Reset(board, 'board');
    end;
    Initialise;
    InitBoard;
    if BestScore > 0 then begin
      PlaceBest;
    end;
    TakeTiles;
    ReWrite(Board, 'board');
    WriteBoard;
    if (BestScore = 0) and (move <> 0) then begin
      { Stuck - swap some tiles }
      if LastTile > 0 then begin
        WriteLn('Couldn''t move!');
        cantmove := true;
      end;
    end;
    move := move+1;
    realmove := realmove+1;
  until cantmove;
end {NewScrab}.