// Currently being worked on.  with USE_GAMESTATE *not* defined,

// scores appear to have gone haywire...

// "USE_GAMESTATE" replaces playword with recursive_playword
// which passes all the game state in as a parameter.  We should
// switch to that one, but it doesn't yet have all the global
// analysis tweaks that the regular playword has.

// global analysis code is now being developed separately in
// subdirectory global_analysis, in file global.c - will
// eventually need to be merged with this.

/*

http://www.gtoal.com/cgi-bin/allmoves.cgi?savedgame=14511

#..-...#...-..D
.+...=..GLYCINE
..+...-.-.R.+.R
-..+.TOAsTIE..M
....NAB...V....
.=.KOP...=D.H=.
..-.PEAGE...U..
AMATE.WUSS.-R.#
..-...-E-I..D..
.F...=.R.XI.E=.
.O..+..I..L.N..
VERECUND.GITS.-
IT+...-O-LAH+..
AA...=.n.I.E.JO  <- 14
EL.-...S.T.WOOF
   ^
   D

place abore (drawprob 0.03%) at 14D across
   ([rzebqynaoiun][rzebqynaoiun][rzebqynaoiun][rzebqynaoiun]?) => abore
row 14 col 4: add 1 to hscore for a
row 14 col 5: add 3 to hscore for b
row 14 col 6: add 1*3L to hscore for o
row 14 col 7: add 1 to hscore for r
add 0 to hscore for ?
board[row 14][col 8] = n{?}

assertion failed: nexttile=4 placed=5

It would appear that the move evaluator has not
noticed the blank as 'n' on the board...

I think this is now fixed.

Next problem: http://www.gtoal.com/cgi-bin/allmoves.cgi?savedgame=14522
P("ern", "einr") = 0.0 ???

No, it's 1.0 !

This comes from the 3rd parameter, racksize, which is set to 7.  It should reduce
in the endgame when 0 tiles left in bag and player's rack shrinks.

*/

#define USE_GAMESTATE
//#define GLOBALTEST
// TO DO: when doing global analysis, need to take the remaining
// tiles that  you hold in your rackas givens.
// Currently can only do that test with *all* unplayed tiles -
// no account taken of rackleave from previous play
/*
    This is a relatively small and simple word-puzzle solver, (or
    at least it was; it seems to have grown a little more complex
    as some bugs were fixed) which takes a board layout and finds all
    possible plays (it is currently up to the user to chose the best one).
    This could be used for other games such as "Crossword Anagrams" or
    "Wordy" as it has no special knowlege of any one game.  The current
    setting of the board size, of course, makes it useful for Scrabble,
    but that's a 2-line parameter that can be easily changed.  (actually
    since I first wrote that, the code has become more Scrabble-oriented)

    I wrote this while on vacation at a Wingate hotel which has T1 ethernet
    to the room.  Very nice.  It originally took about 6 hours to get it
    working, over two evenings, although I've put a little more time into
    it since.  I wrote it specifically because although the Wordgame
    Programmers Archive (www.gtoal.com/wordgames/) has several Scrabble
    sources, there was none that was basic enough to take and modify for use
    in various utilities.  I expect this program will be developed, but I am
    specifically freezing this version as a short and simple example program.

    Although there are two procedures in here which dive into the guts
    of a DAWG data struture, the general framework of the source is
    actually completely independent of DAWGs and any other data structure
    could be used (right down to a linear search through a file of
    strings, if you feel so inclined!).  The two aforementioned procedures
    are not especially high-quality code and are specifically *not* part
    of the instructional purpose of this program.  Just pay attention to the
    interface procedures which wrap them, and ignore the internal details
    entirely - they are not critical to the general algorithm.

You'll need this data file:
---
#..-...#.J.-..#
.+...=...E...+.
..+...-.-S..+..
-..+...-.U.+..-
....+....I+....
.=...=...T.KOR.
..-...-.-I.IRE.
#..-...ALCADE.#
..-...-.-...-..
.=...=...=...=.
....+.....+....
-..+...-...+..-
..+...-.-...+..
.+...=...=...+.
#..-...#...-..#
GIMSST* 0 0

---

   To generate a dawg dictionary file, you'll need the "token" program
   from ../spell
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include <time.h>                        /* for randomising the shuffle */
int irand(int max) /* assert: max > 0 */ /* used in shuffle */
{
  long l = random();
  if (l < 0) l = -l;
  l = l % max;  /* assert: 0 <= l < max */
  return((int)l);
}
#ifndef FALSE
#define TRUE (0==0)
#define FALSE (0!=0)
#endif

int spell_verbose = TRUE;

static int superquit = FALSE;
FILE *errfile = NULL;
#include "global_analysis/latin1.c"
#include "global_analysis/bitsigs.c"  /* fast data structure based on Ars Magna anagram program */
#include "global_analysis/rackgen.c"  /* fast internal unique rack generator */

#ifdef MEMDEBUG
#include "mnemosyne.h"  /* Transparent utility to debug malloc */
#endif

/* 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"          /* load dictionary */

static int debug = FALSE;
static int megaanalysis = FALSE;  /* If doing global_analysis */
static int opt_html = TRUE;      /* Generate html or text?   */

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

#define BLANKCH '?'      /* We're making the assumption that
                            the ascii code for '?' is not going to
                            be used for a letter.  See the explanation
                            in gameconfig about charset portability */

#include "gameconfig.c"  /* Board shape, scores, alphabet etc */
static int bestscore = 0;

#define HORIZONTAL 1
#define VERTICAL 2

//------------------------------------------------------ START

/* This is the format that we use to store potential plays
   in the first hack at global analysis.  Below this is a
   work-in-progress struct which will be needed for multi-level
   lookahead, but which is not yet in use.  Need to migrate
   from this structure to that one... */

typedef struct playfm {
  char place[8];
  char pos[4];
  char dirn;
  int score;
  int freq;
  int cumfreq;
  bitsig sig;
} playfm;
                  /* I'm being lazy here... */
#define MAX_PLAYS (176915 * 7)
playfm play[MAX_PLAYS];
static int nextfree = 0;

#include "global_analysis/sortfn.c" /* auxilliary function for sorting playfm array */

#define MAX_PLAYSCORE 2000 /* surely enough - has to be > highest score you can make on one play */
static double playscore[MAX_PLAYSCORE];  /* eg playscore[87] would contain the probability (count
                                               of the # of ways) of making a play worth 87. */
//------------------------------------------------------ END

static int report = TRUE;

typedef struct GAMESTATE {            // THIS IS THE ALTERNATIVE TO THE ABOVE
  char board[HEIGHT+2][WIDTH+2];      // **UNDER DEVELOPMENT**
  char apparent_letter[HEIGHT+2][WIDTH+2];
  int tiles_held /* = 0*/;
  char tiles[/*RACKSIZE*/MAX_TILES+1]; /* Empty, for static analysis */
  char remaining_tiles[MAX_TILES+1];   /* Full, for static analysis */
  int myscore /*= 0*/;
  int oppscore /*= 0*/;
  int vertical_score[HEIGHT+2][WIDTH+2];
  char fullrack[MAX_TILES+1];
  char *crosschecks[HEIGHT+2][WIDTH+2];
  char *vertical_word[HEIGHT+2][WIDTH+2];
  int vindex[HEIGHT+2][WIDTH+2];
  char *boardfile /* = NULL*/; /* This should be in "main()" but
     for hacky reasons (HTML in playword()) it is global *for now* */
  int bestscore;
  char playtiles[/*RACKSIZE*/MAX_TILES+1]; /* Empty, for static analysis */
  char playword[/*RACKSIZE*/MAX_TILES+1]; /* Empty, for static analysis */
  char playtilesleft[/*RACKSIZE*/MAX_TILES+1]; /* Empty, for static analysis */
  char playtemplate[/*RACKSIZE*/MAX_TILES+1]; /* Empty, for static analysis */
  int playplaced;
  int playLetter;
  int playnumber;
  int playorientation;
  int movesconsidered;
} GAMESTATE;

void display_form(GAMESTATE *game) {
  {
  int row, col;
  fprintf(stdout, "<FORM ACTION='/cgi-bin/allmoves.cgi' METHOD='POST'>\n");
  fprintf(stdout, "It's your turn.  Enter your tiles and I'll work out the possible moves and likely replies.<BR><BR><BR>\n");

  fprintf(stdout, "<input name=\"dict\" type=\"hidden\" value=\"sowpods\">");

  fprintf(stdout, "Enter your rack here:<BR>\n");
  fprintf(stdout, "<select name=\"R0\" size=\"1\"><option>a<option>b<option>c<option>d<option>e<option>f<option>g<option>h<option>i<option>j<option>k<option>l<option>m<option>n<option>o<option>p<option>q<option>r<option>s<option>t<option>u<option>v<option>w<option>x<option>y<option>z<option>BLANK<option selected>No tile</SELECT>");
  fprintf(stdout, "<select name=\"R1\" size=\"1\"><option>a<option>b<option>c<option>d<option>e<option>f<option>g<option>h<option>i<option>j<option>k<option>l<option>m<option>n<option>o<option>p<option>q<option>r<option>s<option>t<option>u<option>v<option>w<option>x<option>y<option>z<option>BLANK<option selected>No tile</SELECT>");
  fprintf(stdout, "<select name=\"R2\" size=\"1\"><option>a<option>b<option>c<option>d<option>e<option>f<option>g<option>h<option>i<option>j<option>k<option>l<option>m<option>n<option>o<option>p<option>q<option>r<option>s<option>t<option>u<option>v<option>w<option>x<option>y<option>z<option>BLANK<option selected>No tile</SELECT>");
  fprintf(stdout, "<select name=\"R3\" size=\"1\"><option>a<option>b<option>c<option>d<option>e<option>f<option>g<option>h<option>i<option>j<option>k<option>l<option>m<option>n<option>o<option>p<option>q<option>r<option>s<option>t<option>u<option>v<option>w<option>x<option>y<option>z<option>BLANK<option selected>No tile</SELECT>");
  fprintf(stdout, "<select name=\"R4\" size=\"1\"><option>a<option>b<option>c<option>d<option>e<option>f<option>g<option>h<option>i<option>j<option>k<option>l<option>m<option>n<option>o<option>p<option>q<option>r<option>s<option>t<option>u<option>v<option>w<option>x<option>y<option>z<option>BLANK<option selected>No tile</SELECT>");
  fprintf(stdout, "<select name=\"R5\" size=\"1\"><option>a<option>b<option>c<option>d<option>e<option>f<option>g<option>h<option>i<option>j<option>k<option>l<option>m<option>n<option>o<option>p<option>q<option>r<option>s<option>t<option>u<option>v<option>w<option>x<option>y<option>z<option>BLANK<option selected>No tile</SELECT>");
  fprintf(stdout, "<select name=\"R6\" size=\"1\"><option>a<option>b<option>c<option>d<option>e<option>f<option>g<option>h<option>i<option>j<option>k<option>l<option>m<option>n<option>o<option>p<option>q<option>r<option>s<option>t<option>u<option>v<option>w<option>x<option>y<option>z<option>BLANK<option selected>No tile</select><br>\n");
  fprintf(stdout, "\n");

  fprintf(stdout, "Score: <INPUT NAME=score VALUE=\"\"><BR>\n");
  fprintf(stdout, "Number of tiles left: <INPUT NAME=remaining VALUE=\"\"><BR>\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "<TABLE BORDER Align='center'>\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "Enter this play in <em>lower case</em>.  Enter blanks in UPPER CASE.\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "<TR Align='center'><TD></TD>\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "<TD>A</TD><TD>B</TD><TD>C</TD><TD>D</TD><TD>E</TD><TD>F</TD><TD>G</TD><TD>H</TD><TD>I</TD><TD>J</TD><TD>K</TD><TD>L</TD><TD>M</TD><TD>N</TD><TD>O</TD></TR>\n");

  for (col = 1; col <= 15; col++) {
    int letter;
    fprintf(stdout, "<TR Align='center'><TD>%d</TD>\n", col);
    for (row = 1; row <= 15; row++) {
      letter = game->board[col][row];
      if (isupper(letter)) { // needs work for i18n version (latin1)
        letter = tolower(letter);
      } else {
        letter = toupper(letter);
      }
      fprintf(stdout, "<TD><INPUT TYPE='Text' NAME='%c%d' VALUE='%c' SIZE=1 MAXLENGTH=1></TD>\n",
                      col-1+'a', row, letter);
    }
    fprintf(stdout, "</TR>\n");
    fprintf(stdout, "\n");
  }
  fprintf(stdout, "</TABLE><BR>\n");
  fprintf(stdout, "\n");
  fprintf(stdout, "<INPUT TYPE='submit' VALUE='Find all moves!'>\n");
  fprintf(stdout, "</FORM><BR>\n");
  }
}


/* Probability function used in both global analysis and rackleave calc */
#include "global_analysis/probfn.c"

//#include "global_analysis/movegen.c"
//  IN-PROGRESS - currently uses playword.  However the
//  recursive_playword below is needed for multi-level minimax.
//  need to merge the two procedures
static char lookahead[8096];
void recursive_playword(GAMESTATE *game, char *tiles, char *word, char *tilesleft, char *template,
  int placed, int Letter, int number, int orientation)
{
  int hscore = 0, vscore = 0;
  int row, col;
  int tmp, tmpch, i, nexttile = 0;
  int wmult = 1;
  char *s = word;
  char outbuff[1024]; char *outbuffp = outbuff;
  int ga_score, confidence;
  tiles[placed] = '\0';

  if (superquit) return;

  /* 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);

  if (report) outbuffp += sprintf(outbuffp,
    (orientation == VERTICAL ? "place %s at %c%d %s (%s) => "
                             : "place %s at %d%c %s (%s) => "),
    tiles,
    (orientation == VERTICAL ? Letter : number),
    (orientation == VERTICAL ? number : Letter),
    (orientation == VERTICAL ? "down" : "across"),
    template);

  assert((word != NULL) && (game->boardfile != NULL) && (game->fullrack != NULL) && (tiles != NULL));
  if (report) {
    if (opt_html) {
      outbuffp += sprintf(outbuffp, "<A HREF=\"play?word=%s&board=%s&bagwas=%s&rackwas=%s%s&letter=%c&number=%d&orientation=%d&game->myscore=%d&game->oppscore=%d\">%s</A>",
        word, game->boardfile, game->fullrack, tiles,tilesleft, Letter, number, orientation, game->myscore, game->oppscore,
        word);
    } else {
      outbuffp += sprintf(outbuffp, "%s", word);
    }
  }
  i = col;
  for (;;) {
    if (*s == '\0') break;    
    if (game->board[row][i] == 0) {
      /* Place first letter from "tiles" here */
      hscore += ((tmp = score[tmpch = tiles[nexttile++]]) * lettermult[row][i]);

      if (debug) outbuffp += sprintf(outbuffp, "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];

      // BUG: when we call playword again just to print the best scoring move,
      // it is failing to add in the vscore component.  Don't yet know why...
      // it MAY be because the orientation has changed (best score was across,
      // we've since also done the downs, and perhaps the move evaluator is
      // looking at some global state that is not stored in the playlist.
      // perhaps I need to keep 2 copies of the cross-check info and index
      // into them by orientation. 

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

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

        game->vertical_word[row][i][game->vindex[row][i]] = *s; /* Plug in the letter to the vword */
        if (report) outbuffp += sprintf(outbuffp, "/%s(%d", game->vertical_word[row][i], tmp);
        if (wordmult[row][i] != 1) if (report) outbuffp += sprintf(outbuffp, ":%dWS", wordmult[row][i]);
        if (report) outbuffp += sprintf(outbuffp, ")");
        game->vertical_word[row][i][game->vindex[row][i]] = '?'; /* restore */
      }

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


    } else {
      /* The tile is already placed - game->apparent_letter takes care of blanks */
      /* no bonuses, AND DO NOT ADD IN VSCORE!!! */
      tmp = score[tmpch = game->apparent_letter[row][i]];
      hscore += tmp;
      if (debug) outbuffp += sprintf(outbuffp, "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) outbuffp += sprintf(outbuffp, "Multiply score by %d\n", wmult);
  hscore *= wmult;
  assert(nexttile == placed);
  if (placed == RACKSIZE) {
    hscore += BINGO;
    if (debug) outbuffp += sprintf(outbuffp, "Add 50 for a bingo!\n");
  }
  if (megaanalysis) {
    if (report) outbuffp += sprintf(outbuffp, ". SCORE %d\n", hscore+vscore);
  } else {
    if (report) {
      char do_global_analysis[1024];
      FILE *ga;
      int rc;
      outbuffp += sprintf(outbuffp, ", leaving %s. SCORE %d.  ", tilesleft, hscore+vscore);
      // --word \"%s\"
      // --board "%s\"
      // --bagwas \"%s\"
      // --rackwas \"%s%s\"
      // --letter \"%c\"
      // --number \"%d\"
      // --orientation \"%d\"
      // --myscore \"%d\"
      // --oppscore \"%d\"
      sprintf(do_global_analysis, "~/gtoal.com/wordgames/scrabble_solver/global_analysis/play"
             " --word \"%s\""
//           " --board \"/home/gtoal/gtoal.com/wordgames/boardgen/temp/wsc_%s.dat\""
             " --board \"%s\""
             " --bagwas \"%s\""               /* THIS IS PASSING THE WRONG VALUE FOR USED BLANKS (tiles) */
             " --rackwas \"%s%s\""
             " --letter \"%c\""
             " --number \"%d\""
             " --orientation \"%d\""
             " --myscore \"%d\""
             " --oppscore \"%d\"",
        word, game->boardfile, game->fullrack, 
        tiles, tilesleft, Letter, number, orientation, 
        game->myscore+hscore+vscore, game->oppscore
      );
      ga = popen(do_global_analysis, "r");
      rc = fscanf(ga, "%d", &ga_score);
      if (rc != 1) ga_score = 0;
      outbuffp += sprintf(outbuffp, "(- <= %d with %d%% confidence => %d)\n", ga_score, confidence, hscore+vscore-ga_score);
      pclose(ga);
    }
  }

  if (megaanalysis && report) {
    static int printed = 0, local_best_score = 0, moves = 0;
    moves += 1;
    if (moves == 1000000) {
        outbuffp = outbuff; // throw current output away
        outbuffp += sprintf(outbuffp, "\nOK, I\'m quitting after 1,000,000 moves so as not to be antisocial to my ISP!\n\n");
        outbuffp += sprintf(outbuffp, "\nIt might be better if you don't get into 'mega analysis' mode until after the\n");
        outbuffp += sprintf(outbuffp, "board has been closed up a bit and there aren't so many choices of move available...\n");
        outbuffp += sprintf(outbuffp, "(I.e. go back to the previous page and enter your tiles, rather than leaving them\n");
        outbuffp += sprintf(outbuffp, " all set to \'No Tile\')\n\n");
        superquit = TRUE;
        *outbuffp = '\0';
        fprintf(stdout, "%s", outbuff);
        outbuffp = outbuff;
    }
    if ((printed < 21) || (hscore+vscore > local_best_score)) {
      static int announced = 0;
      if ((announced == 0) && (printed >= 21)) {
        announced = 1;
        outbuffp += sprintf(outbuffp, "\nToo many results are being returned.  From now on we'll only list ones with better scores...\n\n");
      }
      local_best_score = hscore+vscore;
      *outbuffp = '\0';
      fprintf(stdout,
	      "%sThe probability of playing this move is P(\"%s\", \"%s\") = %1.7f\n", outbuff,
        tiles, game->fullrack, RackProb(tiles, game->fullrack, 7 /*BUG*/));
      printed += 1;
    }
    fflush(stdout);
    if ((strlen(word) +
         strlen(game->boardfile) +
         strlen(game->fullrack) +
         strlen(tiles) +
         strlen(tilesleft)) > 7000) exit(1); // hacking attempt on web interface?
    sprintf(lookahead, "~gtoal/gtoal.com/wordgames/scrabble_solver/global_analysis/play"
                       " --word \"%s\""
                       " --board \"%s\""
                       " --bagwas \"%s\""
                       " --rackwas \"%s%s\""
                       " --letter \"%c\""
                       " --number \"%d\""
                       " --orientation \"%d\""
                       " --myscore \"%d\""
                       " --oppscore \"%d\"",
        word, game->boardfile, game->fullrack, 
        tiles, tilesleft, Letter, number, orientation, 
        game->myscore, game->oppscore,
        word);

    //system(lookahead); // and we want a single numeric value returned as a result!
    //fflush(stdout);
  } else {
    *outbuffp = '\0';
    fprintf(stdout, "%s", outbuff);
  }
  outbuffp = outbuff;
  game->movesconsidered += 1;
  if (hscore+vscore > game->bestscore) {
    game->bestscore = hscore+vscore;
    strcpy(game->playtiles, tiles);
    strcpy(game->playword, word);
    strcpy(game->playtilesleft, tilesleft);
    strcpy(game->playtemplate, template);
    game->playplaced = placed;
    game->playLetter = Letter;
    game->playnumber = number;
    game->playorientation = orientation;
  }

  /* I guess the recursive call to 'minimax()' should go here??? */

}


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];
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];

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

static int orientation = 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 */

#include "crosscheck.c"

/* 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(stderr, "wildcard: %s\n", word);
  (void)crosscheck_internal(dawg, (INDEX)ROOT_NODE, word, result, set, 0, &i);
  if (i == 0) return(NULL);
  if (debug) fprintf(stderr, "          -> %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.

 */

#ifdef NEVER
void playword(char *tiles, char *word, char *tilesleft, char *template,
  int placed, int Letter, int number, int orientation)
{
  int hscore = 0, vscore = 0;
  int row, col;
  int tmp, tmpch, i, nexttile = 0;
  int wmult = 1;
  char *s = word;
  double prob;

  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 = RackProb(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

#ifdef GLOBALTEST
#else
  fprintf(stdout,
    (orientation == VERTICAL ? "place %s (drawprob %1.2f%%) at %c%d %s (%s) => "
                             : "place %s (drawprob %1.2f%%) at %d%c %s (%s) => "),
    tiles, prob,
    (orientation == VERTICAL ? Letter : number),
    (orientation == VERTICAL ? number : Letter),
    (orientation == VERTICAL ? "down" : "across"),
    template);
#endif

  assert((word != NULL) && (boardfile != NULL) && (fullrack != NULL) && (tiles != NULL));
#ifndef GLOBALTEST
  if (opt_html) {
    fprintf(stdout, "<A HREF=\"play?word=%s&board=%s&bagwas=%s+&rackwas=%s%s&letter=%c&number=%d&orientation=%d&myscore=%d&oppscore=%d&dict=%s\">%s</A>",
      word, boardfile, fullrack, tiles,tilesleft, Letter, number, orientation, myscore, oppscore,
      dict == NULL ? "" : dict, word);
  } else {
    fprintf(stdout, "%s", word);
  }
  fflush(stdout);
#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(stdout, "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(stdout, "/%s(%d", vertical_word[row][i], tmp);
        if (wordmult[row][i] != 1) fprintf(stdout, ":%dWS", wordmult[row][i]);
        fprintf(stdout, ")"); fflush(stdout);
#endif
        vertical_word[row][i][vindex[row][i]] = BLANKCH; /* restore */
      }

      if (debug) if (vertical_word[row][i] != NULL) {
        fprintf(stdout, "Intersects with %s ", vertical_word[row][i]);
        vertical_word[row][i][vindex[row][i]] = *s; /* Plug in the letter to the vword */
        fprintf(stdout, "forming %s, worth %d", vertical_word[row][i], tmp);
        if (wordmult[row][i] != 1) fprintf(stdout, " (%dWS)", wordmult[row][i]);
        vertical_word[row][i][vindex[row][i]] = BLANKCH; /* restore */
        fprintf(stdout, "\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(stdout, "add %d to hscore for %c\n", tmp, tmpch);
      if (debug) fprintf(stdout, "board[row %d][col %d] = %c{%c}\n",
         row, i, board[row][i], apparent_letter[row][i]);
    }
    s += 1;
    i += 1;
  } /* simple hscore, and vscores now all added */

  if (debug) if (wmult != 1) fprintf(stdout, "Multiply score by %d\n", wmult);
  hscore *= wmult;
  if (nexttile != placed) { //would appear not to have been changed anywhere?
    fflush(stdout);
    fprintf(stderr, "\n\nassertion failed: nexttile=%d placed=%d\n", nexttile, placed);
    exit(0);
  }
  if (placed == RACKSIZE) {
    hscore += BINGO;
    if (debug) fprintf(stdout, "Add 50 for a bingo!\n");
  }

  if (megaanalysis) {
    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) {
      fprintf(stderr, "Too many plays - increase MAX_PLAYS\n");
      exit(0);
    }

    nextfree += 1;
    fprintf(stdout, ". SCORE %d\n", hscore+vscore);
  } else {
    fprintf(stdout, ", leaving %s. SCORE %d\n", tilesleft, hscore+vscore);
  }

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

  fflush(stdout);
}
#endif
/* External interface procedure follows this internal procedure, which needs
   a lot of extra parameters that the interface isn't really interested in */

#include "fix_scrabble.c"

/* 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, 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(
#ifdef USE_GAMESTATE
  GAMESTATE *game,
#endif
  NODE *dawg, char *word, char *tiles,
  int L, int n, int orientation)
{
char result[MAX_WORD_LEN];
char placedtiles[MAX_WORD_LEN];
int i = 0;
//fprintf(stderr, "match 1\n");
  result[0] = '\0'; placedtiles[0] = '\0';
  fix_scrabble(
#ifdef USE_GAMESTATE
    game,
#endif
    dawg, (INDEX)ROOT_NODE, word, word, result, tiles,
    placedtiles, 0, 0, &i, L, n,
    orientation);
//fprintf(stderr, "match 2\n");
  return(i);
}

/*--------------------------------------------------------------*/
/*
    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.
 */

#include "readboard.c"

/*--------------------------------------------------------------*/
/*
    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.
 */

#ifdef USE_GAMESTATE
char *create_crosscheck_wildcard(GAMESTATE *game, int r, int c)
{
  char crosscheck[WIDTH+2];
  char *s;
  int rp;

  game->vertical_score[r][c] = 0;

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

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

  /* what's above? */
  rp = r-1;
  while (game->board[rp][c] != 0) rp -= 1;
  rp += 1; /* row of 1st letter in crosscheck word */
  s = crosscheck;
  while (rp != r) {
    *s = game->apparent_letter[rp][c];
    game->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 */
  game->vindex[r][c] = s-crosscheck-1;

  /* what's below? */
  rp = r+1;
  while (game->board[rp][c] != 0) {
    *s = game->apparent_letter[rp][c];
    game->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';
  game->vertical_word[r][c] = strdup(crosscheck);
  return(strdup(crosscheck));
}
#else
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++ = BLANKCH; /* 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));
}
#endif
/*--------------------------------------------------------------*/
/*
     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.
 */

void slot_internal(
  GAMESTATE *game,
#define GAME  game,
#define STATE game->
  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 valid;
  int pp = strlen(pattern);
  int opp = strlen(pattern);
  char *s;
//fprintf(stderr, "slot 1\n");
  /* 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;

  /* Covered with an earlier hook? */
  if ((placed == 0) && (STATE board[r][c-1] != 0) && (c != 1)) return;
  /* (special case for first col ... */
//fprintf(stderr, "slot 2\n");

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

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

  if (STATE crosschecks[r][c] == NULL) { /* no tiles above or below in this col */
    if (strchr(STATE 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]", STATE tiles);
    }
  } else if (*STATE crosschecks[r][c] == '!') {
    /* If no valid crosscheck at all, then reject this slot - square is blocked */
    pattern[opp] = '\0'; /* undo recursion */
    return;
  } else if ((strcmp(STATE crosschecks[r][c], "?") == 0)
              && (strchr(STATE 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]", STATE tiles);
  } else if (strcmp(STATE crosschecks[r][c], "?") == 0) {
    /* we do hold a blank, so allow anything */
    sprintf(pattern+pp, "%s", STATE crosschecks[r][c]);
  } else {
    /* This letter is constrained by specific cross-checks */
    sprintf(pattern+pp, "[%s]", STATE crosschecks[r][c]);
  }
//fprintf(stderr, "slot 4\n");
  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? */
//fprintf(stderr, "slot 5\n");
    slot_internal(GAME orig_r, orig_c,
                  r, c+1, still_to_place, placed, pattern,
                  touches_center, touches_horiz, touches_vert,
                  orientation);
//fprintf(stderr, "slot 5a\n");
    pattern[opp] = '\0'; /* undo recursion */
//fprintf(stderr, "slot 5b\n");
    return;
  }

  /* 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 (STATE board[r][p] != 0) {
    /*hscore += ...[r][p];*/
    touches_horiz = TRUE;
    pattern[pp++] = STATE apparent_letter[r][p++]; /* Careful with blanks */
  }}
  pattern[pp] = '\0';
//fprintf(stderr, "slot 6\n");

  valid = (touches_horiz ||
           (touches_vert && (placed > 1)) ||
           (touches_center && (placed > 1)));
//fprintf(stderr, "slot 7\n");

  if (valid) scrabble_match(GAME dawg, pattern, STATE tiles,
    (orientation == HORIZONTAL ? orig_c : orig_r)-1+'A',
    (orientation == HORIZONTAL ? orig_r : orig_c), orientation);
    /* the x/y param could be done more intelligently */
//fprintf(stderr, "slot 8\n");
}
#undef STATE
#undef GAME

int slot(
  GAMESTATE *game,
  int r, int c, int l, int orientation)
{
  char pattern[(WIDTH*(256+2))+1];
  pattern[0] = '\0';
  slot_internal(
#ifdef USE_GAMESTATE
    game,
#endif
    r, c, r, c, l, 0, pattern, FALSE, FALSE, FALSE,
    orientation);
  //fprintf(stderr, "slot returned\n");
  return(0); /* Needs to be true if any were placed */
}

int main(int argc, char **argv)
{
  int i;
  int row = 0, col = 0;
  int orientation = 0;
  int length = 0;
  char *dict = NULL;
  GAMESTATE *game;

  if (debug) report = TRUE;
  if (opt_html) report = TRUE;

  game = calloc(1, sizeof(struct GAMESTATE));

  if (argc != 3) {
    if (argc <= 2) {
      dict = "SOWPODS";
    } else {
      fprintf(stderr, "syntax: %s board.dat dict?\n", argv[0]);
      exit(1);
    }
  } else dict = argv[2]; /* Default word list is TWL98 */

  /* default param when debugging makes running gbd easier */
  if (argv[1] == NULL) game->boardfile = "test.txt"; else game->boardfile = argv[1];

  if (!dawg_init(dict, &dawg, &nedges)) {
    fprintf(stderr, "%s: cannot open wordlist '%s'\n", argv[0], dict);
    exit(2);
  }

  /* Clear the arrays */
  assert(WIDTH == HEIGHT);
  for (row = 0; row < (HEIGHT+2); row++) {
    for (col = 0; col < (WIDTH+2); col++) {
      game->board[row][col] = 0; game->apparent_letter[row][col] = 0;
      game->crosschecks[row][col] = NULL;
      game->vertical_word[row][col] = NULL;
      game->vindex[row][col] = 0;
    }
  }

  for (i = 0; i < 256; i++) {
    tot[i] = 2; /* Blank ;-) */
    score[i] = 0;
    totprob[i] = 0.0;
    watkins_rackvalue[i] = 25.0;
  }
  for (i = 0; i < 26; i++) {
    tot['a'+i] = itot[i];
    score['a'+i] = iscore[i];
    watkins_rackvalue['a'+i] = iwatkins_rackvalue[i];
  }

  /* hand-coded random board.  This could be replaced by a more complex
     format which includes tile distributions, values, language etc */

  strcpy(game->fullrack, FULLRACK); /* If no tiles given in file, do megamove analysis */

  read_board(game, game->boardfile);

  display_form(game);

  if (game->tiles[0] == '\0') {
    megaanalysis = TRUE;
    game->tiles_held = strlen(game->fullrack);
    strcpy(game->tiles, game->fullrack);
  }
  fprintf(stdout, "\nUnseen tiles are: %s\n\n", game->fullrack);

  for (orientation = HORIZONTAL; orientation <= VERTICAL; orientation++) {
  /* We assume throughout that we are examining only horizontal plays,
     and just flip the board to handle vertical plays.  We flip it back
     at the end, in case we want to update it with a play and write it
     back to file.  (but we don't do that yet) */

  for (row = 1; row <= HEIGHT; row++) {

    /* calculate game->crosschecks for this row before we look at
       placements for this row.  We could actually do this for
       the whole board first, but it isn't necessary */
    for (col = 1; col <= WIDTH; col++) {
      char *s;

      /* Clean up from previous tries */
      /* This is about our only use of the heap and I bet if we were
         to make these fixed size statics it would speed up the code... */
      if (game->crosschecks[row][col] != NULL) free(game->crosschecks[row][col]);
      if (game->vertical_word[row][col] != NULL) free(game->vertical_word[row][col]);
      game->vertical_word[row][col] = NULL;

      /* first of all get wildcard pattern, eg "w?rd" : */
      game->crosschecks[row][col] = create_crosscheck_wildcard(game, row, col);

      /* Then convert it to a set of valid letters */
      if (game->crosschecks[row][col] == NULL) {
        /* game->crosschecks don't make sense here */
        game->crosschecks[row][col]= strdup("?");
      } else {
        s = convert_wildcard_to_permitted_set(dawg, game->crosschecks[row][col]);
        free(game->crosschecks[row][col]);
        if (s == NULL) {
          /* No letter can currently be placed in this square legally */
          game->crosschecks[row][col] = strdup("!");
          /* "!" is easier to debug than empty string */
        } else {
          game->crosschecks[row][col] = strdup(s);
        }
      }
    }

    /* Now do placements: try every square on this row as position for
       first tile to be placed down.  Rest follow automatically */

    for (col = 1; col <= WIDTH; col++) {
      /* if (debug) fprintf(stderr, "Testing row %d col %d\n", row, col); */
      for (length = 1; length <= MIN(game->tiles_held, 7); length++) {
        /* Can I legally place this many tiles onto the board here? */
        if (slot(game, row, col, length, orientation)) {
          /* would be nice to pass the wildcard string back to here
             and then expand it/search for plays, and make the plays
             separately.  The hack above was for convenience only. */
          /* record row,col,length and wildcard for move generator (maybe) */
        }
      }
    }
  } /* Try every row on the board */

  /* flip the board around the x=y diagonal */

  for (row = 1; row <= HEIGHT; row++) {
    for (col = 1; col <= WIDTH; col++) {
      int i;
      if (row > col) { /* avoid doing twice (no-op) */
        /* swap board and game->apparent_letter at row,col */
        i = game->board[row][col];
        game->board[row][col] = game->board[col][row];
        game->board[col][row] = i;

        i = game->apparent_letter[row][col];
        game->apparent_letter[row][col] = game->apparent_letter[col][row];
        game->apparent_letter[col][row] = i;

      }
    }
  }

  } /* end of horiz/vert placement loop */

  /* If want to make best play on board and save it, do it here */
  report = TRUE;

  // OUTSTANDING BUG: when playword displays this move again, it forgets to add in the vscore component!??!
  if (game->bestscore != 0) {
    /* if orientation == VERTICAL it expects the board 
       to have already been swapped... quick hack... */

    if (game->playorientation == VERTICAL) {
      /* flip the board around the x=y diagonal */
      for (row = 1; row <= HEIGHT; row++) {
        for (col = 1; col <= WIDTH; col++) {
          int i;
          if (row > col) { /* avoid doing twice (no-op) */
            /* swap board and game->apparent_letter at row,col */
            i = game->board[row][col];
            game->board[row][col] = game->board[col][row];
            game->board[col][row] = i;

            i = game->apparent_letter[row][col];
            game->apparent_letter[row][col] = game->apparent_letter[col][row];
            game->apparent_letter[col][row] = i;

          }
        }
      }
    }

    // redo crosschecks:  THIS DUPLICATED CODE SHOULD BE MADE INTO A PROCEDURE

    for (row = 1; row <= HEIGHT; row++) {

      /* calculate game->crosschecks for this row before we look at
         placements for this row.  We could actually do this for
         the whole board first, but it isn't necessary */
      for (col = 1; col <= WIDTH; col++) {
        char *s;

        /* Clean up from previous tries */
        /* This is about our only use of the heap and I bet if we were
           to make these fixed size statics it would speed up the code... */
        if (game->crosschecks[row][col] != NULL) free(game->crosschecks[row][col]);
        if (game->vertical_word[row][col] != NULL) free(game->vertical_word[row][col]);
        game->vertical_word[row][col] = NULL;

        /* first of all get wildcard pattern, eg "w?rd" : */
        game->crosschecks[row][col] = create_crosscheck_wildcard(game, row, col);

        /* Then convert it to a set of valid letters */
        if (game->crosschecks[row][col] == NULL) {
          /* game->crosschecks don't make sense here */
          game->crosschecks[row][col]= strdup("?");
        } else {
          s = convert_wildcard_to_permitted_set(dawg, game->crosschecks[row][col]);
          free(game->crosschecks[row][col]);
          if (s == NULL) {
            /* No letter can currently be placed in this square legally */
            game->crosschecks[row][col] = strdup("!");
            /* "!" is easier to debug than empty string */
          } else {
            game->crosschecks[row][col] = strdup(s);
          }
	}
      }
    }



    fprintf(stdout, "\n");
    recursive_playword(game, game->playtiles, game->playword, game->playtilesleft,
           game->playtemplate, game->playplaced, game->playLetter,
           game->playnumber, game->playorientation);
  }
  fprintf(stdout, "%d moves were considered for this play\n", game->movesconsidered);
  exit(0);
}