/*	Building a Better Boggle Copyright 1993 by Phil Goetz
	goetz@cse.buffalo.EDU
	philgoetz@yahoo.com
	flick@populus.net
	Version 3: Genetic algorithm, sexual, dictionary, survivors.
	Changes to make:
		Add cba to abc; only check abc.
		Make tri turn qu -> q

        Note from gtoal@gtoal.com: Now modified to use an
        internal boggle word finder and scoring - runs approx 10 times
        faster.

        Also hacked one small library incompatibility (log2/ilog2)

*/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define AUTO		2       /* Chance out of 64 that X mates with X */
#define BIRTHS		200
#define BS		(BIRTHS+SURVIVORS)
#define DEBUG		(1+8+16+32)
/*#define DEBUG		(1+4+16+32+64+128+1024)*/
	/* 1	Print gens & ave hiscore		*/
	/* 2	Print each reproducing board's info about 1/16 the time */
	/* 4	Print each reproducing board's info	*/
	/* 8    Use raster chromosome			*/
	/* 16	Print top score				*/
	/* 32	printboard(winner)			*/
/* 64, 128, 256, 512, and 1024 are enabled by 2 or 4	*/
	/* 64	printboard				*/
	/* 128	print score + # kids			*/
	/* 256	print gen 0 ancestors			*/
	/* 512	print ancestry				*/
	/* 1024	print summary of gen 0 ancestors	*/
#define	DICE	(SIDE*SIDE)
#define GENS		30	/* generations				*/
#define MUTRATE		2	/* average # of mutations per descendant */
#define SIDE		4
#define STATES		6
#define SURVIVORS	(BIRTHS/8)
#define TICKETS		10000.	/* Total # of lottery tickets given out	*/

/* Global variables:	*/

static
char	chdice[DICE][STATES] = {"aeiouy", "aeiouy", "aeiouy", "aeiouy",
			"aeioue", "aeioae", "aeioae", "tdbtgp",
			"tdgtdp", "tdgtdb", "tdbtpg", "rlhmnk",
			"rlhmnk", "cswfhs", "cswfhs", "qxzjkv"};
			/* chdice[0][5] = face 5 of dice 0		*/
static
double	score[BS];

static
int	board[BS][DICE], /* board[][2]=3 => face 3 of dice 2 is up	*/
	dice[DICE][STATES],  /* Permuted integer version of chdice: A = 0 */
#if DEBUG & 1024
	firstgen[BIRTHS],	/* Track # of each 1st-gen ancestors	*/
#endif
	new[BIRTHS][DICE];

static
struct ancestors
	{	int	name;
		struct ancestors *mom, *dad;
	}
	*ancestorses[BS],
	*newanc[BIRTHS];

#ifndef HAS_LOG2
/* I hope this is right.  It's a quick hack because bsd doesn't
   seem to have the same math funs as the author's system - Graham Toal */
static int ilog2(long l)  /* rounds down */
{
  int i;

  if (l <= 0L) return 0; /* Shouldn't be needed */
  i = 0;
  for (;;) {
    if (l == 0) return(i-1);
    l >>= 1;
    i += 1;
  }
}
#endif


extern int bogglescore(char *rawboard);

static void printboard(int b,	        /* board #			*/
                       int nice);	/* 1 = print in 2D format	*/

static double calcscore(int b) /* board #	*/
{
  static int bestscore = -1;
  char	call[17];
  int	i, j, bscore;
  call[16] = '\0';

  for (i=0; i<SIDE; i++)
  { for (j=0; j<SIDE; j++)
	call[i*SIDE+j] =
		((char) dice[i*SIDE+j][board[b][i*SIDE+j]]) + 'a';
  }

  bscore = bogglescore(call);
  if (bscore > bestscore) {
    bestscore = bscore;
    printboard(b, 0); fprintf(stdout, " -> %d\n", bscore);
  }

  return (double) (bscore-16);  /* Why -16 ??? - gtoal */
}

static void init(void)
{
	int	b,i,j, perm[DICE], r;
	
	/* Randomly permute the dice			*/
	/* Make perm a random permutation of 0..DICE-1	*/
	for (i = 0; i < DICE; i++)
	{   perm[i] = -1;
	}
	i = 0;
	do
	{   j = (rand() & 65535) / (65536/DICE);	/* 0 to (DICE-1) */
	    if (perm[j] == -1) perm[j] = i++;
	}
	while (i < DICE);
	for (i = 0; i < DICE; i++)
	    for (j=0; j<STATES; j++)
	        dice[i][j] = (int) (chdice[perm[i]][j] - 'a');
	for (b=0; b<BIRTHS; b++)
	{   for (i=0; i<DICE; i++)
	    {   do
	            r = (rand() & 65535) >> 13;	/* 0 - 7 */
	        while (r > 5);
	        new[b][i] = r;
	    }
	    newanc[b] = (struct ancestors *) malloc(sizeof(struct ancestors));
	    newanc[b]->mom = NULL;
	    newanc[b]->dad = NULL;
	    newanc[b]->name = b;
	}
	for (b=BIRTHS; b<BS; b++)	/* Prevent blank survivor spots */
	    score[b] = .0;		/* from having kids in gen 0 */
}

static void printboard(int b,	        /* board #			*/
                       int nice)	/* 1 = print in 2D format	*/
{
	int	die, i, j;

/*
	if (!nice) printf("\n");
*/
	for (j = 0; j < SIDE; j++)	/* Row */
	{   for (i=0; i<SIDE; i++)
	    {	die = i + SIDE*j;	/* Row major	*/
		printf("%c", (char) dice[die][board[b][die]]+'a');
	    }
	    if (nice) printf("\n"); else if (j+1<SIDE) printf(" ");
	}
}

/* Create new[i] as offspring of board[mom] and board[dad]	*/
static void birth(int dad, int mom, int kid)
{	int
#if DEBUG & 8
		chromo[] ={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
#else
		chromo[] ={4,0,1,5,6,2,3,7,11,15,14,10,9,13,12,8},
#endif
		j, parent,
		r, xpoint;
	struct ancestors *anc;

	anc = (struct ancestors *) malloc(sizeof(struct ancestors));
	anc->mom = ancestorses[mom];
	anc->dad = ancestorses[dad];
	anc->name = kid;
	newanc[kid] = anc;
	parent = dad;
	xpoint = (rand() & 65535) / (65536/DICE); /* 0..(DICE-1) */
	for (j=0; j<DICE; j++)
	{   if (j==xpoint) parent = mom;
	    r = (rand() & 65535) / (65536/(4*DICE)); /* 0..(4*DICE-1) */
	    if (r < (int) (4. * (double) MUTRATE))	/* If a mutation */
	    {   r = (rand() & 65535) / (65536/STATES);
		new[kid][chromo[j]] = r;
	    }
	    else new[kid][chromo[j]] = board[parent][chromo[j]];
        }
}

static void ancestry(struct ancestors *b)
{
	if (b->dad == NULL)
#if DEBUG & 1024
	    firstgen[b->name]++;
#else
	    {printf("%d", b->name); fflush(stdout);}
#endif
	else
	{
#if DEBUG & 512
		printf("%d (", b->name); fflush(stdout);
#endif
		ancestry(b->dad);
#if (!(DEBUG & 1024))
		printf(" "); fflush(stdout);
#endif
		ancestry(b->mom);
#if DEBUG & 512
		printf(")"); fflush(stdout);
#endif
	}
}

static int survivor(int b)	/* Returns 1 if board b is identical to one in survivor list */
{	int	i, j;
	for (i=BIRTHS; i<BS; i++)
	{	j=0;
		while (dice[j][board[b][j]] == dice[j][board[i][j]] && j < DICE)
		{	j++;	}
		if (j == DICE) return 1;
	}
	return 0;
}

static void insert(int b)	/* Insert board i into survivor list, ranked by score	*/
/* Assumes score[b] > score[BS-1]			*/
{	int	i, spot;

	spot = BS-1;
	while (score[b] > score[spot-1] && spot > BIRTHS)
	/* Move old survivors down */
	{	ancestorses[spot] = ancestorses[spot-1];
		for (i=0; i<DICE; i++)
			board[spot][i] = board[spot-1][i];
		score[spot] = score[--spot];
	}
	ancestorses[spot] = ancestorses[b];
	for (i=0; i<DICE; i++)
		board[spot][i] = board[b][i];
	score[spot] = score[b];
}

#include "splib.h"
extern NODE *dict;
extern INDEX nedges;

int main(int argc, char **argv)
{
	double	ave, avef,
		maxscore,
		sum,
		tscore[BS],
		tssum;
	int	i,j,k,r,
		descendants[BS],
		gens,	/* # of generations		*/
		holder,	/* ticket holder		*/
		maxind,
		oldholder,
		tickets[BS],
		tsum;	/* # of tickets issued		*/

/*   Boggle initialisation:  */
	if (!dawg_init("words", &dict, &nedges)) {
		fprintf(stderr, "Cannot open words.dwg\n");
		exit(1);
	}

	srand48(234987234);
	srand((unsigned) time(0));

	init();

	for (gens=0; gens<GENS; gens++)
	{
	    for (i=0; i<BIRTHS; i++)
	    {	for (j=0; j<DICE; j++)
		    board[i][j] = new[i][j];
		ancestorses[i] = newanc[i];
	    }
	    sum = .0;
	    for (i=0; i<BIRTHS; i++)
	    {   score[i] = calcscore(i);
		if (score[i] > maxscore)
		{   maxscore = score[i];
		    maxind = i;
		}
	        sum += score[i];
	    }
	    ave = sum/(double) BIRTHS;
	    tssum = .0;
	    avef = ave * 2./3.;
	    for (i=0; i<BS; i++)
	    {   if (score[i] > ave)
		    tscore[i] = (score[i]-avef)*(score[i]-avef);
		else tscore[i] = 0.;
		tssum += tscore[i];
		descendants[i] = 0;
	    }
	    tsum = 0;
	    for (i=0; i<BS; i++)
	    {	tickets[i] = (int) (TICKETS*tscore[i]/tssum + .5);
		tsum += tickets[i];
	    }
#if DEBUG & 1
	    printf("Generation %d: %d tickets issued, ave score %g\n",
		gens, (int) tsum, ave); fflush(stdout);
#endif
	    /* Pick (2*BIRTHS) lottery winners */
	    /* k = smallest 2^n > tsum		*/

#ifdef HAS_LOG2
	    k = ((int) log2((double)tsum)) + 1;
#else
	    k = ilog2((long)tsum) + 1;
#endif
	    for (i=0; i<BIRTHS; i++)
	    {	for (j=0; j<2; j++)	/* 2 parents	*/
		{   oldholder = holder;
		    do
		    { do
		      {   r = rand(); r &= (1<<k)-1;}
		      while (r > (tsum-1));
		      /* Holder of ticket r gets 1 descendant */
		      holder = -1;
		      while (r > -1)
		          r -= tickets[++holder];
		    }
		    while (holder == oldholder && ((rand() & 255) >> 4) > AUTO);
		    descendants[holder]++;	/* For our info	*/
		}
		birth(holder, oldholder, i);
	    }
#if (DEBUG & 2) || (DEBUG & 4)
	    j=0;
	    for (i=0; i<BS; i++)
#if (DEBUG & 4)
		if (descendants[i])
#else if (DEBUG & 2)
		if (descendants[i] && (((rand() & 255) >> 4) == 1))
#endif
		{   printf("#%2d ",i); fflush(stdout);
#if (DEBUG & 64)
		    printboard(i,0); fflush(stdout);
#endif
#if DEBUG & 128
		    printf(", sc %3g, d%3d",
			score[i], descendants[i] ); fflush(stdout);
#endif
#if DEBUG & (256+512+1024)
		    printf(","); fflush(stdout);
#if DEBUG & 1024
		    for (k=0; k<BIRTHS; k++)
			firstgen[k]=0;
#endif
		    ancestry(ancestorses[i]);
#if DEBUG & 1024
		    for (k=0; k<BIRTHS; k++)
			if (firstgen[k])
			    printf(" %dx%d",firstgen[k], k);
                    fflush(stdout);
#endif
#endif
		    printf("\n"); fflush(stdout);
		    j+= descendants[i];
		}
#endif
	/* Pick survivors */
	for (i=0; i<BIRTHS; i++)
		/* Lowest-scoring old survivor in BS-1 */
		if (score[i] > score[BS-1] && !survivor(i))
			insert(i);
	maxind = BIRTHS; maxscore = score[maxind];
	}

#if DEBUG & 16
	printf("Top score: %f  #%d\n",score[maxind], maxind); fflush(stdout);
#endif
#if DEBUG & 32
	printboard(maxind,0); printf("\n"); fflush(stdout);
#endif

/* Boggle termination: */

	free(dict); /* no special procedure to free these. */
	            /* Must be done once only, at end of program */

	exit(0);
}