Subversion Repositories wimsdev

Rev

Rev 3398 | Rev 3840 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*2010-08-20
  2.  
  3. Modify the output to wims
  4. Rename it in crossword.c
  5. Bernadette Perrin-Riou
  6. bpr@math.u-psud.fr
  7.  
  8. */
  9.  
  10. /*  POTM ENTRY: jigsaw                                          */
  11. /*  Your Name:  Franz Korntner                                  */
  12. /*  Your email: fkorntne@hiscom.nl                              */
  13. /*              korntner@xs4all.nl                              */
  14. /*  To:         enter@potm.ffast.att.com                        */
  15. /*  Subject:    C R O Z Z L E                                   */
  16.  
  17. /*
  18. **  Hello Fred. This is my entry with some additional comments and
  19. **  some modifications to make it more portable. I haven't changed
  20. **  the algorithm in any way whatsoever.
  21. **  Because the source will be made available to the public, I've
  22. **  included a copyright message to state that the source is free
  23. **  and not public-domain. I'm sure thats alright with you.
  24. */
  25.  
  26. /*
  27.    jigsaw, to create crossword puzzle grids
  28.    Copyright 1996 Franz Korntner <fkorntne@hiscom.nl> <korntner@xs4all.nl>
  29.  
  30. This program is free software; you can redistribute it and/or modify
  31. it under the terms of the GNU General Public License as published by
  32. the Free Software Foundation; either version 2 of the License, or
  33. (at your option) any later version.
  34.  
  35. This program is distributed in the hope that it will be useful,
  36. but WITHOUT ANY WARRANTY; without even the implied warranty of
  37. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  38. GNU General Public License for more details.
  39.  
  40. You should have received a copy of the GNU General Public License
  41. along with this program; if not, write to the Free Software
  42. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  43. */
  44.  
  45. /*
  46. **  *** For your pleasure only ***
  47. **
  48. **  The code can produce symmetrical grids by setting the macro
  49. **  SYMMETRICAL to 1. Sadly enough not as many words will fit
  50. **  into a symmetrical grid than into a non-symmetrical grid,
  51. **  so NODEMAX must be increased (say with 1000) to get better results.
  52. */
  53.  
  54. /* Configuring parameters */
  55.  
  56. #define TIMEMAX         (10*60-15)      /* 10 minute limit              */
  57. #define DEBUG           0               /* 0=Off 1=On 2=Verbose         */
  58. #define NODEMAX         1500            /* 500=Fast 1500=Normal         */
  59. #define SYMMETRICAL     0               /* 0=No 1=Yes                   */
  60. #define MALLOC_BROKEN   1               /* 0=No 1=Yes (See below)       */
  61. #define WIMS 1
  62. /*
  63. ** It seems that malloc() under Linux is broken. I could only allocate
  64. ** a couple of thousand nodes before my program crashed. A solution was to
  65. ** allocate a BIG chunk of memory and split it up manually. This way about
  66. ** 10500 nodes could be allocated.
  67. */
  68.  
  69. /* Start of program */
  70.  
  71. #include <stdio.h>
  72. #include <stdlib.h>
  73. #include <string.h>
  74. #include <ctype.h>
  75. #include <unistd.h>
  76. #include <sys/times.h>
  77.  
  78. #if defined(__alpha__)
  79. #ifndef __GNUC__
  80. #error I want GCC !!!
  81. #endif
  82. typedef          int int8   __attribute__ ((mode (QI)));
  83. typedef unsigned int uint8  __attribute__ ((mode (QI)));
  84. typedef          int int16  __attribute__ ((mode (HI)));
  85. typedef unsigned int uint16 __attribute__ ((mode (HI)));
  86. typedef          int int32  __attribute__ ((mode (SI)));
  87. typedef unsigned int uint32 __attribute__ ((mode (SI)));
  88. #else
  89. /* It seems that your Sparc-GCC doesn't support the above :-(( */
  90. typedef          char  int8;
  91. typedef unsigned char  uint8;
  92. typedef          short int16;
  93. typedef unsigned short uint16;
  94. typedef          long  int32;
  95. typedef unsigned long  uint32;
  96. #endif
  97.  
  98. #define GRIDMAX 22                      /* Size of grid incl. border    */
  99. #define LINKMAX 4096                    /* # links                      */
  100. #define WORDMAX 256                     /* # words                      */
  101. #define WORDLENMAX 32                   /* # length of word             */
  102. #define ADJMAX 128                      /* # unaccounted adjacent chars */
  103. #define SCOREMAX 1000                   /* Spead for hashing            */
  104.  
  105. #define BASE ('a'-1)                    /* Word conversion              */
  106. #define STAR   (28)                     /* Word delimiters              */
  107. #define FREE   (31)                     /* Unoccupied grid cell         */
  108. #define TODOH 1                         /* Hint: Hor. word can be here  */
  109. #define TODOV 2                         /* Hint: Ver. word can be here  */
  110. #define BORDER 4                        /* Cell is grid border          */
  111.  
  112. #define ISSTAR(C)   ((C)==STAR)         /* Test the above               */
  113. #define ISFREE(C)   ((C)==FREE)
  114. #define ISCHAR(C)   ((C)< STAR)
  115. #define ISBORDER(C) ((C)&BORDER)
  116.  
  117. #define BITSET(M,B) ((M).s[(B)>>3] |=  (1<<((B)&7))) /* BitSet          */
  118. #define BITCLR(M,B) ((M).s[(B)>>3] &= ~(1<<((B)&7))) /*   manipulation  */
  119. #define INSET(M,B)  ((M).s[(B)>>3] &   (1<<((B)&7)))
  120.  
  121. typedef struct {                        /* Bitset containing 256 bits   */
  122.   uint8 s[32];
  123. } SET;
  124.  
  125. typedef struct node {
  126.   struct node   *next;                  /*                              */
  127.   int           seqnr;                  /* For diagnostics              */
  128.   SET           words;                  /* Summary of placed words      */
  129.   int           numword,numchar,numconn;/* Statistics                   */
  130.   uint32        hash;                   /* Duplicate detection          */
  131.   int           firstlevel, lastlevel;  /* Grids hotspot                */
  132.   float         score;                  /* Will it survive?             */
  133.   int8          symdir;                 /* Force symmetry               */
  134.   int16         symxy;                  /*                              */
  135.   int16         symlen;                 /*                              */
  136.   int           numadj;                 /* # unprocessed char pairs     */
  137.   int8          adjdir[ADJMAX];         /* Pair's direction             */
  138.   int16         adjxy[ADJMAX];          /* Pair's location              */
  139.   int16         adjl[ADJMAX];           /* Pair's wordlist              */
  140.   uint8         grid[GRIDMAX*GRIDMAX];  /* *THE* grid                   */
  141.   uint8         attr[GRIDMAX*GRIDMAX];  /* Grid hints                   */
  142. } node;
  143.  
  144. struct link {
  145.   int16         next;
  146.   int16         w;                              /* What's the word      */
  147.   int16         ofs;                            /* Were's in the word   */
  148. };
  149.  
  150.  
  151. /* The external word list */
  152. uint8           wordbase[WORDMAX][WORDLENMAX];  /* Converted wordlist   */
  153. int             wlen[WORDMAX];                  /* Length of words      */
  154. int             numword;                        /* How many             */
  155. char            *wordfname = "wordlist";        /* Where are they found */
  156.  
  157. /* Where are 1,2,3 long character combinations */
  158. int16           links1[32];                     /* 1-char wordlist      */
  159. int16           links2[32][32];                 /* 2-char wordlist      */
  160. int16           links3[32][32][32];             /* 3-char wordlist      */
  161. struct link     linkdat[LINKMAX];               /* Body above wordlist  */
  162. int             numlinkdat;                     /* How many             */
  163.  
  164. /* Hotspot pre-calculations */
  165. int16           xy2level[GRIDMAX*GRIDMAX];      /* distance 0,0 to x,y  */
  166. int16           level2xy[GRIDMAX*2];            /* inverse              */
  167.  
  168. /* Node administration */
  169. node            *freenode;                      /* Don't malloc() too much */
  170. int             numnode, realnumnode;           /* Statistics           */
  171. node            solution;                       /* What are we doing?   */
  172. node            *scores[SCOREMAX];              /* Speed up hashing     */
  173.  
  174. /* Diagnostics */
  175. int             seqnr;
  176. int             hashtst, hashhit;
  177. int             nummalloc;
  178. int             numscan;
  179. int             flg_dump;
  180.  
  181. /* What's left */
  182. int             clktck;                         /* Timing portability   */
  183.  
  184. /* Forward declartions */
  185. char *elapsedstr(void);
  186.  
  187. /*
  188. ** Display the grid. Show disgnostics info in verbose mode
  189. */
  190.  
  191. void dump_grid (node *d)
  192. {
  193. int x, y;
  194.  
  195. #if DEBUG==2
  196.  int attr, cnt;
  197.   /* Double check the number of words */
  198.   cnt = 0;
  199.   for (x=GRIDMAX*GRIDMAX-1; x>=0; x--)
  200.     if (ISCHAR(d->grid[x])) {
  201.       if (!ISCHAR(d->grid[x-1]) && ISCHAR(d->grid[x+1])) cnt++;
  202.       if (!ISCHAR(d->grid[x-GRIDMAX]) && ISCHAR(d->grid[x+GRIDMAX])) cnt++;
  203.     }
  204.   printf("%s seqnr:%d level:%d/%d score:%f numword:%d/%d numchar:%d numconn:%d\n",
  205.          elapsedstr(), d->seqnr, d->firstlevel, d->lastlevel, d->score, d->numword,
  206.          cnt, d->numchar, d->numconn);
  207. #endif
  208.  
  209. #if DEBUG==2
  210.   /* Show grid as I would like to see it */
  211.   for (y=0; y<GRIDMAX; y++) {
  212.     for (x=0; x<GRIDMAX; x++) {
  213.       attr = d->attr[x+y*GRIDMAX]&(TODOH|TODOV);
  214.       if (attr==(TODOH|TODOV))
  215.         printf("B");
  216.       else if (attr==TODOH)
  217.         printf("H");
  218.       else if (attr==TODOV)
  219.         printf("V");
  220.       else
  221.         printf(".");
  222.     }
  223.     printf (" ");
  224.     for (x=0; x<GRIDMAX; x++)
  225.       if (ISSTAR(d->grid[x+y*GRIDMAX]))
  226.         printf("*");
  227.       else if (ISFREE(d->grid[x+y*GRIDMAX]))
  228.         printf(".");
  229.       else if (ISCHAR(d->grid[x+y*GRIDMAX]))
  230.         printf ("%c", d->grid[x+y*GRIDMAX]+BASE);
  231.       else
  232.         printf("0x%02x", d->grid[x+y*GRIDMAX]);
  233. #else
  234. # if WIMS==1
  235.   int sizex, sizey ; sizex=0 ;  sizey=0;
  236.       for (y=1; y<GRIDMAX; y++) {
  237.        for (x=1; x<GRIDMAX; x++) {
  238.          if (ISCHAR(d->grid[x+y*GRIDMAX])) { if (x > sizex) sizex = x ; }
  239.        }
  240.       }
  241.       for (x=1; x<GRIDMAX-1; x++) {
  242.        for (y=1; y<GRIDMAX-1; y++) {
  243.          if (ISCHAR(d->grid[x+y*GRIDMAX])) { if (y > sizey) sizey = y ; }
  244.        }
  245.       }
  246.       for (y=1; y<sizey+1; y++) {
  247.        for (x=1; x<sizex+1; x++) {
  248.         if (!ISCHAR(d->grid[x+y*GRIDMAX]) && x <sizex ){ printf(",");}
  249.         else {
  250.         if (ISCHAR(d->grid[x+y*GRIDMAX])){
  251.          printf ("%c", d->grid[x+y*GRIDMAX]+BASE);
  252.          if (x <sizex) printf(",") ;
  253.         }
  254.        }
  255.       }
  256.      printf("\n");
  257.    }
  258. #else
  259.    /* Show grid as Fred would like to see it */
  260.   for (y=1; y<GRIDMAX-1; y++) {
  261.     for (x=1; x<GRIDMAX-1; x++)
  262.       if (ISCHAR(d->grid[x+y*GRIDMAX]))
  263.         printf ("%c", d->grid[x+y*GRIDMAX]+BASE);
  264.       else
  265.         printf("-");
  266. #endif
  267. #endif
  268.  
  269.  
  270.   /* For diagnostics */
  271.   fflush (stdout);
  272. }
  273.  
  274. int elapsedtime (void)
  275. {
  276. int seconds;
  277. static struct tms TMS;
  278.  
  279.   times(&TMS);
  280.  
  281.   /* add the elapsed system and user times                        */
  282.   /* calculation comes out to the nearest second - rounded down   */
  283.   seconds = (TMS.tms_stime + TMS.tms_utime)/clktck;
  284.   if (seconds < 1) seconds=1;
  285.   if (seconds >= TIMEMAX) {
  286.     /* PRINT OUT YOUR SOLUTION BEFORE YOU GO! */
  287.     /*
  288.     ** HELP, the algorithm must compleet well under 10 minutes.
  289.     */
  290.     dump_grid (&solution);
  291.     exit(0);
  292.   }
  293.   return seconds;
  294. }
  295.  
  296. /*
  297. ** Elapsed time for diagnostics
  298. */
  299. char *elapsedstr(void)
  300. {
  301. static char line[40];
  302. int seconds;
  303.  
  304.   seconds = elapsedtime();
  305.   sprintf(line, "%02d:%02d", seconds/60, seconds%60);
  306.   return line;
  307. }
  308.  
  309. /*
  310. ** Get a node, reusing free'ed nodes.
  311. */
  312.  
  313. node *mallocnode (void)
  314. {
  315. node *d;
  316.  
  317.   d = freenode;
  318.   if (d == NULL) {
  319.     d = (node*) malloc (sizeof(node));
  320.     nummalloc++;
  321.   } else
  322.     freenode = d->next;
  323.  
  324.   if (d == NULL) {
  325.     fprintf (stderr, "Out of memory after %d nodes (%lo bytes)\n", nummalloc, nummalloc*sizeof(node));
  326.     dump_grid (&solution);
  327.     exit(0);
  328.   }
  329.  
  330.   return d;
  331. }
  332.  
  333. void add_node (node *d)
  334. {
  335. int i;
  336. node **prev, *next;
  337.  
  338.   /* Evaluate grids score */
  339.   d->score = (float)d->numconn/d->numchar;
  340.  
  341.   /* Insert grid into sorted list, eliminating duplicates */
  342.   i = d->score*(SCOREMAX-1);
  343.   if (i<0) i=0;
  344.   if (i>=SCOREMAX) i=SCOREMAX-1;
  345.   prev = &scores[i];
  346.   next = scores[i];
  347.   for (;;) {
  348.     if (next == NULL) break;
  349.     if (d->score > next->score) break;
  350.     if (d->score == next->score && d->hash >= next->hash) break;
  351.     prev = &next->next;
  352.     next = next->next;
  353.   }
  354.  
  355.   /* Test if entry is duplicate */
  356.   while (next && d->score == next->score && d->hash == next->hash) {
  357.     hashtst++;
  358.     if (memcmp(d->grid, next->grid, sizeof(d->grid)) == 0) {
  359.       d->next = freenode;
  360.       freenode = d;
  361.       return;
  362.     }
  363.     hashhit++;
  364.     prev = &next->next;
  365.     next = next->next;
  366.   }
  367.  
  368.   /* Diagnostics */
  369.   if (flg_dump > 1)
  370.     dump_grid (d);
  371.  
  372.   /* Enter grid into list */
  373.   d->seqnr = seqnr++;
  374.   d->next = next;
  375.   (*prev) = d;
  376.   if (d->numadj == 0)
  377.     numnode++;
  378.   realnumnode++;
  379. }
  380.  
  381. /*
  382. ** The next two routines test if a given word can be placed in the grid.
  383. ** If a new character will be adjecent to an existing character, check
  384. ** if the newly formed character pair exist in the wordlist (it doesn't
  385. ** matter where).
  386. */
  387.  
  388. int test_hword (node *d, int xybase, int word)
  389. {
  390. uint8 *p, *grid;
  391. int l;
  392.  
  393.   /* Some basic tests */
  394.   if (xybase < 0 || xybase+wlen[word] >= GRIDMAX*GRIDMAX+1) return 0;
  395.  
  396. #if SYMMETRICAL
  397.   /* How about star's */
  398.   if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-xybase]))
  399.     return 0;
  400.   if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xybase+wlen[word]-1)]))
  401.     return 0;
  402. #endif
  403.  
  404.   /* Will new characters create conflicts */
  405.   for (grid=d->grid+xybase,p=wordbase[word]; *p; grid++,p++) {
  406.     if (*grid == *p)
  407.       continue; /* Char already there */
  408.     if (!ISFREE(*grid))
  409.       return 0; /* Char placement conflict */
  410.     if (ISSTAR(*p))
  411.       continue; /* Skip stars */
  412.     if (!ISCHAR(grid[-GRIDMAX]) && !ISCHAR(grid[+GRIDMAX]))
  413.       continue; /* No adjecent chars */
  414.  
  415.     if (ISFREE(grid[-GRIDMAX]))
  416.       l = links2[*p][grid[+GRIDMAX]];
  417.     else if (ISFREE(grid[+GRIDMAX]))
  418.       l = links2[grid[-GRIDMAX]][*p];
  419.     else
  420.       l = links3[grid[-GRIDMAX]][*p][grid[+GRIDMAX]];
  421.     if (l == 0)
  422.       return 0;
  423.   }
  424.  
  425.   /* Word can be placed */
  426.   return 1;
  427. }
  428.  
  429. int test_vword (node *d, int xybase, int word)
  430. {
  431. uint8 *p, *grid;
  432. int l;
  433.  
  434.   /* Some basic tests */
  435.   if (xybase < 0 || xybase+wlen[word]*GRIDMAX >= GRIDMAX*GRIDMAX+GRIDMAX) return 0;
  436.  
  437. #if SYMMETRICAL
  438.   /* How about star's */
  439.   if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-xybase]))
  440.     return 0;
  441.   if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xybase+wlen[word]*GRIDMAX-GRIDMAX)]))
  442.     return 0;
  443. #endif
  444.  
  445.   /* Will new characters create conflicts */
  446.   for (grid=d->grid+xybase,p=wordbase[word]; *p; grid+=GRIDMAX,p++) {
  447.     if (*grid == *p)
  448.       continue; /* Char already there */
  449.     if (!ISFREE(*grid))
  450.       return 0; /* Char placement conflict */
  451.     if (ISSTAR(*p))
  452.       continue; /* Skip stars */
  453.     if (!ISCHAR(grid[-1]) && !ISCHAR(grid[+1]))
  454.       continue; /* No adjacent characters */
  455.  
  456.     if (ISFREE(grid[-1]))
  457.       l = links2[*p][grid[+1]];
  458.     else if (ISFREE(grid[+1]))
  459.       l = links2[grid[-1]][*p];
  460.     else
  461.       l = links3[grid[-1]][*p][grid[+1]];
  462.     if (l == 0)
  463.       return 0;
  464.   }
  465.  
  466.   /* Word can be placed */
  467.   return 1;
  468. }
  469.  
  470. /*
  471. ** The next two routines will place a given word in the grid. These routines
  472. ** also performs several sanity checks to make sure the new grid is worth
  473. ** it to continue with. If a newly placed character is adjecent to an
  474. ** existing character, then that pair must be part of a word that can
  475. ** be physically placed. If multiple character pairs exist, then no check
  476. ** is done to determine if those words (of which the pairs are part) can
  477. ** be adjecent. That is done later as these grids are not counted against
  478. ** NODEMAX.
  479. */
  480.  
  481. int place_hword (node *data, int xybase, int word)
  482. {
  483. node *d=data;
  484. uint8 *p, *grid, *attr;
  485. int i, l, xy;
  486. int newnumadj;
  487. struct link *ld;
  488.  
  489.   /* Can word be placed */
  490.   if (INSET(d->words, word)) return 0;
  491.   if (xybase < 0 || xybase+wlen[word] >= GRIDMAX*GRIDMAX+1) return 0;
  492.  
  493. #if SYMMETRICAL
  494.   /* How about star's */
  495.   if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-xybase]))
  496.     return 0;
  497.   if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xybase+wlen[word]-1)]))
  498.     return 0;
  499. #endif
  500.  
  501.   /* check character environment */
  502.   newnumadj = d->numadj;
  503.   for (xy=xybase,grid=d->grid+xy,p=wordbase[word]; *p; grid++,xy++,p++) {
  504.     if (*grid == *p)
  505.       continue; /* Char already there */
  506.     if (!ISFREE(*grid))
  507.       return 0; /* Char placement conflict */
  508.     if (ISSTAR(*p))
  509.       continue; /* Skip stars */
  510.     if (!ISCHAR(grid[-GRIDMAX]) && !ISCHAR(grid[+GRIDMAX]))
  511.       continue; /* No adjecent chars */
  512.  
  513.     if (ISFREE(grid[-GRIDMAX])) {
  514.       d->adjxy [newnumadj] = xy;
  515.       d->adjl  [newnumadj] = links2[*p][grid[+GRIDMAX]];
  516.     } else if (ISFREE(grid[+GRIDMAX])) {
  517.       d->adjxy [newnumadj] = xy-GRIDMAX;
  518.       d->adjl  [newnumadj] = links2[grid[-GRIDMAX]][*p];
  519.     } else {
  520.       d->adjxy [newnumadj] = xy-GRIDMAX;
  521.       d->adjl  [newnumadj] = links3[grid[-GRIDMAX]][*p][grid[+GRIDMAX]];
  522.     }
  523.     if (d->adjl[newnumadj] == 0 || newnumadj == ADJMAX-1)
  524.       return 0;
  525.     d->adjdir[newnumadj++] = 'V';
  526.   }
  527.  
  528.   /* Test if new adj's really exist */
  529.   for (i=d->numadj; i<newnumadj; i++) {
  530.     for (l=d->adjl[i]; l; l=ld->next) {
  531.       ld = &linkdat[l];
  532.       if (test_vword (d, d->adjxy[i]+ld->ofs*GRIDMAX, ld->w))
  533.         break;
  534.     }
  535.     if (l == 0) return 0;
  536.     d->adjl[i] = l;
  537.   }
  538.  
  539.   /* Get a new grid */
  540.   d = mallocnode();
  541.   memcpy (d, data, sizeof(node));
  542.  
  543.   /* Place word */
  544.   BITSET(d->words, word);
  545.   d->numword++;
  546.   d->numadj = newnumadj;
  547.   for (xy=xybase,grid=d->grid+xy,attr=d->attr+xy,p=wordbase[word];
  548.        *p;
  549.        grid++,attr++,xy++,p++) {
  550.     if (!ISSTAR(*p)) {
  551.       *attr &= ~TODOH;
  552.  
  553.       /* Remove character pair hints that are part of the new word */
  554.       for (i=0; i<d->numadj; i++)
  555.         if (d->adjdir[i] == 'H' && d->adjxy[i] == xy) {
  556.           d->adjdir[i] = d->adjdir[--d->numadj];
  557.           d->adjxy [i] = d->adjxy [  d->numadj];
  558.           d->adjl  [i] = d->adjl  [  d->numadj];
  559.           break; /* There can be only one */
  560.         }
  561.  
  562.       /* Place character */
  563.       if (ISFREE(*grid)) {
  564.         d->hash += (123456+xy)*(123456-*p); /* Neat, isn't it? */
  565.         *attr |= TODOV;
  566.         d->numchar++;
  567.       } else {
  568.         d->numconn++;
  569.      }
  570.     }
  571.     *grid = *p;
  572.   }
  573.  
  574.   /* Update hotspot */
  575.   if (xy2level[xy-1] > d->lastlevel)
  576.     d->lastlevel = xy2level[xy-1];
  577.  
  578. #if SYMMETRICAL
  579.   /* Don't forget the symmetry */
  580.   if (d->symdir == 0) {
  581.     d->symdir = 'H';
  582.     d->symxy  = GRIDMAX*GRIDMAX-1-(xybase+wlen[word]-1);
  583.     d->symlen = wlen[word];
  584.   } else
  585.     d->symdir = 0;
  586. #endif
  587.  
  588.   /* Save grid */
  589.   add_node (d);
  590.   return 1;
  591. }
  592.  
  593. int place_vword (node *data, int xybase, int word)
  594. {
  595. node *d=data;
  596. uint8 *p, *grid, *attr;
  597. int i, xy, l;
  598. int newnumadj;
  599. struct link *ld;
  600.  
  601.   /* Some basic tests */
  602.   if (INSET(d->words, word)) return 0;
  603.   if (xybase < 0 || xybase+wlen[word]*GRIDMAX >= GRIDMAX*GRIDMAX+GRIDMAX) return 0;
  604.  
  605. #if SYMMETRICAL
  606.   /* How about star's */
  607.   if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-xybase]))
  608.     return 0;
  609.   if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xybase+wlen[word]*GRIDMAX-GRIDMAX)]))
  610.     return 0;
  611. #endif
  612.  
  613.   /* Check character environment */
  614.   newnumadj = d->numadj;
  615.   for (xy=xybase,grid=d->grid+xy,p=wordbase[word]; *p; grid+=GRIDMAX,xy+=GRIDMAX,p++) {
  616.     if (*grid == *p)
  617.       continue; /* Char already there */
  618.     if (!ISFREE(*grid))
  619.       return 0; /* Char placement conflict */
  620.     if (ISSTAR(*p))
  621.       continue; /* Skip stars */
  622.     if (!ISCHAR(grid[-1]) && !ISCHAR(grid[+1]))
  623.       continue; /* No adjecent chars */
  624.  
  625.     if (ISFREE(grid[-1])) {
  626.       d->adjxy [newnumadj] = xy;
  627.       d->adjl  [newnumadj] = links2[*p][grid[+1]];
  628.     } else if (ISFREE(grid[+1])) {
  629.       d->adjxy [newnumadj] = xy-1;
  630.       d->adjl  [newnumadj] = links2[grid[-1]][*p];
  631.     } else {
  632.       d->adjxy [newnumadj] = xy-1;
  633.       d->adjl  [newnumadj] = links3[grid[-1]][*p][grid[+1]];
  634.     }
  635.     if (d->adjl[newnumadj] == 0 || newnumadj == ADJMAX-1)
  636.       return 0;
  637.     d->adjdir[newnumadj++] = 'H';
  638.   }
  639.  
  640.   /* Test if new adj's really exist */
  641.   for (i=d->numadj; i<newnumadj; i++) {
  642.     for (l=d->adjl[i]; l; l=ld->next) {
  643.       ld = &linkdat[l];
  644.       if (test_hword (d, d->adjxy[i]+ld->ofs, ld->w))
  645.         break;
  646.     }
  647.     if (l == 0) return 0;
  648.     d->adjl[i] = l;
  649.   }
  650.  
  651.   /* Get a new grid */
  652.   d = mallocnode();
  653.   memcpy (d, data, sizeof(node));
  654.  
  655.   /* Place word */
  656.   BITSET(d->words, word);
  657.   d->numword++;
  658.   d->numadj = newnumadj;
  659.   for (xy=xybase,grid=d->grid+xy,attr=d->attr+xy,p=wordbase[word];
  660.        *p;
  661.        grid+=GRIDMAX,attr+=GRIDMAX,xy+=GRIDMAX,p++) {
  662.     if (!ISSTAR(*p)) {
  663.       *attr &= ~TODOV;
  664.  
  665.       /* Remove character pair hints that are part of the new word */
  666.       for (i=0; i<d->numadj; i++)
  667.         if (d->adjdir[i] == 'V' && d->adjxy[i] == xy) {
  668.           d->adjdir[i] = d->adjdir[--d->numadj];
  669.           d->adjxy [i] = d->adjxy [  d->numadj];
  670.           d->adjl  [i] = d->adjl  [  d->numadj];
  671.           break; /* There can be only one */
  672.         }
  673.  
  674.       /* Place character */
  675.       if (ISFREE(*grid)) {
  676.         *attr |= TODOH;
  677.         d->hash += (123456+xy)*(123456-*p); /* Neat, isn't it? */
  678.         d->numchar++;
  679.       } else {
  680.         d->numconn++;
  681.       }
  682.     }
  683.     *grid = *p;
  684.   }
  685.  
  686.   /* Update hotspot */
  687.   if (xy2level[xy-GRIDMAX] > d->lastlevel)
  688.     d->lastlevel = xy2level[xy-GRIDMAX];
  689.  
  690. #if SYMMETRICAL
  691.   /* Don't forget the symmetry */
  692.   if (d->symdir == 0) {
  693.     d->symdir = 'V';
  694.     d->symxy  = GRIDMAX*GRIDMAX-1-(xybase+wlen[word]*GRIDMAX-GRIDMAX);
  695.     d->symlen = wlen[word];
  696.   } else
  697.     d->symdir = 0;
  698. #endif
  699.  
  700.   /* Save grid */
  701.   add_node (d);
  702.   return 1;
  703. }
  704.  
  705. /*
  706. ** Scan a grid and place a word. To supress an exponential growth of
  707. ** generated grids, we can be very fussy when chosing which word to
  708. ** place. I have chosen to fill the grid from top-left to bottom-right
  709. ** making sure the newly placed words fit tightly to the already placed
  710. ** words. If this is not possible, then I choose just one word such that
  711. ** the first letter is nearest to the start of the hotspot regeon.
  712. ** This sounds easy but it took me quite some time to figure it out.
  713. */
  714.  
  715. void scan_grid (node *d)
  716. {
  717. uint8 *grid, *attr;
  718. int xy, l, cnt, tstxy, level;
  719. struct link *ld;
  720. int hasplace, hasfree;
  721.  
  722. #if SYMMETRICAL
  723.   int w ;
  724.   /* Don't forget the symmetry */
  725.   if (d->symdir == 'H') {
  726.     for (w=numword-1; w>=0; w--)
  727.       if (!INSET(d->words, w) && wlen[w] == d->symlen)
  728.         place_hword (d, d->symxy, w);
  729.     return;
  730.   }
  731.   if (d->symdir == 'V') {
  732.     for (w=numword-1; w>=0; w--)
  733.       if (!INSET(d->words, w) && wlen[w] == d->symlen)
  734.         place_vword (d, d->symxy, w);
  735.     return;
  736.   }
  737. #endif
  738.  
  739.   /* locate unaccounted adjacent cells */
  740.   if (d->numadj > 0) {
  741.     xy  = d->adjxy[--d->numadj];
  742.     if (d->adjdir[d->numadj] == 'H') {
  743.       for (l=d->adjl[d->numadj]; l; l=ld->next) {
  744.         ld = &linkdat[l];
  745.         place_hword (d, xy+ld->ofs, ld->w);
  746.       }
  747.     } else {
  748.       for (l=d->adjl[d->numadj]; l; l=ld->next) {
  749.         ld = &linkdat[l];
  750.         place_vword (d, xy+ld->ofs*GRIDMAX, ld->w);
  751.       }
  752.     }
  753.     return;
  754.   }
  755.  
  756.   /* Nominate grid for final result */
  757.   if (d->numword > solution.numword)
  758.     memcpy (&solution, d, sizeof(node));
  759.  
  760.   /* Sweep grid from top-left to bottom-right corner */
  761.   for (level=d->firstlevel; level<=d->lastlevel&&level<=GRIDMAX*2-4; level++) {
  762.  
  763. #if !SYMMETRICAL
  764.     /* Locate 'tight' words */
  765.     hasplace = 0;
  766.     for (xy=level2xy[level],grid=d->grid+xy,attr=d->attr+xy;
  767.          !ISBORDER(*attr);
  768.          xy+=GRIDMAX-1,grid+=GRIDMAX-1,attr+=GRIDMAX-1) {
  769.       if (*attr&TODOH) {
  770.         for (l=links1[*grid]; l; l=ld->next) {
  771.           ld = &linkdat[l];
  772.           tstxy = xy+ld->ofs;
  773.           if (xy2level[tstxy] == d->firstlevel)
  774.             hasplace += place_hword (d, tstxy, ld->w);
  775.         }
  776.       }
  777.       if (*attr&TODOV) {
  778.         for (l=links1[*grid]; l; l=ld->next) {
  779.           ld = &linkdat[l];
  780.           tstxy = xy+ld->ofs*GRIDMAX;
  781.           if (xy2level[tstxy] == d->firstlevel)
  782.             hasplace += place_vword (d, tstxy, ld->w);
  783.         }
  784.       }
  785.       if (hasplace)
  786.         return;
  787.     }
  788. #endif
  789.  
  790.     /* Locate 'adjacent' word */
  791.     hasplace = 0;
  792.     for (xy=level2xy[level],grid=d->grid+xy,attr=d->attr+xy;
  793.          !ISBORDER(*attr);
  794.          xy+=GRIDMAX-1,grid+=GRIDMAX-1,attr+=GRIDMAX-1) {
  795.       if (*attr&TODOH) {
  796.         for (l=links1[*grid]; l; l=ld->next) {
  797.           ld = &linkdat[l];
  798.           tstxy = xy+ld->ofs;
  799.           if (ISSTAR(d->grid[tstxy]))
  800.             hasplace += place_hword (d, tstxy, ld->w);
  801. #if SYMMETRICAL
  802.           if (ISSTAR(d->grid[GRIDMAX*GRIDMAX-1-(tstxy+wlen[ld->w]-1)]))
  803.             hasplace += place_hword (d, tstxy, ld->w);
  804. #endif
  805.         }
  806.       }
  807.       if (*attr&TODOV) {
  808.         for (l=links1[*grid]; l; l=ld->next) {
  809.           ld = &linkdat[l];
  810.           tstxy = xy+ld->ofs*GRIDMAX;
  811.           if (ISSTAR(d->grid[tstxy]))
  812.             hasplace += place_vword (d, tstxy, ld->w);
  813. #if SYMMETRICAL
  814.           if (ISSTAR(d->grid[GRIDMAX*GRIDMAX-1-(tstxy+wlen[ld->w]*GRIDMAX-GRIDMAX)]))
  815.             hasplace += place_vword (d, tstxy, ld->w);
  816. #endif
  817.         }
  818.       }
  819.       if (hasplace)
  820.         return;
  821.     }
  822.  
  823.     /* Locate word fragments (just one word please) */
  824.     hasplace = 0;
  825.     hasfree = 0;
  826.     for (xy=level2xy[level],grid=d->grid+xy,attr=d->attr+xy;
  827.          !ISBORDER(*attr);
  828.          xy+=GRIDMAX-1,grid+=GRIDMAX-1,attr+=GRIDMAX-1) {
  829.       if (ISFREE(*grid))
  830.         hasfree = 1;
  831.       if (*attr&TODOH) {
  832.         cnt = 0;
  833.         for (l=links1[*grid]; l; l=ld->next) {
  834.           ld = &linkdat[l];
  835.           tstxy = xy+ld->ofs;
  836.           if (!ISSTAR(d->grid[tstxy]))
  837.             cnt += place_hword (d, tstxy, ld->w);
  838. #if !SYMMETRICAL
  839.           if (cnt != 0)
  840.             break;
  841. #endif
  842.         }
  843.         if (cnt == 0) {
  844.           /* Speed things up (Not 100% correct, but it's fast) */
  845.           *attr &= ~TODOH;
  846.           grid[-1] = STAR;
  847.           grid[+1] = STAR;
  848. #if SYMMETRICAL
  849.           if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xy-1)])) return; /* Arghh */
  850.           d->grid[GRIDMAX*GRIDMAX-1-(xy-1)] = STAR;
  851.           if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xy+1)])) return; /* Arghh */
  852.           d->grid[GRIDMAX*GRIDMAX-1-(xy+1)] = STAR;
  853. #endif
  854.         }
  855.         hasplace += cnt;
  856.       }
  857.       if (*attr&TODOV) {
  858.         cnt = 0;
  859.         for (l=links1[*grid]; l; l=ld->next) {
  860.           ld = &linkdat[l];
  861.           tstxy = xy+ld->ofs*GRIDMAX;
  862.           if (!ISSTAR(d->grid[tstxy]))
  863.             cnt += place_vword (d, tstxy, ld->w);
  864. #if !SYMMETRICAL
  865.           if (cnt != 0)
  866.             break;
  867. #endif
  868.         }
  869.         if (cnt == 0) {
  870.           /* Speed things up (Not 100% correct, but it's fast) */
  871.           *attr &= ~TODOV;
  872.           grid[-GRIDMAX] = STAR;
  873.           grid[+GRIDMAX] = STAR;
  874. #if SYMMETRICAL
  875.           if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xy-GRIDMAX)])) return; /* Arghh */
  876.           d->grid[GRIDMAX*GRIDMAX-1-(xy-GRIDMAX)] = STAR;
  877.           if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xy+GRIDMAX)])) return; /* Arghh */
  878.           d->grid[GRIDMAX*GRIDMAX-1-(xy+GRIDMAX)] = STAR;
  879. #endif
  880.         }
  881.         hasplace += cnt;
  882.       }
  883.       if (hasplace)
  884.         return;
  885.     }
  886.  
  887.     /* Update hotspot */
  888.     if (!hasfree)
  889.       d->firstlevel = level+1;
  890.   }
  891. }
  892.  
  893. void kick_ass (void)
  894. {
  895. node *d, *todonode;
  896. int i;
  897.  
  898.   for (;;) {
  899.     /* setup up some debugging statistics */
  900.     realnumnode = numnode = numscan = 0;
  901.     hashtst = hashhit = 0;
  902.  
  903.     /* gather all nodes into a single list with highest score first */
  904.     d=todonode = NULL;
  905.     for (i=SCOREMAX-1; i>=0; i--)
  906.       if (scores[i]) {
  907.         if (todonode == NULL)
  908.           d=todonode=scores[i];
  909.         else
  910.           d->next = scores[i];
  911.         while (d->next) d=d->next;
  912.         scores[i] = NULL;
  913.       }
  914.  
  915.     /* Ok babe, lets go!!! */
  916.     while (todonode) {
  917.       d = todonode;
  918.       todonode = d->next;
  919.       if (d->numadj > 0 || numnode<NODEMAX) {
  920.         if (d->numadj == 0) numscan++;
  921.         scan_grid (d);
  922.       }
  923.       d->next = freenode;
  924.       freenode = d;
  925.     }
  926.  
  927.     /* Test for timeouts */
  928.     elapsedtime();
  929.  
  930. #if DEBUG
  931.     fprintf(stderr, "%s word:%2d score:%f level:%2d/%2d node:%4d/%4d/%4d hash:%3d/%3d\n",
  932.             elapsedstr(), solution.numword, solution.score,
  933.             solution.firstlevel, solution.lastlevel, numscan, numnode,
  934.             realnumnode, hashtst, hashhit);
  935.     if (flg_dump) dump_grid (&solution);
  936. #endif
  937.  
  938.     /* Ass kicked? */
  939.     if (realnumnode == 0)
  940.       break;
  941.   }
  942. }
  943.  
  944. void load_words (void)
  945. {
  946. char line[80], c;
  947. FILE *f;
  948. int i, w, done;
  949. uint8 *p;
  950.  
  951.   /* Open file and load the words and check if they are valid */
  952.   f = fopen (wordfname, "r");
  953.   if (!f) {
  954.     fprintf (stderr, "Cannot open %s\n", wordfname);
  955.     exit (1);
  956.   }
  957.   for (numword=0;;) {
  958.     /* Read line */
  959.     fgets (line, sizeof(line), f);
  960.     if (feof(f))
  961.       break;
  962.     /* Copy the word */
  963.     i = 0;
  964.     wordbase[numword][i++] = STAR;
  965.     for (;isalpha(c=line[i-1]);i++)
  966.       wordbase[numword][i] = isupper(c) ? tolower(c)-BASE : c-BASE;
  967.     wordbase[numword][i++] = STAR;
  968.     wordbase[numword][i] = 0;
  969.     wlen[numword] = i;
  970.     if (i > WORDLENMAX-2) {
  971.       fprintf(stderr, "Word too long\n");
  972.       exit(0);
  973.     }
  974.     if (i > 2){
  975.       if (numword == WORDMAX-1) {
  976.         fprintf(stderr, "Too many words\n");
  977.         exit(0);
  978.       } else {
  979.         numword++;}
  980.      }
  981.   }
  982.   fclose (f);
  983.  
  984. #if DEBUG
  985.   fprintf(stderr, "%s Loaded %d words\n", elapsedstr(), numword);
  986. #endif
  987.  
  988.   /*
  989.   ** Calculate links. These are indexes on the wordlist which are used
  990.   ** to quickly locate 1,2,3 long letter sequences within the words.
  991.   */
  992.   numlinkdat=1;
  993.   memset (links1, 0, sizeof(links1));
  994.   memset (links2, 0, sizeof(links2));
  995.   memset (links3, 0, sizeof(links3));
  996.   for (i=0,done=0; !done && i<WORDLENMAX; i++) {
  997.     done = 1;
  998.     for (w=numword-1; w>=0; w--) {
  999.       p = wordbase[w];
  1000.       if (i>=0 && i<=wlen[w]-3) {
  1001.         /* With delimiters */
  1002.         linkdat[numlinkdat].w = w;
  1003.         linkdat[numlinkdat].ofs = -i;
  1004.         linkdat[numlinkdat].next = links3[p[i+0]][p[i+1]][p[i+2]];
  1005.         links3[p[i+0]][p[i+1]][p[i+2]] = numlinkdat++;
  1006.         done = 0;
  1007.       }
  1008.       if (i>=0 && i<=wlen[w]-2) {
  1009.         /* With delimiters */
  1010.         linkdat[numlinkdat].w = w;
  1011.         linkdat[numlinkdat].ofs = -i;
  1012.         linkdat[numlinkdat].next = links2[p[i+0]][p[i+1]];
  1013.         links2[p[i+0]][p[i+1]] = numlinkdat++;
  1014.         done = 0;
  1015.       }
  1016.       if (i>0 && i<=wlen[w]-2) {
  1017.         /* Without delimiters */
  1018.         linkdat[numlinkdat].w = w;
  1019.         linkdat[numlinkdat].ofs = -i;
  1020.         linkdat[numlinkdat].next = links1[p[i]];
  1021.         links1[p[i]] = numlinkdat++;
  1022.         done = 0;
  1023.       }
  1024.     }
  1025.   }
  1026.  
  1027. #if DEBUG
  1028.   fprintf(stderr, "%s Found %d links\n", elapsedstr(), numlinkdat);
  1029. #endif
  1030. }
  1031.  
  1032. int main (int argc, char **argv)
  1033. {
  1034. node *d;
  1035. int x, y, i, w;
  1036.  
  1037. #if DEBUG
  1038.   fprintf(stderr, "sizeof(node)=%d\n", sizeof(node));
  1039. #endif
  1040.  
  1041.   /* simple arguments */
  1042.   for (i=1; i<argc; i++) {
  1043.     if (argv[i][0] != '-')
  1044.       wordfname = argv[i];
  1045.     else {
  1046.       switch (argv[i][1]) {
  1047.         case 'd':
  1048.           flg_dump++; /* Diagnostics */
  1049.           break;
  1050.       }
  1051.     }
  1052.   }
  1053.  
  1054.   /* In case we run out of memory, sigh */
  1055.   fflush (stderr);
  1056.   fflush (stdout);
  1057.  
  1058.   /* Load the database */
  1059.   clktck = sysconf(_SC_CLK_TCK);
  1060.   load_words();
  1061.  
  1062.   /* init grid administration */
  1063.   freenode = NULL;
  1064. #if MALLOC_BROKEN
  1065.   nummalloc = 6000;
  1066.   d=malloc (nummalloc*sizeof(node));
  1067.   if (d == NULL)
  1068.     nummalloc = 0;
  1069.   else
  1070.     for(i=0; i<nummalloc; i++) {
  1071.       d[i].next = freenode;
  1072.       freenode = d+i;
  1073.     }
  1074. #endif
  1075.  
  1076.   /* Do some hotspot pre-calculations */
  1077.   for (i=GRIDMAX*GRIDMAX-1; i>=0; i--)
  1078.     xy2level[i] = (i%GRIDMAX)+(i/GRIDMAX);
  1079.   for (i=0; i<GRIDMAX*2; i++) {
  1080.     if (i<GRIDMAX) {
  1081.       level2xy[i] = i+GRIDMAX-1;
  1082.     } else {
  1083.       level2xy[i] = GRIDMAX*GRIDMAX-(GRIDMAX*2-3-i)*GRIDMAX-2;
  1084.     }
  1085.   }
  1086.  
  1087.   /* create an initial grid */
  1088.   d = (node*) calloc(1, sizeof(node));
  1089.   for (y=GRIDMAX-1; y>=0; y--)
  1090.     for (x=GRIDMAX-1; x>=0; x--) {
  1091.       d->grid[x+y*GRIDMAX] = STAR;
  1092.       d->attr[x+y*GRIDMAX] = BORDER;
  1093.     }
  1094.   for (y=GRIDMAX-2; y>0; y--)
  1095.     for (x=GRIDMAX-2; x>0; x--) {
  1096.       d->grid[x+y*GRIDMAX] = FREE;
  1097.       d->attr[x+y*GRIDMAX] = 0;
  1098.     }
  1099.  
  1100.   /* Place all the words for starters */
  1101. #if SYMMETRICAL
  1102.   /* work from the middle out */
  1103.   for (w=numword-1; w>=0; w--)
  1104.     if (wlen[w]>=5) {
  1105.       d->hash = 0;
  1106.       place_hword (d, (GRIDMAX/2+2-wlen[w])+(GRIDMAX/2)*GRIDMAX, w);
  1107.     }
  1108. #else
  1109.   /* work from top-left to bottom-right */
  1110.   for (w=numword-1; w>=0; w--) {
  1111.     d->firstlevel = 2;
  1112.     place_hword (d, 0+1*GRIDMAX, w);
  1113.   }
  1114. #endif
  1115.   d->next = freenode;
  1116.   freenode = d;
  1117.  
  1118.   /* Here we go */
  1119.   kick_ass();
  1120.   dump_grid (&solution);
  1121.  
  1122.   exit(0);
  1123. }
  1124.