Subversion Repositories wimsdev

Compare Revisions

No changes between revisions

Ignore whitespace Rev 3397 → Rev 3398

/trunk/wims/src/Misc/crossword/Makefile.in
0,0 → 1,17
# @configure_input@
 
wims_home=../../..
cc=@CC@
cflags=@CFLAGS@ -Wall
defines=@DEFINES@
lopt=-lm
 
LDFLAGS =
 
all: crossword
 
clean:
rm -f *.o *.core *.stackdump crossword
 
crossword: crossword.c
$(cc) $(cflags) $(LDFLAGS) -o crossword crossword.c
Property changes:
Added: svn:executable
+*
\ No newline at end of property
/trunk/wims/src/Misc/crossword/crossword.c
0,0 → 1,1129
/*2010-08-20
 
Modify the output to wims
Rename it in crossword.c
Bernadette Perrin-Riou
bpr@math.u-psud.fr
 
*/
 
/* POTM ENTRY: jigsaw */
/* Your Name: Franz Korntner */
/* Your email: fkorntne@hiscom.nl */
/* korntner@xs4all.nl */
/* To: enter@potm.ffast.att.com */
/* Subject: C R O Z Z L E */
 
/*
** Hello Fred. This is my entry with some additional comments and
** some modifications to make it more portable. I haven't changed
** the algorithm in any way whatsoever.
** Because the source will be made available to the public, I've
** included a copyright message to state that the source is free
** and not public-domain. I'm sure thats alright with you.
*/
 
/*
jigsaw, to create crossword puzzle grids
Copyright 1996 Franz Korntner <fkorntne@hiscom.nl> <korntner@xs4all.nl>
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
 
/*
** *** For your pleasure only ***
**
** The code can produce symmetrical grids by setting the macro
** SYMMETRICAL to 1. Sadly enough not as many words will fit
** into a symmetrical grid than into a non-symmetrical grid,
** so NODEMAX must be increased (say with 1000) to get better results.
*/
/* Configuring parameters */
 
#define TIMEMAX (10*60-15) /* 10 minute limit */
#define DEBUG 0 /* 0=Off 1=On 2=Verbose */
#define NODEMAX 1500 /* 500=Fast 1500=Normal */
#define SYMMETRICAL 0 /* 0=No 1=Yes */
#define MALLOC_BROKEN 1 /* 0=No 1=Yes (See below) */
#define WIMS 1
/*
** It seems that malloc() under Linux is broken. I could only allocate
** a couple of thousand nodes before my program crashed. A solution was to
** allocate a BIG chunk of memory and split it up manually. This way about
** 10500 nodes could be allocated.
*/
 
/* Start of program */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/times.h>
 
#if defined(__alpha__)
#ifndef __GNUC__
#error I want GCC !!!
#endif
typedef int int8 __attribute__ ((mode (QI)));
typedef unsigned int uint8 __attribute__ ((mode (QI)));
typedef int int16 __attribute__ ((mode (HI)));
typedef unsigned int uint16 __attribute__ ((mode (HI)));
typedef int int32 __attribute__ ((mode (SI)));
typedef unsigned int uint32 __attribute__ ((mode (SI)));
#else
/* It seems that your Sparc-GCC doesn't support the above :-(( */
typedef char int8;
typedef unsigned char uint8;
typedef short int16;
typedef unsigned short uint16;
typedef long int32;
typedef unsigned long uint32;
#endif
 
#define GRIDMAX 22 /* Size of grid incl. border */
#define LINKMAX 4096 /* # links */
#define WORDMAX 256 /* # words */
#define WORDLENMAX 32 /* # length of word */
#define ADJMAX 128 /* # unaccounted adjacent chars */
#define SCOREMAX 1000 /* Spead for hashing */
 
#define BASE ('a'-1) /* Word conversion */
#define STAR (28) /* Word delimiters */
#define FREE (31) /* Unoccupied grid cell */
#define TODOH 1 /* Hint: Hor. word can be here */
#define TODOV 2 /* Hint: Ver. word can be here */
#define BORDER 4 /* Cell is grid border */
 
#define ISSTAR(C) ((C)==STAR) /* Test the above */
#define ISFREE(C) ((C)==FREE)
#define ISCHAR(C) ((C)< STAR)
#define ISBORDER(C) ((C)&BORDER)
 
#define BITSET(M,B) ((M).s[(B)>>3] |= (1<<((B)&7))) /* BitSet */
#define BITCLR(M,B) ((M).s[(B)>>3] &= ~(1<<((B)&7))) /* manipulation */
#define INSET(M,B) ((M).s[(B)>>3] & (1<<((B)&7)))
 
typedef struct { /* Bitset containing 256 bits */
uint8 s[32];
} SET;
 
typedef struct node {
struct node *next; /* */
int seqnr; /* For diagnostics */
SET words; /* Summary of placed words */
int numword,numchar,numconn;/* Statistics */
uint32 hash; /* Duplicate detection */
int firstlevel, lastlevel; /* Grids hotspot */
float score; /* Will it survive? */
int8 symdir; /* Force symmetry */
int16 symxy; /* */
int16 symlen; /* */
int numadj; /* # unprocessed char pairs */
int8 adjdir[ADJMAX]; /* Pair's direction */
int16 adjxy[ADJMAX]; /* Pair's location */
int16 adjl[ADJMAX]; /* Pair's wordlist */
uint8 grid[GRIDMAX*GRIDMAX]; /* *THE* grid */
uint8 attr[GRIDMAX*GRIDMAX]; /* Grid hints */
} node;
 
struct link {
int16 next;
int16 w; /* What's the word */
int16 ofs; /* Were's in the word */
};
 
 
/* The external word list */
uint8 wordbase[WORDMAX][WORDLENMAX]; /* Converted wordlist */
int wlen[WORDMAX]; /* Length of words */
int numword; /* How many */
char *wordfname = "wordlist"; /* Where are they found */
 
/* Where are 1,2,3 long character combinations */
int16 links1[32]; /* 1-char wordlist */
int16 links2[32][32]; /* 2-char wordlist */
int16 links3[32][32][32]; /* 3-char wordlist */
struct link linkdat[LINKMAX]; /* Body above wordlist */
int numlinkdat; /* How many */
 
/* Hotspot pre-calculations */
int16 xy2level[GRIDMAX*GRIDMAX]; /* distance 0,0 to x,y */
int16 level2xy[GRIDMAX*2]; /* inverse */
 
/* Node administration */
node *freenode; /* Don't malloc() too much */
int numnode, realnumnode; /* Statistics */
node solution; /* What are we doing? */
node *scores[SCOREMAX]; /* Speed up hashing */
 
/* Diagnostics */
int seqnr;
int hashtst, hashhit;
int nummalloc;
int numscan;
int flg_dump;
 
/* What's left */
int clktck; /* Timing portability */
 
/* Forward declartions */
char *elapsedstr(void);
 
/*
** Display the grid. Show disgnostics info in verbose mode
*/
 
void dump_grid (node *d)
{
int x, y;
 
#if DEBUG==2
int attr, cnt;
/* Double check the number of words */
cnt = 0;
for (x=GRIDMAX*GRIDMAX-1; x>=0; x--)
if (ISCHAR(d->grid[x])) {
if (!ISCHAR(d->grid[x-1]) && ISCHAR(d->grid[x+1])) cnt++;
if (!ISCHAR(d->grid[x-GRIDMAX]) && ISCHAR(d->grid[x+GRIDMAX])) cnt++;
}
printf("%s seqnr:%d level:%d/%d score:%f numword:%d/%d numchar:%d numconn:%d\n",
elapsedstr(), d->seqnr, d->firstlevel, d->lastlevel, d->score, d->numword,
cnt, d->numchar, d->numconn);
#endif
 
#if DEBUG==2
/* Show grid as I would like to see it */
for (y=0; y<GRIDMAX; y++) {
for (x=0; x<GRIDMAX; x++) {
attr = d->attr[x+y*GRIDMAX]&(TODOH|TODOV);
if (attr==(TODOH|TODOV))
printf("B");
else if (attr==TODOH)
printf("H");
else if (attr==TODOV)
printf("V");
else
printf(".");
}
printf (" ");
for (x=0; x<GRIDMAX; x++)
if (ISSTAR(d->grid[x+y*GRIDMAX]))
printf("*");
else if (ISFREE(d->grid[x+y*GRIDMAX]))
printf(".");
else if (ISCHAR(d->grid[x+y*GRIDMAX]))
printf ("%c", d->grid[x+y*GRIDMAX]+BASE);
else
printf("0x%02x", d->grid[x+y*GRIDMAX]);
#else
# if WIMS==1
int sizex, sizey ; sizex=0 ; sizey=0;
for (y=1; y<GRIDMAX; y++) {
for (x=1; x<GRIDMAX; x++) {
if (ISCHAR(d->grid[x+y*GRIDMAX])) { if (x > sizex) sizex = x ; }
}
}
for (x=1; x<GRIDMAX-1; x++) {
for (y=1; y<GRIDMAX-1; y++) {
if (ISCHAR(d->grid[x+y*GRIDMAX])) { if (y > sizey) sizey = y ; }
}
}
for (y=1; y<sizey+1; y++) {
for (x=1; x<sizex+1; x++) {
if (!ISCHAR(d->grid[x+y*GRIDMAX]) && x <sizex ){ printf(",");}
else {
if (ISCHAR(d->grid[x+y*GRIDMAX])){
printf ("%c", d->grid[x+y*GRIDMAX]+BASE);
if (x <sizex) printf(",") ;
}
}
}
printf("\n");
}
#else
/* Show grid as Fred would like to see it */
for (y=1; y<GRIDMAX-1; y++) {
for (x=1; x<GRIDMAX-1; x++)
if (ISCHAR(d->grid[x+y*GRIDMAX]))
printf ("%c", d->grid[x+y*GRIDMAX]+BASE);
else
printf("-");
#endif
#endif
 
 
/* For diagnostics */
fflush (stdout);
}
 
int elapsedtime (void)
{
int seconds;
static struct tms TMS;
 
times(&TMS);
 
/* add the elapsed system and user times */
/* calculation comes out to the nearest second - rounded down */
seconds = (TMS.tms_stime + TMS.tms_utime)/clktck;
if (seconds < 1) seconds=1;
if (seconds >= TIMEMAX) {
/* PRINT OUT YOUR SOLUTION BEFORE YOU GO! */
/*
** HELP, the algorithm must compleet well under 10 minutes.
*/
dump_grid (&solution);
exit(0);
}
return seconds;
}
 
/*
** Elapsed time for diagnostics
*/
char *elapsedstr(void)
{
static char line[40];
int seconds;
 
seconds = elapsedtime();
sprintf(line, "%02d:%02d", seconds/60, seconds%60);
return line;
}
 
/*
** Get a node, reusing free'ed nodes.
*/
 
node *mallocnode (void)
{
node *d;
 
d = freenode;
if (d == NULL) {
d = (node*) malloc (sizeof(node));
nummalloc++;
} else
freenode = d->next;
 
if (d == NULL) {
fprintf (stderr, "Out of memory after %d nodes (%d bytes)\n", nummalloc, nummalloc*sizeof(node));
dump_grid (&solution);
exit(0);
}
 
return d;
}
 
void add_node (node *d)
{
int i;
node **prev, *next;
 
/* Evaluate grids score */
d->score = (float)d->numconn/d->numchar;
 
/* Insert grid into sorted list, eliminating duplicates */
i = d->score*(SCOREMAX-1);
if (i<0) i=0;
if (i>=SCOREMAX) i=SCOREMAX-1;
prev = &scores[i];
next = scores[i];
for (;;) {
if (next == NULL) break;
if (d->score > next->score) break;
if (d->score == next->score && d->hash >= next->hash) break;
prev = &next->next;
next = next->next;
}
 
/* Test if entry is duplicate */
while (next && d->score == next->score && d->hash == next->hash) {
hashtst++;
if (memcmp(d->grid, next->grid, sizeof(d->grid)) == 0) {
d->next = freenode;
freenode = d;
return;
}
hashhit++;
prev = &next->next;
next = next->next;
}
 
/* Diagnostics */
if (flg_dump > 1)
dump_grid (d);
 
/* Enter grid into list */
d->seqnr = seqnr++;
d->next = next;
(*prev) = d;
if (d->numadj == 0)
numnode++;
realnumnode++;
}
 
/*
** The next two routines test if a given word can be placed in the grid.
** If a new character will be adjecent to an existing character, check
** if the newly formed character pair exist in the wordlist (it doesn't
** matter where).
*/
 
int test_hword (node *d, int xybase, int word)
{
uint8 *p, *grid;
int l;
 
/* Some basic tests */
if (xybase < 0 || xybase+wlen[word] >= GRIDMAX*GRIDMAX+1) return 0;
 
#if SYMMETRICAL
/* How about star's */
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-xybase]))
return 0;
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xybase+wlen[word]-1)]))
return 0;
#endif
 
/* Will new characters create conflicts */
for (grid=d->grid+xybase,p=wordbase[word]; *p; grid++,p++) {
if (*grid == *p)
continue; /* Char already there */
if (!ISFREE(*grid))
return 0; /* Char placement conflict */
if (ISSTAR(*p))
continue; /* Skip stars */
if (!ISCHAR(grid[-GRIDMAX]) && !ISCHAR(grid[+GRIDMAX]))
continue; /* No adjecent chars */
 
if (ISFREE(grid[-GRIDMAX]))
l = links2[*p][grid[+GRIDMAX]];
else if (ISFREE(grid[+GRIDMAX]))
l = links2[grid[-GRIDMAX]][*p];
else
l = links3[grid[-GRIDMAX]][*p][grid[+GRIDMAX]];
if (l == 0)
return 0;
}
 
/* Word can be placed */
return 1;
}
 
int test_vword (node *d, int xybase, int word)
{
uint8 *p, *grid;
int l;
 
/* Some basic tests */
if (xybase < 0 || xybase+wlen[word]*GRIDMAX >= GRIDMAX*GRIDMAX+GRIDMAX) return 0;
 
#if SYMMETRICAL
/* How about star's */
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-xybase]))
return 0;
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xybase+wlen[word]*GRIDMAX-GRIDMAX)]))
return 0;
#endif
 
/* Will new characters create conflicts */
for (grid=d->grid+xybase,p=wordbase[word]; *p; grid+=GRIDMAX,p++) {
if (*grid == *p)
continue; /* Char already there */
if (!ISFREE(*grid))
return 0; /* Char placement conflict */
if (ISSTAR(*p))
continue; /* Skip stars */
if (!ISCHAR(grid[-1]) && !ISCHAR(grid[+1]))
continue; /* No adjacent characters */
 
if (ISFREE(grid[-1]))
l = links2[*p][grid[+1]];
else if (ISFREE(grid[+1]))
l = links2[grid[-1]][*p];
else
l = links3[grid[-1]][*p][grid[+1]];
if (l == 0)
return 0;
}
 
/* Word can be placed */
return 1;
}
 
/*
** The next two routines will place a given word in the grid. These routines
** also performs several sanity checks to make sure the new grid is worth
** it to continue with. If a newly placed character is adjecent to an
** existing character, then that pair must be part of a word that can
** be physically placed. If multiple character pairs exist, then no check
** is done to determine if those words (of which the pairs are part) can
** be adjecent. That is done later as these grids are not counted against
** NODEMAX.
*/
 
int place_hword (node *data, int xybase, int word)
{
node *d=data;
uint8 *p, *grid, *attr;
int i, l, xy;
int newnumadj;
struct link *ld;
 
/* Can word be placed */
if (INSET(d->words, word)) return 0;
if (xybase < 0 || xybase+wlen[word] >= GRIDMAX*GRIDMAX+1) return 0;
 
#if SYMMETRICAL
/* How about star's */
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-xybase]))
return 0;
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xybase+wlen[word]-1)]))
return 0;
#endif
 
/* check character environment */
newnumadj = d->numadj;
for (xy=xybase,grid=d->grid+xy,p=wordbase[word]; *p; grid++,xy++,p++) {
if (*grid == *p)
continue; /* Char already there */
if (!ISFREE(*grid))
return 0; /* Char placement conflict */
if (ISSTAR(*p))
continue; /* Skip stars */
if (!ISCHAR(grid[-GRIDMAX]) && !ISCHAR(grid[+GRIDMAX]))
continue; /* No adjecent chars */
 
if (ISFREE(grid[-GRIDMAX])) {
d->adjxy [newnumadj] = xy;
d->adjl [newnumadj] = links2[*p][grid[+GRIDMAX]];
} else if (ISFREE(grid[+GRIDMAX])) {
d->adjxy [newnumadj] = xy-GRIDMAX;
d->adjl [newnumadj] = links2[grid[-GRIDMAX]][*p];
} else {
d->adjxy [newnumadj] = xy-GRIDMAX;
d->adjl [newnumadj] = links3[grid[-GRIDMAX]][*p][grid[+GRIDMAX]];
}
if (d->adjl[newnumadj] == 0 || newnumadj == ADJMAX-1)
return 0;
d->adjdir[newnumadj++] = 'V';
}
 
/* Test if new adj's really exist */
for (i=d->numadj; i<newnumadj; i++) {
for (l=d->adjl[i]; l; l=ld->next) {
ld = &linkdat[l];
if (test_vword (d, d->adjxy[i]+ld->ofs*GRIDMAX, ld->w))
break;
}
if (l == 0) return 0;
d->adjl[i] = l;
}
 
/* Get a new grid */
d = mallocnode();
memcpy (d, data, sizeof(node));
 
/* Place word */
BITSET(d->words, word);
d->numword++;
d->numadj = newnumadj;
for (xy=xybase,grid=d->grid+xy,attr=d->attr+xy,p=wordbase[word];
*p;
grid++,attr++,xy++,p++) {
if (!ISSTAR(*p)) {
*attr &= ~TODOH;
 
/* Remove character pair hints that are part of the new word */
for (i=0; i<d->numadj; i++)
if (d->adjdir[i] == 'H' && d->adjxy[i] == xy) {
d->adjdir[i] = d->adjdir[--d->numadj];
d->adjxy [i] = d->adjxy [ d->numadj];
d->adjl [i] = d->adjl [ d->numadj];
break; /* There can be only one */
}
 
/* Place character */
if (ISFREE(*grid)) {
d->hash += (123456+xy)*(123456-*p); /* Neat, isn't it? */
*attr |= TODOV;
d->numchar++;
} else {
d->numconn++;
}
}
*grid = *p;
}
 
/* Update hotspot */
if (xy2level[xy-1] > d->lastlevel)
d->lastlevel = xy2level[xy-1];
 
#if SYMMETRICAL
/* Don't forget the symmetry */
if (d->symdir == 0) {
d->symdir = 'H';
d->symxy = GRIDMAX*GRIDMAX-1-(xybase+wlen[word]-1);
d->symlen = wlen[word];
} else
d->symdir = 0;
#endif
 
/* Save grid */
add_node (d);
return 1;
}
 
int place_vword (node *data, int xybase, int word)
{
node *d=data;
uint8 *p, *grid, *attr;
int i, xy, l;
int newnumadj;
struct link *ld;
 
/* Some basic tests */
if (INSET(d->words, word)) return 0;
if (xybase < 0 || xybase+wlen[word]*GRIDMAX >= GRIDMAX*GRIDMAX+GRIDMAX) return 0;
 
#if SYMMETRICAL
/* How about star's */
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-xybase]))
return 0;
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xybase+wlen[word]*GRIDMAX-GRIDMAX)]))
return 0;
#endif
 
/* Check character environment */
newnumadj = d->numadj;
for (xy=xybase,grid=d->grid+xy,p=wordbase[word]; *p; grid+=GRIDMAX,xy+=GRIDMAX,p++) {
if (*grid == *p)
continue; /* Char already there */
if (!ISFREE(*grid))
return 0; /* Char placement conflict */
if (ISSTAR(*p))
continue; /* Skip stars */
if (!ISCHAR(grid[-1]) && !ISCHAR(grid[+1]))
continue; /* No adjecent chars */
 
if (ISFREE(grid[-1])) {
d->adjxy [newnumadj] = xy;
d->adjl [newnumadj] = links2[*p][grid[+1]];
} else if (ISFREE(grid[+1])) {
d->adjxy [newnumadj] = xy-1;
d->adjl [newnumadj] = links2[grid[-1]][*p];
} else {
d->adjxy [newnumadj] = xy-1;
d->adjl [newnumadj] = links3[grid[-1]][*p][grid[+1]];
}
if (d->adjl[newnumadj] == 0 || newnumadj == ADJMAX-1)
return 0;
d->adjdir[newnumadj++] = 'H';
}
 
/* Test if new adj's really exist */
for (i=d->numadj; i<newnumadj; i++) {
for (l=d->adjl[i]; l; l=ld->next) {
ld = &linkdat[l];
if (test_hword (d, d->adjxy[i]+ld->ofs, ld->w))
break;
}
if (l == 0) return 0;
d->adjl[i] = l;
}
 
/* Get a new grid */
d = mallocnode();
memcpy (d, data, sizeof(node));
 
/* Place word */
BITSET(d->words, word);
d->numword++;
d->numadj = newnumadj;
for (xy=xybase,grid=d->grid+xy,attr=d->attr+xy,p=wordbase[word];
*p;
grid+=GRIDMAX,attr+=GRIDMAX,xy+=GRIDMAX,p++) {
if (!ISSTAR(*p)) {
*attr &= ~TODOV;
 
/* Remove character pair hints that are part of the new word */
for (i=0; i<d->numadj; i++)
if (d->adjdir[i] == 'V' && d->adjxy[i] == xy) {
d->adjdir[i] = d->adjdir[--d->numadj];
d->adjxy [i] = d->adjxy [ d->numadj];
d->adjl [i] = d->adjl [ d->numadj];
break; /* There can be only one */
}
 
/* Place character */
if (ISFREE(*grid)) {
*attr |= TODOH;
d->hash += (123456+xy)*(123456-*p); /* Neat, isn't it? */
d->numchar++;
} else {
d->numconn++;
}
}
*grid = *p;
}
 
/* Update hotspot */
if (xy2level[xy-GRIDMAX] > d->lastlevel)
d->lastlevel = xy2level[xy-GRIDMAX];
 
#if SYMMETRICAL
/* Don't forget the symmetry */
if (d->symdir == 0) {
d->symdir = 'V';
d->symxy = GRIDMAX*GRIDMAX-1-(xybase+wlen[word]*GRIDMAX-GRIDMAX);
d->symlen = wlen[word];
} else
d->symdir = 0;
#endif
 
/* Save grid */
add_node (d);
return 1;
}
 
/*
** Scan a grid and place a word. To supress an exponential growth of
** generated grids, we can be very fussy when chosing which word to
** place. I have chosen to fill the grid from top-left to bottom-right
** making sure the newly placed words fit tightly to the already placed
** words. If this is not possible, then I choose just one word such that
** the first letter is nearest to the start of the hotspot regeon.
** This sounds easy but it took me quite some time to figure it out.
*/
 
void scan_grid (node *d)
{
uint8 *grid, *attr;
int xy, l, cnt, tstxy, level;
struct link *ld;
int hasplace, hasfree;
 
#if SYMMETRICAL
int w ;
/* Don't forget the symmetry */
if (d->symdir == 'H') {
for (w=numword-1; w>=0; w--)
if (!INSET(d->words, w) && wlen[w] == d->symlen)
place_hword (d, d->symxy, w);
return;
}
if (d->symdir == 'V') {
for (w=numword-1; w>=0; w--)
if (!INSET(d->words, w) && wlen[w] == d->symlen)
place_vword (d, d->symxy, w);
return;
}
#endif
 
/* locate unaccounted adjacent cells */
if (d->numadj > 0) {
xy = d->adjxy[--d->numadj];
if (d->adjdir[d->numadj] == 'H') {
for (l=d->adjl[d->numadj]; l; l=ld->next) {
ld = &linkdat[l];
place_hword (d, xy+ld->ofs, ld->w);
}
} else {
for (l=d->adjl[d->numadj]; l; l=ld->next) {
ld = &linkdat[l];
place_vword (d, xy+ld->ofs*GRIDMAX, ld->w);
}
}
return;
}
 
/* Nominate grid for final result */
if (d->numword > solution.numword)
memcpy (&solution, d, sizeof(node));
 
/* Sweep grid from top-left to bottom-right corner */
for (level=d->firstlevel; level<=d->lastlevel&&level<=GRIDMAX*2-4; level++) {
 
#if !SYMMETRICAL
/* Locate 'tight' words */
hasplace = 0;
for (xy=level2xy[level],grid=d->grid+xy,attr=d->attr+xy;
!ISBORDER(*attr);
xy+=GRIDMAX-1,grid+=GRIDMAX-1,attr+=GRIDMAX-1) {
if (*attr&TODOH) {
for (l=links1[*grid]; l; l=ld->next) {
ld = &linkdat[l];
tstxy = xy+ld->ofs;
if (xy2level[tstxy] == d->firstlevel)
hasplace += place_hword (d, tstxy, ld->w);
}
}
if (*attr&TODOV) {
for (l=links1[*grid]; l; l=ld->next) {
ld = &linkdat[l];
tstxy = xy+ld->ofs*GRIDMAX;
if (xy2level[tstxy] == d->firstlevel)
hasplace += place_vword (d, tstxy, ld->w);
}
}
if (hasplace)
return;
}
#endif
 
/* Locate 'adjacent' word */
hasplace = 0;
for (xy=level2xy[level],grid=d->grid+xy,attr=d->attr+xy;
!ISBORDER(*attr);
xy+=GRIDMAX-1,grid+=GRIDMAX-1,attr+=GRIDMAX-1) {
if (*attr&TODOH) {
for (l=links1[*grid]; l; l=ld->next) {
ld = &linkdat[l];
tstxy = xy+ld->ofs;
if (ISSTAR(d->grid[tstxy]))
hasplace += place_hword (d, tstxy, ld->w);
#if SYMMETRICAL
if (ISSTAR(d->grid[GRIDMAX*GRIDMAX-1-(tstxy+wlen[ld->w]-1)]))
hasplace += place_hword (d, tstxy, ld->w);
#endif
}
}
if (*attr&TODOV) {
for (l=links1[*grid]; l; l=ld->next) {
ld = &linkdat[l];
tstxy = xy+ld->ofs*GRIDMAX;
if (ISSTAR(d->grid[tstxy]))
hasplace += place_vword (d, tstxy, ld->w);
#if SYMMETRICAL
if (ISSTAR(d->grid[GRIDMAX*GRIDMAX-1-(tstxy+wlen[ld->w]*GRIDMAX-GRIDMAX)]))
hasplace += place_vword (d, tstxy, ld->w);
#endif
}
}
if (hasplace)
return;
}
 
/* Locate word fragments (just one word please) */
hasplace = 0;
hasfree = 0;
for (xy=level2xy[level],grid=d->grid+xy,attr=d->attr+xy;
!ISBORDER(*attr);
xy+=GRIDMAX-1,grid+=GRIDMAX-1,attr+=GRIDMAX-1) {
if (ISFREE(*grid))
hasfree = 1;
if (*attr&TODOH) {
cnt = 0;
for (l=links1[*grid]; l; l=ld->next) {
ld = &linkdat[l];
tstxy = xy+ld->ofs;
if (!ISSTAR(d->grid[tstxy]))
cnt += place_hword (d, tstxy, ld->w);
#if !SYMMETRICAL
if (cnt != 0)
break;
#endif
}
if (cnt == 0) {
/* Speed things up (Not 100% correct, but it's fast) */
*attr &= ~TODOH;
grid[-1] = STAR;
grid[+1] = STAR;
#if SYMMETRICAL
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xy-1)])) return; /* Arghh */
d->grid[GRIDMAX*GRIDMAX-1-(xy-1)] = STAR;
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xy+1)])) return; /* Arghh */
d->grid[GRIDMAX*GRIDMAX-1-(xy+1)] = STAR;
#endif
}
hasplace += cnt;
}
if (*attr&TODOV) {
cnt = 0;
for (l=links1[*grid]; l; l=ld->next) {
ld = &linkdat[l];
tstxy = xy+ld->ofs*GRIDMAX;
if (!ISSTAR(d->grid[tstxy]))
cnt += place_vword (d, tstxy, ld->w);
#if !SYMMETRICAL
if (cnt != 0)
break;
#endif
}
if (cnt == 0) {
/* Speed things up (Not 100% correct, but it's fast) */
*attr &= ~TODOV;
grid[-GRIDMAX] = STAR;
grid[+GRIDMAX] = STAR;
#if SYMMETRICAL
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xy-GRIDMAX)])) return; /* Arghh */
d->grid[GRIDMAX*GRIDMAX-1-(xy-GRIDMAX)] = STAR;
if (ISCHAR(d->grid[GRIDMAX*GRIDMAX-1-(xy+GRIDMAX)])) return; /* Arghh */
d->grid[GRIDMAX*GRIDMAX-1-(xy+GRIDMAX)] = STAR;
#endif
}
hasplace += cnt;
}
if (hasplace)
return;
}
 
/* Update hotspot */
if (!hasfree)
d->firstlevel = level+1;
}
}
 
void kick_ass (void)
{
node *d, *todonode;
int i;
 
for (;;) {
/* setup up some debugging statistics */
realnumnode = numnode = numscan = 0;
hashtst = hashhit = 0;
 
/* gather all nodes into a single list with highest score first */
d=todonode = NULL;
for (i=SCOREMAX-1; i>=0; i--)
if (scores[i]) {
if (todonode == NULL)
d=todonode=scores[i];
else
d->next = scores[i];
while (d->next) d=d->next;
scores[i] = NULL;
}
 
/* Ok babe, lets go!!! */
while (todonode) {
d = todonode;
todonode = d->next;
if (d->numadj > 0 || numnode<NODEMAX) {
if (d->numadj == 0) numscan++;
scan_grid (d);
}
d->next = freenode;
freenode = d;
}
 
/* Test for timeouts */
elapsedtime();
 
#if DEBUG
fprintf(stderr, "%s word:%2d score:%f level:%2d/%2d node:%4d/%4d/%4d hash:%3d/%3d\n",
elapsedstr(), solution.numword, solution.score,
solution.firstlevel, solution.lastlevel, numscan, numnode,
realnumnode, hashtst, hashhit);
if (flg_dump) dump_grid (&solution);
#endif
 
/* Ass kicked? */
if (realnumnode == 0)
break;
}
}
 
void load_words (void)
{
char line[80], c;
FILE *f;
int i, w, done;
uint8 *p;
 
/* Open file and load the words and check if they are valid */
f = fopen (wordfname, "r");
if (!f) {
fprintf (stderr, "Cannot open %s\n", wordfname);
exit (1);
}
for (numword=0;;) {
/* Read line */
fgets (line, sizeof(line), f);
if (feof(f))
break;
/* Copy the word */
i = 0;
wordbase[numword][i++] = STAR;
for (;isalpha(c=line[i-1]);i++)
wordbase[numword][i] = isupper(c) ? tolower(c)-BASE : c-BASE;
wordbase[numword][i++] = STAR;
wordbase[numword][i] = 0;
wlen[numword] = i;
if (i > WORDLENMAX-2) {
fprintf(stderr, "Word too long\n");
exit(0);
}
if (i > 2){
if (numword == WORDMAX-1) {
fprintf(stderr, "Too many words\n");
exit(0);
} else {
numword++;}
}
}
fclose (f);
 
#if DEBUG
fprintf(stderr, "%s Loaded %d words\n", elapsedstr(), numword);
#endif
 
/*
** Calculate links. These are indexes on the wordlist which are used
** to quickly locate 1,2,3 long letter sequences within the words.
*/
numlinkdat=1;
memset (links1, 0, sizeof(links1));
memset (links2, 0, sizeof(links2));
memset (links3, 0, sizeof(links3));
for (i=0,done=0; !done && i<WORDLENMAX; i++) {
done = 1;
for (w=numword-1; w>=0; w--) {
p = wordbase[w];
if (i>=0 && i<=wlen[w]-3) {
/* With delimiters */
linkdat[numlinkdat].w = w;
linkdat[numlinkdat].ofs = -i;
linkdat[numlinkdat].next = links3[p[i+0]][p[i+1]][p[i+2]];
links3[p[i+0]][p[i+1]][p[i+2]] = numlinkdat++;
done = 0;
}
if (i>=0 && i<=wlen[w]-2) {
/* With delimiters */
linkdat[numlinkdat].w = w;
linkdat[numlinkdat].ofs = -i;
linkdat[numlinkdat].next = links2[p[i+0]][p[i+1]];
links2[p[i+0]][p[i+1]] = numlinkdat++;
done = 0;
}
if (i>0 && i<=wlen[w]-2) {
/* Without delimiters */
linkdat[numlinkdat].w = w;
linkdat[numlinkdat].ofs = -i;
linkdat[numlinkdat].next = links1[p[i]];
links1[p[i]] = numlinkdat++;
done = 0;
}
}
}
 
#if DEBUG
fprintf(stderr, "%s Found %d links\n", elapsedstr(), numlinkdat);
#endif
}
 
int main (int argc, char **argv)
{
node *d;
int x, y, i, w;
 
#if DEBUG
fprintf(stderr, "sizeof(node)=%d\n", sizeof(node));
#endif
 
/* simple arguments */
for (i=1; i<argc; i++) {
if (argv[i][0] != '-')
wordfname = argv[i];
else {
switch (argv[i][1]) {
case 'd':
flg_dump++; /* Diagnostics */
break;
}
}
}
 
/* In case we run out of memory, sigh */
fflush (stderr);
fflush (stdout);
 
/* Load the database */
clktck = sysconf(_SC_CLK_TCK);
load_words();
 
/* init grid administration */
freenode = NULL;
#if MALLOC_BROKEN
nummalloc = 6000;
d=malloc (nummalloc*sizeof(node));
if (d == NULL)
nummalloc = 0;
else
for(i=0; i<nummalloc; i++) {
d[i].next = freenode;
freenode = d+i;
}
#endif
 
/* Do some hotspot pre-calculations */
for (i=GRIDMAX*GRIDMAX-1; i>=0; i--)
xy2level[i] = (i%GRIDMAX)+(i/GRIDMAX);
for (i=0; i<GRIDMAX*2; i++) {
if (i<GRIDMAX) {
level2xy[i] = i+GRIDMAX-1;
} else {
level2xy[i] = GRIDMAX*GRIDMAX-(GRIDMAX*2-3-i)*GRIDMAX-2;
}
}
 
/* create an initial grid */
d = (node*) calloc(1, sizeof(node));
for (y=GRIDMAX-1; y>=0; y--)
for (x=GRIDMAX-1; x>=0; x--) {
d->grid[x+y*GRIDMAX] = STAR;
d->attr[x+y*GRIDMAX] = BORDER;
}
for (y=GRIDMAX-2; y>0; y--)
for (x=GRIDMAX-2; x>0; x--) {
d->grid[x+y*GRIDMAX] = FREE;
d->attr[x+y*GRIDMAX] = 0;
}
 
/* Place all the words for starters */
#if SYMMETRICAL
/* work from the middle out */
for (w=numword-1; w>=0; w--)
if (wlen[w]>=5) {
d->hash = 0;
place_hword (d, (GRIDMAX/2+2-wlen[w])+(GRIDMAX/2)*GRIDMAX, w);
}
#else
/* work from top-left to bottom-right */
for (w=numword-1; w>=0; w--) {
d->firstlevel = 2;
place_hword (d, 0+1*GRIDMAX, w);
}
#endif
d->next = freenode;
freenode = d;
 
/* Here we go */
kick_ass();
dump_grid (&solution);
 
exit(0);
}