/* Only externals used here are my 'dawg' library, and even that is
   only for loading the dict file.   However in a later iteration I
   may move the two dawg-based procedures here to the library */

#include "splib.h"  /* Support for loading DAWGs */

static int debug = FALSE;
static int megaanalysis = FALSE;

#define MIN(a, b) (a < b ? a : b)

#define WIDTH 15
#define HEIGHT 15
#define MAX_TILES 100
#define RACKSIZE 7    /* 7 for most, 9 for old-style German */
#define BINGO 50

#ifdef SWEDISH /* дце */
// This swedish stuff is the first gropings towards a fully I18N
// multilingual scrabble.  It's *very* messy right now but I'd rather
// do other development with this engine than the old one and have
// to re-merge the changes later...
char FULLRACK[256];
#else
#define FULLRACK "aaaaaaaaabbccddddeeeeeeeeeeeeffggghhiiiiiiiiijkllllmm\
nnnnnnooooooooppqrrrrrrssssttttttuuuuvvwwxyyz??"
#endif

/* 1..WIDTH for board, 0 and (WIDTH+1) for tombstones */
static char board[HEIGHT+2][WIDTH+2];
static char apparent_letter[HEIGHT+2][WIDTH+2];
static int lettermult[HEIGHT+2][WIDTH+2] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0},
{0,1,1,1,1,1,3,1,1,1,3,1,1,1,1,1,0},
{0,1,1,1,1,1,1,2,1,2,1,1,1,1,1,1,0},
{0,2,1,1,1,1,1,1,2,1,1,1,1,1,1,2,0},
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
{0,1,3,1,1,1,3,1,1,1,3,1,1,1,3,1,0},
{0,1,1,2,1,1,1,2,1,2,1,1,1,2,1,1,0},
{0,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0},
{0,1,1,2,1,1,1,2,1,2,1,1,1,2,1,1,0},
{0,1,3,1,1,1,3,1,1,1,3,1,1,1,3,1,0},
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
{0,2,1,1,1,1,1,1,2,1,1,1,1,1,1,2,0},
{0,1,1,1,1,1,1,2,1,2,1,1,1,1,1,1,0},
{0,1,1,1,1,1,3,1,1,1,3,1,1,1,1,1,0},
{0,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
};
static int wordmult[HEIGHT+2][WIDTH+2] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,3,1,1,1,1,1,1,3,1,1,1,1,1,1,3,0},
{0,1,2,1,1,1,1,1,1,1,1,1,1,1,2,1,0},
{0,1,1,2,1,1,1,1,1,1,1,1,1,2,1,1,0},
{0,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0},
{0,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
{0,3,1,1,1,1,1,1,2,1,1,1,1,1,1,3,0},
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0},
{0,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,0},
{0,1,1,1,2,1,1,1,1,1,1,1,2,1,1,1,0},
{0,1,1,2,1,1,1,1,1,1,1,1,1,2,1,1,0},
{0,1,2,1,1,1,1,1,1,1,1,1,1,1,2,1,0},
{0,3,1,1,1,1,1,1,3,1,1,1,1,1,1,3,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};

static int myscore = 0;
static int oppscore = 0;
static char *dict = NULL;

static int vertical_score[HEIGHT+2][WIDTH+2];
static int tiles_held = 0;
static char tiles[/*RACKSIZE*/MAX_TILES+1]; // player's rack.  in  this case the player is the opponent.
static char fullrack[MAX_TILES+1];
static char *crosschecks[HEIGHT+2][WIDTH+2];
static char *vertical_word[HEIGHT+2][WIDTH+2];
static int vindex[HEIGHT+2][WIDTH+2];
static char remaining_tiles[MAX_TILES+1];
int tiles_left_in_bag;
int opponent_tile_count;

static char *boardfile = NULL; /* This should be in "main()" but
     for hacky reasons (HTML in playword()) it is global *for now* */

#define HORIZONTAL 1
#define VERTICAL 2
static int orientation = 0;

static float totprob[256];
static int tot[256];
static int itot[27] =
/*  a b c d  e f g h i j k l m n o p  q r s t u v w x y  z */
 {  9,2,2,4,12,2,3,2,9,1,1,4,2,6,8,2, 1,6,4,6,4,2,2,1,2, 1};

static int score[256];
static int iscore[27] =
/* a  b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q   r  s  t */
{  1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1,
/* u  v  w  x  y   z */
   1, 4, 4, 8, 4, 10};

/* Temporarily, until I write some code to handle rack leaves in
   my own idiosyncratic way, I'm going to use Mark Watkins' values
   for rack-leave on a per-tile basis.  (Not actually used yet) */

static float watkins_rackvalue[256];
static float iwatkins_rackvalue[27] =
/*  a     b     c    d    e     f     g    h     i     j */
{ 1.0, -2.0, -0.5, 0.5, 2.0, -2.0, -3.0, 1.5, -1.0, -2.0,
/*  k    l    m    n     o     p     q    r    s    t */
  0.0, 0.5, 0.5, 0.5, -0.5, -1.0, -9.0, 1.0, 8.0, 0.0,
/*   u     v     w    x     y    z */
  -4.0, -6.0, -3.0, 2.5, -0.5, 5.0};

static NODE *dawg;
static INDEX nedges;

/*--------------------- crosscheck code -------------------------*/
/*
      When placing a word horizontally, if it abuts to any tiles either
    above or below, we will have generated a wildcard string consisting
    of the tiles above, a "?", and the tiles below, from which we then
    find all the valid words allowed which match that wildcard string.
      From that we find which letters were valid at that position, and
    we return the set of valid letters to the scrabble code, so that
    the search for legal plays can be trimmed for speed.
 */

/* External interface procedure follows this internal procedure, which needs
   a lot of extra parameters that the interface isn't really interested in */
static int crosscheck_internal(
  NODE *dawg, INDEX i,
  char *word, char *res, char *set,
  int len, int *found
)
{
int endsword, last, ch, target;
NODE node;
INDEX link;
static int wildch = '\0';

//{int q;for (q = 0; q < len; q++) fprintf(errfile, "  ");}
//fprintf(errfile, "dawg[%d]; '%*s' '%*s' [%s] len=%d\n", i, len, (len ? word:""), len, (len ? res:""), set, len);

  for (;;) {
    node = dawg[i++];
    ch = (int)((node >> V_LETTER) & M_LETTER);
    last = ((node & (NODE)M_END_OF_NODE) != 0);
    endsword = ((node & (NODE)M_END_OF_WORD) != 0);
    link = node & (NODE)M_NODE_POINTER;

    res[len] = ch; res[len+1] = '\0';
    target = ((int)*word)&255;
    if (ch != 0) {
      if ((ch == target) || (target == '?')) {
        if (target == '?') {
          wildch = ch;
        }
        if (endsword && (*(word+1) == '\0')) {
          int charpos;
          (*found)++;
          /* Add ch to crosscheck set if not already there */
          /* We just assume only 1 '?' per word, and last wildch was it */
          if (strchr(set, wildch) == NULL) {
            charpos = strlen(set);
            set[charpos] = wildch; set[charpos+1] = '\0';
	  }
        }
        if ((*(word+1) != '\0') && (link != 0)) (void) crosscheck_internal(dawg, link, word+1, res, set, len+1, found);
      }
    }
    if (last) break;
  }
  return(0==0);
}

/* This is a 'black box' procedure which can be replaced by anything which
   behaves the same even though implemented differently.  In fact the
   internal code of this function is a 'quick hack' and probably should
   be replaced by something neater.

   INTERFACE:  inputs: word list, wild-card string containing one "?"
               output: string of letters which the "? could represent
                       or NULL if none found. (i.e this square is blocked)

   example:  twl98, "c?t"  ->  "auo"
                            (cat, cut and cot all matching the pattern)
 */

char *convert_wildcard_to_permitted_set(NODE *dawg, char *word)
{
char result[MAX_WORD_LEN];
static char set[MAX_WORD_LEN];
int i = 0;
  *set = '\0';
  if (debug) fprintf(errfile, "wildcard: %s\n", word);
  (void)crosscheck_internal(dawg, (INDEX)ROOT_NODE, word, result, set, 0, &i);
  if (i == 0) {
  if (debug) fprintf(errfile, "          -> []\n", set);
    return(NULL);
  }
  if (debug) fprintf(errfile, "          -> [%s]\n", set);
  return(set);
}

/*--------------------- placement code -------------------------*/
/*
     This finds words which can be played at this row,column position
    of the length requested.  It does a wildcard match of a string
    such as "c[aiou]t[etrains]" which in this instance might return
    cats, cots, cute & cuts.  The search is pruned in two ways - the first,
    by which letters are allowed on certain squares, eg [aiou] may
    be a constraint from cross-check words (see above); and the
    second by the tiles in your rack (eg "etrains").  If you hold a
    blank, the wildcard might look like "c[aiou]t?" instead.

    The decision to do separate searches for different lengths of
    words was a deliberate one, to simplify the code.  Some scrabble
    programs would prefer to do searches which looked more like
    "c[aiou]t*" allowing more letters to be added at the right.
    I chose not to do this for simplicity, and also because some
    other optimisations are possible when you are using fixed-length
    searches (although those have not yet been done).
 
    This procedure is also passed the tiles which we hold, because
    the wildcard itself does not describe only valid words.  For
    instance, if we hold the tiles "act" then the wildcard might
    be "[act][act][act]" - but "tat" could not be played even
    though wildcard expansion listed it.  By removing the tiles
    played at each point for a wild letter, we end up playing
    only "act" and "cat" here.

    One known design flaw: if we have the tiles "ob?" and want
    to pay the word "bob", then the fixed "b" will always be put
    down before the wild-card as a "b".  This could be a mistake
    if the second "b" is on a triple-letter score, for example.
    [this bug was fixed when we switched from an iterative placement
     procedure to a recursive one, similar to that in upwords example]

 */

int playword(char *tiles, char *word, char *tilesleft, char *template,
	      int placed, int Letter, int number, int orientation) /* returns score */
{
  int hscore = 0, vscore = 0;
  int row, col;
  int tmp, tmpch, i, nexttile = 0;
  int wmult = 1;
  char *s = word;
  double prob;
  //return; // ************************************************************** DEBUG
  tiles[placed] = '\0';

  /* we treat the board as horizontal even if it's not */
  row = (orientation == HORIZONTAL ? number : Letter-'A'+1),
  col = (orientation == HORIZONTAL ? Letter-'A'+1 : number),

  prob = RackCount(tiles, fullrack, 7 /* BUG: TESTING. ADJUST LATER */); /* error if any wanted tile in "Tiles" does not exist in "Bag" */

  // tiles-to-play  where  how  resulting-words  score  no-of-ways-of-picking-tiles
  // nounier 3A across nounier/etape(7)/rob(6) 79 12

  fprintf(logfile,
    (orientation == VERTICAL ? "place %s (drawprob %1.10f) at %c%d %s (%s) => \n"
                             : "place %s (drawprob %1.10f) at %d%c %s (%s) => \n"),
    tiles, prob,
    (orientation == VERTICAL ? Letter : number),
    (orientation == VERTICAL ? number : Letter),
    (orientation == VERTICAL ? "down" : "across"),
    template);

  assert((word != NULL) && (boardfile != NULL) && (fullrack != NULL) && (tiles != NULL));
#ifndef GLOBALTEST
  fprintf(logfile, "<A HREF=\"play?word=%s&board=%s&bagwas=%s&rackwas=%s&letter=%c&number=%d&orientation=%d&myscore=%d&oppscore=%d&dict=%s\">%s</A>",
    word, boardfile, fullrack, tiles, Letter, number, orientation, myscore, oppscore,
    dict == NULL ? "" : dict, word);
  fflush(logfile);
#endif

  i = col;
  for (;;) {
    if (*s == '\0') break;    
    if (board[row][i] == 0) {
      /* Place first letter from "tiles" here */
      hscore += ((tmp = score[tmpch = tiles[nexttile++]]) * lettermult[row][i]);

      /*if (debug)*/ fprintf(logfile, "row %d col %d: add %d%s to hscore for %c\n",
        row, i,
        tmp,
        (lettermult[row][i] == 2 ? "*2L" : (
         lettermult[row][i] == 3 ? "*3L" : "")
        ),
        tmpch);

      wmult *= wordmult[row][i];

      if (vertical_word[row][i] != NULL) {

        tmp = (vertical_score[row][i] + (score[tmpch] * lettermult[row][i]))
              * wordmult[row][i];
        vscore += tmp;

        vertical_word[row][i][vindex[row][i]] = *s; /* Plug in the letter to the vword */
#ifndef GLOBALTEST
        fprintf(logfile, "/%s(%d", vertical_word[row][i], tmp);
        if (wordmult[row][i] != 1) fprintf(logfile, ":%dWS", wordmult[row][i]);
        fprintf(logfile, ")"); fflush(logfile);
#endif
        vertical_word[row][i][vindex[row][i]] = '?'; /* restore */
      }

      if (debug) if (vertical_word[row][i] != NULL) {
        fprintf(logfile, "Intersects with %s ", vertical_word[row][i]);
        vertical_word[row][i][vindex[row][i]] = *s; /* Plug in the letter to the vword */
        fprintf(logfile, "forming %s, worth %d", vertical_word[row][i], tmp);
        if (wordmult[row][i] != 1) fprintf(logfile, " (%dWS)", wordmult[row][i]);
        vertical_word[row][i][vindex[row][i]] = '?'; /* restore */
        fprintf(logfile, "\n");
      }


    } else {
      /* The tile is already placed - apparent_letter takes care of blanks */
      /* no bonuses */
      tmp = score[tmpch = apparent_letter[row][i]];
      hscore += tmp;
      if (debug) fprintf(logfile, "add %d to hscore for %c\n", tmp, tmpch);
    }
    s += 1;
    i += 1;
  } /* simple hscore, and vscores now all added */

  /*if (debug)*/ if (wmult != 1) fprintf(logfile, "Multiply hscore of %d by %d\n", hscore, wmult);
  hscore *= wmult;
  assert(nexttile == placed);
  if (placed == RACKSIZE) {
    hscore += BINGO;
    /*if (debug)*/ fprintf(logfile, "Add 50 for a bingo!\n");
  }
  if (vscore) fprintf(logfile, "And add vscore of %d for incidental words formed\n", vscore);
  if (megaanalysis) {
//#ifndef GLOBALTEST
    fprintf(logfile, ". SCORE %d\n", hscore+vscore);
//#else
    strcpy(play[nextfree].place, tiles);
    sprintf(play[nextfree].pos, "%c%0d", Letter, number);
    play[nextfree].dirn = orientation;
    play[nextfree].score = hscore+vscore;
    play[nextfree].freq = prob;

    makeonesig(play[nextfree].place, play[nextfree].sig);

    if (nextfree >= MAX_PLAYS) {
      static int printed = 0;
      excess_plays += 1;
      if (printed == 0) fprintf(errfile, "Too many plays - increase MAX_PLAYS\n");
      printed = 1;
      // exit(0);
// - now just going to ignore any extra plays that we don't have room for
    } else nextfree += 1;
//#endif
  } else {
    fprintf(logfile, ", leaving %s. SCORE %d\n", tilesleft, hscore+vscore);
  }

// NO LONGER DONE HERE  playscore[hscore+vscore] += prob;

  fflush(logfile);
  return hscore+vscore;
}

/* External interface procedure follows this internal procedure, which needs
   a lot of extra parameters that the interface isn't really interested in */

#define BLANKCH '?'
static void fix_scrabble(
  NODE *dawg, INDEX i,
  char *word, char *template, char *res, char *tilesleft,
  char *placedtiles, int placed,
  int len, int *found, int L, int n,
  int orientation, int *cumscore
)
{
int endsword, last, ch, target;
NODE node;
INDEX link;
//fprintf(errfile, "fix 1\n");

  for (;;) {
//fprintf(errfile, "fix 2: node = %llx,  dawg = %p, i = %d  (max %lld)\n", (NODE)node, dawg, i, (NODE)nedges);  //  165374
    node = dawg[i++];
//fprintf(errfile, "fix 2a\n");
    ch = (int)((node >> V_LETTER) & M_LETTER);
    last = ((node & (NODE)M_END_OF_NODE) != 0);
    endsword = ((node & (NODE)M_END_OF_WORD) != 0);
    link = node & M_NODE_POINTER;
//fprintf(errfile, "fix 2b\n");

    res[len] = ch; res[len+1] = '\0';
//fprintf(errfile, "fix 2c\n");
    target = *word; target &= 255;
//fprintf(errfile, "fix 3\n");
    if (ch != 0) {
      if (ch == target) { /* matches a tile on the board already */
//fprintf(errfile, "fix 4\n");
        if (endsword && *(word+1) == '\0') {
          (*cumscore) += playword(placedtiles, res, tilesleft, template,
                   placed, L, n, orientation);
          (*found)++;
        }
        if (*(word+1) != '\0' && link != 0) {
          (void) fix_scrabble(dawg, link, word+1, template, res, tilesleft,
			      placedtiles, placed, len+1, found, L, n, orientation, cumscore);
	}
//fprintf(errfile, "fix 5\n");
      } else if (target == BLANKCH) { /* We matched a wildcard.  If we have
                  the correct letter, play it; otherwise play the blank */
        if (endsword && *(word+1) == '\0') {
//fprintf(errfile, "fix 6\n");
          char *s;
          int i;
          s = strchr(tilesleft, ch);
          if (s != NULL) { /* Do we have the actual tile? */
            i = *s;
            *s = tilesleft[0]; tilesleft[0] = i;
            placedtiles[placed] = ch;
            (*cumscore) += playword(placedtiles, res, tilesleft+1, template, placed+1, L, n, orientation);
            (*found)++;
	  }
//fprintf(errfile, "fix 7\n");

          s = strchr(tilesleft, BLANKCH);
          if (s != NULL) { /* If not, do we have a blank left to play? */
            i = *s;
            *s = tilesleft[0]; tilesleft[0] = i;
//fprintf(errfile, "fix 8\n");

            res[len] = latin1_toupper(ch); /* Signal blank to display routine */
            /* This trick is not ideal.  Would be better to have a
               parallel array to 'res' which is given the equivalent
               of 'apparent_letter' instead of uppercase. (i.e. the actual
               blank character.  Note in almost every case I can think of,
               it is better to play the actual letter if you have it
               rather than the blank.  The only exception is when you're
               playing the same letter twice in a word, once as the letter
               and once as the blank.  In that case, you don't know which
               of the two positions may be a double or triple letter square.
               But if the blank is not needed in a word at all because
               you already have all the letters, why would you ever play it?
             */

            placedtiles[placed] = BLANKCH; /* blank */
            (*cumscore) += playword(placedtiles, res, tilesleft+1, template, placed+1, L, n, orientation);
            (*found)++;
          }
//fprintf(errfile, "fix 9\n");

        }
        if (*(word+1) != '\0' && link != 0) {
          char *s;
          int i;
          s = strchr(tilesleft, ch);
          if (s != NULL) { /* Do we have the actual tile? */
            i = *s;
            *s = tilesleft[0]; tilesleft[0] = i;
            placedtiles[placed] = ch;
            (void) fix_scrabble(dawg, link, word+1, template, res, tilesleft+1,
				placedtiles, placed+1, len+1, found, L, n, orientation, cumscore);
	  }
//fprintf(errfile, "fix 10\n");

          s = strchr(tilesleft, BLANKCH);
          if (s != NULL) { /* If not, do we have a blank left? */
            i = *s;
            *s = tilesleft[0]; tilesleft[0] = i;
            res[len] = latin1_toupper(ch); /* Signal blank to display routine */
            placedtiles[placed] = BLANKCH; /* Blank */
            (void) fix_scrabble(dawg, link, word+1, template, res, tilesleft+1,
				placedtiles, placed+1, len+1, found, L, n, orientation, cumscore);
          }

	}
      } else if (target == '[') { /* Is this letter in our set of valid letters? */
        char choices[(WIDTH * (256+2)) + 1];
        char *s, *saved = word;
//fprintf(errfile, "fix 11\n");
        strcpy(choices, word+1);
        s = strchr(choices, ']'); /* We assume well-formed expressions */
        *s = '\0';
        word = strchr(word, ']');
        if (strchr(choices, ch) != NULL) {
          if (endsword && *(word+1) == '\0') {
            char *s;
            int i;
            s = strchr(tilesleft, ch);
            if (s != NULL) { /* Do we have the actual tile? */
              i = *s;
              *s = tilesleft[0]; tilesleft[0] = i;
              placedtiles[placed] = ch;
              (*cumscore) += playword(placedtiles, res, tilesleft+1, template, placed+1, L, n, orientation);
              (*found)++;
	    }

            s = strchr(tilesleft, BLANKCH);
            if (s != NULL) { /* If not, do we have a blank left? */
              i = *s;
              *s = tilesleft[0]; tilesleft[0] = i;
              res[len] = latin1_toupper(ch); /* Signal blank to display routine */
              placedtiles[placed] = BLANKCH; /* Blank */
              (*cumscore) += playword(placedtiles, res, tilesleft+1, template, placed+1, L, n, orientation);
              (*found)++;
            }

          }
//fprintf(errfile, "fix 12\n");
          if (*(word+1) != '\0' && link != 0) {
            char *s;
            int i;
            s = strchr(tilesleft, ch);
            if (s != NULL) { /* Do we have the actual tile? */
              i = *s;
              *s = tilesleft[0]; tilesleft[0] = i;
              placedtiles[placed] = ch;
              (void) fix_scrabble(dawg, link, word+1, template, res, tilesleft+1,
				  placedtiles, placed+1, len+1, found, L, n, orientation, cumscore);
	    }

            s = strchr(tilesleft, BLANKCH);
            if (s != NULL) { /* If not, do we have a blank left? */
              i = *s;
              *s = tilesleft[0]; tilesleft[0] = i;
              res[len] = latin1_toupper(ch); /* Signal blank to display routine */
              placedtiles[placed] = BLANKCH; /* Blank */
              (void) fix_scrabble(dawg, link, word+1, template, res, tilesleft+1,
				  placedtiles, placed+1, len+1, found, L, n, orientation, cumscore);
            }

	  }
	}
//fprintf(errfile, "fix 13\n");
        word = saved;
      }
    }
//fprintf(errfile, "fix 14\n");
    if (last) break;
  }
//fprintf(errfile, "fix 15\n");
}

/* This is a 'black box' procedure which can be replaced by anything which
   behaves the same even though implemented differently.  In fact the
   internal code of this function (above) is a 'quick hack' and probably
   should be replaced by something neater.

   INTERFACE:  inputs: word list, wild-card, rack, row & column
               outputs: should be list of words to play.  (Currently
               the output is printed or added to the 'play' array, not returned)

   The wild card can contain only fixed letters, [abcd...] (sets of
   letters), or "?" (single-character wild-card).  There are no multi-
   character wild-cards (eg "*") - the word length is fixed.

   IMPORTANT NOTE:   "cat?" is not the same as "c[a]t?" - the
                     former expects ONE tile to be placed, the
                     latter exects TWO.  I.e. simple letters
                     represent tiles already placed on the board.

   A cleaned-up interface would not need L/n (letter/number, ie row/col
   in format such as "H7 across" or "G5 down".
   This procedure should just return (or action) the list of words, and
   doesn't need to know where they are placed if it is not doing the
   placement itself.
*/

int scrabble_match(NODE *dawg, char *word, char *tiles,
		   int L, int n, int orientation, int *count, int *score)
{
char result[MAX_WORD_LEN];
char placedtiles[MAX_WORD_LEN];

  result[0] = '\0'; placedtiles[0] = '\0';

  //fprintf(errfile, "Are there any words which match %s from my rack of %s?\n", word, tiles);

  fix_scrabble(dawg, (INDEX)ROOT_NODE, word, word, result, tiles,
    placedtiles, 0, 0, count, L, n,
	       orientation, score);

  return(*count);
}

/*--------------------------------------------------------------*/
/*
    This reads a board in the simple file format which Kevin Cowtan
   uses in his board-graphic image generator software.  I just happened
   to have some cgi web page software which generates this format so I
   plan to reuse it to create a graphical front-end to solving scrabble
   problems.
 */

void read_board_for_opps_move(char *fname)
{
  int row, col;
  int c, rackletter;
  int my_unseen_tiles;
  FILE *sample;
  char *s;

  opponent_tile_count = 0;
  sample = fopen(fname, "r");
  if (sample == NULL) {
    fprintf(errfile, "Cannot open board file '%s'\n", fname);
    exit(0);
  }
  for (row = 1; row <= HEIGHT; row++) {
    if (debug) fprintf(errfile, "Row %02d: ", row);
    for (col = 1; col <= WIDTH; col++) {
      c = fgetc(sample)&255;
      if (latin1_isalpha(c)) { /* Take care with locale for other language versions */
        board[row][col] = latin1_tolower(c);
        if (debug) fprintf(errfile, "%c", latin1_tolower(c));
        if (latin1_islower(c)) {
          rackletter = '?' /* BLANK */;
	} else {
          rackletter = latin1_tolower(c);
	}
        apparent_letter[row][col] = rackletter;
        s = strchr(fullrack, rackletter);
        if (s == NULL) {
          fprintf(errfile, "Error: we appear to have too many %c's on the board\n", rackletter);
          fprintf(errfile, "       %s\n", FULLRACK);
          fprintf(errfile, "       %s\n", fullrack);
          exit(0);
	}
        memmove(s, s+1, strlen(s));
      } else if (debug) fprintf(errfile, " ");
    }
    c = fgetc(sample); /* newline */
    if (c != '\n') {
      fprintf(errfile, "Data format in .dat file\n");
      exit(0);
    }
    if (debug) fprintf(errfile, "\n");
  }
  tiles_held = 0; my_unseen_tiles = 0;
  // this is 'my' rack we're reading below, but really this program is
  // currently only being invoked for the opponent at a single level of ply
  // so we don't care about our tiles except for 
  // 1) removing them from the available pool
  // and 2) calculating the value of the rackleave, which we'll do *after*
  // generating all the opponent responses
  for (col = 0; col < RACKSIZE; col++) {
    c = fgetc(sample);
    if (c == '?') { my_unseen_tiles += 1; continue;} // unknown tile
    if (c == '*') c = '?';  // blank
    if (latin1_isalpha(c)) c = latin1_tolower(c);
    rackletter = tiles[tiles_held++] = c;
    s = strchr(fullrack, rackletter);
    if (s == NULL) {
      fprintf(errfile, "Error: we appear to have too many %c's [%d] in the rack\n", rackletter, rackletter);
      fprintf(errfile, "       %s\n", FULLRACK);
      fprintf(errfile, "       %s\n", fullrack);
      exit(0);
    }
    memmove(s, s+1, strlen(s)); // remove the tile from the rack
  }
  tiles[tiles_held] = '\0';
  // so at this point 'tiles[]' is really 'my' tiles, but we want it to be opponent's tiles (unknown)


  fscanf(sample, " %d %d\n", &myscore, &tiles_left_in_bag);
  if (debug) {
    fprintf(errfile, "%0d Tiles: %s\n", tiles_held, tiles);
  }
  if ((tiles_left_in_bag != 0) && (tiles_left_in_bag != strlen(fullrack))) {
    fprintf(errfile, "Data file says %d tiles left.  I'm showing %d left\n%s\n",
      tiles_left_in_bag, strlen(fullrack), fullrack);
    exit(0);
  } else tiles_left_in_bag = strlen(fullrack)-strlen(tiles);

  // tiles left in bag now includes only the ones actually in the bag, plus the
  // ones the opponent is holding.

  opponent_tile_count = MIN(7, tiles_left_in_bag);
  tiles_left_in_bag -= opponent_tile_count;

  if (tiles_left_in_bag == 0) {
    fprintf(logfile, "Endgame: my tiles are %s, opponents tiles are %s - bag is now empty\n", tiles, fullrack);
  } else {
    fprintf(logfile, "Really only %d tiles left in the bag, but we don't know which ones they are out of %d: %s\n",
                    tiles_left_in_bag, strlen(fullrack), fullrack);
    if (my_unseen_tiles > 0) {
      fprintf(logfile, "By the time the opponent plays, I will have drawn %d tiles from the bag.\n", my_unseen_tiles);
    }
    fprintf(logfile, "The opponent, whose responses we are calculating to our suggested move\n");
    fprintf(logfile, "is holding %d tiles chosen from the list above.\n", opponent_tile_count);
  }

  /* We don't really need this now since we're calculating it on the fly */
  remaining_tiles[0] = '\0';
  fgets(remaining_tiles, MAX_TILES+1, sample); /* Ignore error code */
  remaining_tiles[MAX_TILES] = '\0';
  fclose(sample);
}

/*--------------------------------------------------------------*/
/*
    This looks at the place where a tile is likely to be played, and
    whether there are tiles abutting above or below it, which would
    permit or deny a word to be placed there.  This generates a
    wild-card string which is then expanded in the code above to
    generate the set of letters which are valid here.  It is that
    set which is stored, not the wild card string or the words themselves.
 */

char *create_crosscheck_wildcard(int r, int c)
{
  char crosscheck[WIDTH+2];
  char *s;
  int rp;

  vertical_score[r][c] = 0;

  /* Already a tile here so crosscheck is meaningless */
  if (board[r][c] != 0) return(NULL);

  /* none above and none below? */
  if ((board[r-1][c] == 0) && (board[r+1][c] == 0)) return(NULL);

  /* what's above? */
  rp = r-1;
  while (board[rp][c] != 0) rp -= 1;
  rp += 1; /* row of 1st letter in crosscheck word */
  s = crosscheck;
  while (rp != r) {
    *s = apparent_letter[rp][c];
    vertical_score[r][c] += score[*s]; /* 0 if blank */
    if (*s == '?') *s = board[rp][c]; // FIX FOR BUG IN "wildcard:"
    s += 1; rp += 1;
  }
  *s++ = '?'; /* r */
  vindex[r][c] = s-crosscheck-1;

  /* what's below? */
  rp = r+1;
  while (board[rp][c] != 0) {
    *s = apparent_letter[rp][c];
    vertical_score[r][c] += score[*s]; /* 0 if blank */
    if (*s == '?') *s = board[rp][c]; // FIX FOR BUG IN "wildcard:"
    s += 1; rp += 1;
  }
  *s = '\0';
  vertical_word[r][c] = strdup(crosscheck);
  return(strdup(crosscheck));
}

/*--------------------------------------------------------------*/
/*
     Can we slot a word into the board at this row,col and length(*)?
   If so, then the very last thing we do before returning is to actually
   generate the words.  This is not really good design - we should
   pass the wildcard which this generates up a level and have that
   level do the search.  This procedure generates a wild-card string
   using fixed letters, "[...]" letter sets, and wildcards ("?")
   which when expanded returns valid words which can be played here.

   *: Note 'length' here doesn't mean word length.  It means number of
      tiles to be placed.  The word may be longer because of tiles
      already on the board that we are placing around.
 */

int slot_internal(
  int orig_r, int orig_c,
  int r, int c,
  int still_to_place, int placed,
  char *pattern,
  int touches_center, int touches_horiz, int touches_vert,
  int orientation,
  int *count,
  int *score)
{
  int valid;
  int pp = strlen(pattern);
  int opp = strlen(pattern);
  char *s;

  /* No more loop.  Just place one, and recurse.  If I had done this
     with the scrabble program to begin with, I wouldn't have had the
     stupid bug to do with playing blanks as the same letter as another
     letter you hold in the rack... */

  /* Ran off end of board with tiles still to place? */
  if ((c > WIDTH) && (still_to_place > 0)) return FALSE;

  /* Covered with an earlier hook? */
  if ((placed == 0) && (board[r][c-1] != 0) && (c != 1)) return FALSE;
  /* (special case for first col ... */

  while (board[r][c] != 0) {
    /* There is already a tile on the board here.  Careful with blanks */
    pattern[pp++] = board[r][c++]; pattern[pp] = '\0';
  }
  if ((c) > WIDTH) return FALSE; /* Oops, we went over the edge */

  /* If center square was empty this must be the first move */
  if ((board[WIDTH/2+1][HEIGHT/2+1] == 0) 
       && (((WIDTH/2)+1 == r) && (c == (WIDTH/2)+1))
     ) {
    touches_center = (touches_center || TRUE);
  }
  if (board[r][c-1] != 0 || board[r][c+1] != 0) touches_horiz = (touches_horiz || TRUE);
  if (board[r-1][c] != 0 || board[r+1][c] != 0) touches_vert = (touches_vert || TRUE);

  if (crosschecks[r][c] == NULL) { /* no tiles above or below in this col */
    if (strchr(tiles, '?') != NULL) {
      /* We hold a blank so it could be anything */
      sprintf(pattern+pp, "?");
    } else {
      /* restrict search space when possible, ie only those letters
         for which we hold tiles */
      sprintf(pattern+pp, "[%s]", tiles);
    }
  } else if (*crosschecks[r][c] == '!') {
    /* If no valid crosscheck at all, then reject this slot - square is blocked */
    pattern[opp] = '\0'; /* undo recursion */
    return FALSE;
  } else if ((strcmp(crosschecks[r][c], "?") == 0)
              && (strchr(tiles, '?') == NULL)) {
    /* we don't hold a blank, so do a reduction-in-strength of a
       wild-card "?" to [abcdefg] (i.e. only the tiles we hold) */
    sprintf(pattern+pp, "[%s]", tiles);
  } else if (strcmp(crosschecks[r][c], "?") == 0) {
    /* we do hold a blank, so allow anything */
    sprintf(pattern+pp, "%s", crosschecks[r][c]);
  } else {
    /* This letter is constrained by specific cross-checks */
    sprintf(pattern+pp, "[%s]", crosschecks[r][c]);
  }
  pp = strlen(pattern); pattern[pp] = '\0';
  placed += 1; still_to_place -= 1;

  if (still_to_place != 0) {
    /* Should this test be moved up before some of the code above? */
    int rc = slot_internal(orig_r, orig_c,
                  r, c+1, still_to_place, placed, pattern,
                  touches_center, touches_horiz, touches_vert,
			   orientation, count, score);
    pattern[opp] = '\0'; /* undo recursion */
    return rc;
  }

  /* after placing all tiles, are there still some letters abutting
      to the right?  If so, add them to the string to be matched */
  {int p = c+1; while (board[r][p] != 0) {
    /*hscore += ...[r][p];*/
    touches_horiz = TRUE;
    pattern[pp++] = board[r][p++]; /* Careful with blanks */
  }}
  pattern[pp] = '\0';

  valid = (touches_horiz ||
           (touches_vert && (placed > 1)) ||
           (touches_center && (placed > 1)));

  if (valid) scrabble_match(dawg, pattern, tiles,
    (orientation == HORIZONTAL ? orig_c : orig_r)-1+'A',
			    (orientation == HORIZONTAL ? orig_r : orig_c), orientation, count, score);
    /* the x/y param could be done more intelligently */
}

int slot(int r, int c, int l, int orientation, int *count, int *score)
{
  char pattern[(WIDTH*(256+2))+1];
  pattern[0] = '\0';
  //fprintf(errfile, "slot %d %d %d %d\n", r, c, l, orientation);
  return slot_internal(r, c, r, c, l, 0, pattern, FALSE, FALSE, FALSE,
		       orientation, count, score);
}