/*
 * This is scrambloyd, by Bill Tanenbaum.
 */

/*
 * This code is almost, but not quite identical with my entry.
 * It is even better.
 * It solves the third final square in 86 moves instead of 94.
 * On the other four final squares, it does the same as my entry.
 * This code was written before the finals.
 */
/*
 * NOTE: The conditionally compiled stuff inside #ifdef KNOTS or #ifdef FDIST
 * is needed only for very anomalous starting positions.  They are refinements
 * of the evaluation function in cases where it would otherwise be too low.
 * I have not commented any of this code.  On the five squares in the finals,
 * the program actually does better in total without this code.
 */

#include 
#include 

/* define some constants */

#define HASHBITS 12
#define HASH2 ((HASHBITS)+(HASHBITS))
#define HASH3 ((HASHBITS)+(HASH2))
#define HASHBINS (1 << HASHBITS)
#define HASHMASK ((HASHBINS) - 1)

/* Maximum number of turns to solve puzzle */
#define MMAX 150

/* Maximum allowed value of evaluation function */
#define DMAX 290

/* defines square in grid as function of row and column */
#define CPOS(a, b) ((5*(a))+(b))

#define DIST(a) ((a)->FDist)


/* Defines structure indicating the current contents of one grid square. */
typedef struct {
	char x; /* Destination row of letter. Range 0-4 */
	char y; /* Destination column of letter. Range 0-4 */
	char dist; /* Minimum possible no. of moves to reach destination,
		    * including possible transport move from center to
		    * destination, if that helps. Range 0-5.
		    * Zero means letter is at its destination.
		    */
} INFO;

/* Possible moves */
typedef enum {
	D,
	U,
	L,
	R,
	T,
	NMOVES,
} MOVES;

/* Inverses of the moves ( yes, I know T is not its own inverse) */
MOVES Inverse[NMOVES] = { U, D, R, L, T};

/* Structure defining a configuration (i.e. "position") for the entire grid,
 * plus the past move history.  For performance reasons, there is some
 * redundant information also stored.
 */
typedef struct {
	INFO pos[25];	/* Defines the contents of all 25 squares
			 * and the individual distances from
			 * the final destinations.
			 */
#ifdef KNOTS
	char knotx[5];
	char knoty[5];
	short Kdist;
#endif
	short Dist; /* Sum of distances from destination of all 24 letters */
	short FDist; /* Dist + 2 * Adist + Bdist (weighted distance) */
		     /* This is the return value of the evaluation
		      * function.  The weighting gives an incentive
		      * to complete the top and left letters first.  This
		      * gives better results than without.
		      */
	short Adist; /* Sum of distances from destination of letters
		      * destined for first row or column, (ABCDEFKPU)
		      * Get these letters in place early!
		      */
	short Bdist; /* Sum of distances from destination of letters
		      * destined for second row or column, (GHIJLQV)
		      * Get these letters in place after the ones
		      * in Adist!  (The letters MNORSTWX can wait.)
		      */
	short tturns; /* Total no. of transport moves so far. */
#ifdef FDIST
	short distmax;
#endif
#ifndef DUPS
	unsigned short hashval; /* Hash value of pos[25] array.
				 * Used for eliminating duplicate positions.
				 */
#endif
	char parity; /* 0 (1) = final pos can(not) be reached without t move */
	char prev;	/* Previous move. (enum) D, U, L, R, T */
	char X;		/* Row of empty square.  0-4 */
	char Y;		/* Column of empty square. 0-4 */
	char Moves[NMOVES]; /* entries for DULRT. 1 = possible next move,
			     * 0 = illegal move or reverses last move.
			     */
	char moves[MMAX]; /* character string - move history to reach pos. */
} POSIT;

/* GLOBAL VARIABLES BEGIN HERE */

POSIT *Pos;	/* Pointer to array of positions before current turn */ 
POSIT *Npos; 	/* Pointer to array of positions after current turn */

int Best; /* minimum value of eval. function for current positions. */
int Turns; /* Number of moves to get to each current position from init. pos. */
int Nn;	/* misc. counter for number of positions */
int maxdepth; /* give up if can't solve in this number of moves.
		* Defaults to 149 unless overridden from command line.
		*/
int maxwidth = 16000;	/* Maximum number of positions to keep at
			 * any given turn.  Running time is approx.
			 * linear in this number.  Reducing this number
			 * speeds things up, but may give less optimal
			 * solution.  The default of 16000 can be overridden
			 * from command line.  
			 */
int halfwidth; 	/* = maxwidth/2 */
int doublewidth; /* = maxwidth * 2 */

void read_grid();
void elimdups();
int moveit();
void dumpit();
void donext();
char getdist();
char gdist();

int  Dists[DMAX]; /* Array that gives the number of current positions that
	* have the subscript as the value of the evaluation function.
	* If Dists[67] = 25, then 25 current positions have an evaluation
	* function of 67.
	*/
#ifndef DUPS
unsigned short hash();

/* These arrays are used to eliminate duplicate positions. */
int  Counts[HASHBINS];
int  count[HASHBINS];
POSIT **Ptrs[HASHBINS];

#ifdef DEBUG
int dupselim;
#endif
#endif

/* FInally, the main program! */
main(ac, av)
int ac;
char *av[];
{
	maxdepth = MMAX - 1; /* default for maxdepth */
	/* possibly override defaults of maxdepth and maxwidth from cmd line */
	if (ac > 2) {
		maxwidth = atoi(av[2]);
		halfwidth = maxwidth/2;
		doublewidth = maxwidth*2;
		if (ac > 3) {
			maxdepth = atoi(av[3]);
		}
	}
	/* Allocate a position, and zero it out */
	Pos = (POSIT *)calloc(1, sizeof(POSIT));

	/* Read the input grid, filling in most of Pos. */
	read_grid(av[1], Pos);
	/* Fill in parity */
	Pos->parity = parity(Pos);
	/* Since there is only one initial position, don't
	 * bother with weighting.
	 */
	Pos->FDist = Pos->Dist;
#ifdef KNOTS
	Pos->FDist += Pos->Kdist; 
#endif
	Best = DIST(Pos); /* Evaluation function of initial position */
	Dists[Best] = 1;
	if (Best == 0) {
		/* Just in case initial position was final position! */
		printf("\n");
		exit(0);
	}
	Nn = 1; /* We have only one initial position. */

	/* Now loop over turns, one at a time. */
	for (Turns = 0; Turns <= maxdepth; Turns++) {
		/* From the current (old) positions (Pos), determine
		 * the (new) positions after one more turn (Npos).
		 * (If the final position is reached, donext()
		 * does not return).
		 */
		donext(Pos, &Npos);
		/* Discard old positions. */
		free(Pos);
		/* New positions are now old positions */
		Pos = Npos;
	}
	/* If we get here, we failed to find a solution */
	exit(1);
}

/* Get possible new positions from old positions */
void
donext(pi, pp)
POSIT *pi;	/* old positions */
POSIT **pp;	/* New positions */
{
	int i, k;
	int n = Nn; /* Save number of old positions */
	int best; 
	int leeway;
	int ttbest = Turns + 2;
	POSIT *pj;
	POSIT *ttj;

	Nn = 0;	/* Initialize counter of positions to keep. */
	/* Start at minimum evaluation function, and count
	 * old positions (which have already been evaluated)
	 * until either maxwidth positions are encountered,
	 * or we run out of positions.
	 */
	for (i = Best; i < DMAX; i++) {
		int d = Dists[i];
		if (d) { 
			int lee = i - Best;
			if ((Nn + d) > maxwidth) {
				/* 
				 * This value (i) for the evaluation
				 * function gives us too many (>maxwidth)
				 * positions.  Except for unusual
				 * cases, break out and stop at i-1.
				 */
				/* If we already have > maxwidth/2,
				 * break out if the evaluation function
				 * is more than 2 above its best value.
				 */
				if (Nn > halfwidth && lee > 2) break;

				/* Don't go over maxwidth * 2, except
				 * if there are no positions if we don't.
				 */
				if ((Nn + d) > doublewidth && Nn > 0) break;
			}
			Nn += d;
			leeway = lee;
		}
	}

	/* Set eval. function cutoff for keeping old positions. */
	best = Best + leeway;
#ifdef DEBUG
	printf("%d %d %d %d %d\n", Best, Best+Turns, n, Nn, leeway);
#endif
	/* Eliminate duplicate positions, and also positions above cutoff */
	/* The positions to be eliminated are tagged with FDist = -1 */
	/* Also, the possible Moves for each old position are filled in,
	 * eliminating the inverse of the previous move.
	 */
	elimdups(pi, n, Nn, best);

	/* Malloc enough space for new positions.
	 * Except for the very first move, there is a maximum of
	 * four (not five) possible moves.  After d, u, l, or r,
	 * we do not allow the inverse move.  Also, after t,
	 * another t is impossible because the center square is empty.
	 */
	Nn++;
	Nn *= 4; 
	pj = (POSIT *)malloc(Nn*sizeof(POSIT));
	if (pj == 0) {
		printf("malloc fails %d\n", Nn);
		exit(1);
	}
	*pp = pj;
	/* zero Dists array */
	memset((char *)Dists, 0, sizeof(Dists));

	/* Initialize Best to a large value */
	Best = MMAX;

	/* Now we loop over the old positions,
	 * determining the possible new positions after one more move.
	 */
	for (i = 0; i < n; pi++, i++) {
		/* Ignore old positions that were discarded,
		 * either as duplicates, or because there were
		 * too many positions.  All these have
		 * been tagged with FDist = -1.
		 */
		if (pi->FDist >= 0) {
			/* Loop over the five moves (D U L R T) */
			for (k = 0; k < 5; k++) {
				int dist;
				/* Skip move that reverses last one */
				if (!pi->Moves[k]) continue;

				/* moveit() attempts to make the move.
				 * If impossible, moveit() returns 0.
				 * Otherwise, pj points to the new position.
				 * Note that if the move was impossible,
				 * the pointer pj is not incremented.
				 */
				if (moveit(pi, pj, k)) {
					/* Move was made */
					/* Eval. function */
					dist = DIST(pj);
					/* Increment appropriate
					 * member of Dists array.
					 */
					if (dist < DMAX)
						Dists[dist]++;
					/* Is this the best, so far? */
					if (dist < Best) Best = dist;
					/* Have we reached perfection? */
					if (dist == 0) {
						/*
						 * Yes, but we may reach the
						 * same pos. by multiple
						 * paths. Pick the path
						 * with fewest t moves.
						 */
						if (pj->tturns < ttbest) {
							ttbest = pj->tturns;
							ttj = pj;
						}
					}
					pj++;
				}
			}
		}
	}
	Nn = pj - *pp; /* This is the number of new positions */
	if (Best <= 0) {
		/* If we reached the final position,
		 * print out the move sequence and exit.
		 */
		dumpit(ttj);
		exit(0);
	}
}

void
elimdups(pb, n, nn, best)
POSIT *pb;	/* positions */
int n;		/* Number of positions */
int nn;		/* Approx number of positions to keep */
int best;	/* Max value of eval. function of position to keep */
{
	int i, j, k;
	POSIT *p;
	POSIT *pe;

	if (n == 1) return;
	pe = pb + n;
#ifndef DUPS
	memset((char *)Counts, 0, sizeof(Counts));
#endif
	/* Loop over positions */
	for (p = pb; p < pe; p++) {
		/* If the eval. function is above cutoff, tag for elim. */
		if (DIST(p) > best) {
			p->FDist = -1;
		} else {
			/* Below cutoff.  Keep unless duplicate */
#ifndef DUPS
			/* Save hash value over position */
			p->hashval = hash(p->pos);
			Counts[p->hashval]++;
#endif
			/* Disallow move that is inverse of previous move. */
			p->Moves[D] = p->Moves[U] = p->Moves[L] = p->Moves[R] =
				p->Moves[T] = 1; 
			p->Moves[Inverse[p->prev]] = 0;

			/* If the position is symmetric around the
			 * main diagonal, we can disallow U and D moves
			 * as equivalent to L and R respectively.
			 */
			if (symmet(p->pos)) {
				p->Moves[U] = p->Moves[D] = 0;
				switch (p->prev) {
				case D:
					p->Moves[L] = 0;
					break;
				case U:
					p->Moves[R] = 0;
					break;
				}
			}
		}
	}

	/* The remaining code in this function tags duplicate
	 * positions for elimination.
	 */
#ifndef DUPS
	/* Malloc some space, and set up some counters and pointers. */
	Ptrs[0] = (POSIT **)malloc(nn*sizeof(POSIT *));
	for (i = 1; i < HASHBINS; i++) {
		Ptrs[i] = Ptrs[i-1] + Counts[i-1];
	}
	memset((char *)count, 0, sizeof(count));
	/* Loop over positions, skipping those already tagged for elim. */
	for (p = pb; p < pe; p++) {
		if (p->FDist < 0) continue;
		k = p->hashval;
		Ptrs[k][count[k]++] = p;
		if (count[k] > Counts[k]) {
			printf("WHOOPS\n");
			exit();
		}
	}
#ifdef DEBUG
	dupselim = 0;
#endif
	/* Loop over hashbins */
	for (k = 0; k < HASHBINS; k++) {
		POSIT **pp = Ptrs[k];

		/* Loop over positions in this hashbin */
		for (i = 0; i < count[k]; i++) {
			POSIT *pi = pp[i];

			/* skip position if tagged for elim. */
			if (pi->FDist < 0) continue;

			/* Second loop over positions in this hashbin */
			for (j = 0; j < i; j++) {
				POSIT *pj = pp[j];
				int l;

				/* skip position if tagged for elim. */
				if (pj->FDist < 0) continue;

				/* Check if positions are the same. */
				for (l = 0; l < 25; l++) {
					if (pi->pos[l].x != pj->pos[l].x) goto skip;
					if (pi->pos[l].y != pj->pos[l].y) goto skip;
				}
				{
					/* Both positions are the same.
					 * Tag the one with the most
					 * t moves in its history for
					 * elimination.
					 */
					POSIT *pd, *pk;
					if (pi->tturns < pj->tturns) {
						pd = pj;
						pk = pi;
					} else {
						pd = pi;
						pk = pj;
					}
					pd->FDist = -1;
					/* The moves that were disallowed for
					 * EITHER position can be disallowed.
					 */
					pk->Moves[D] = pj->Moves[D] && pi->Moves[D];
					pk->Moves[U] = pj->Moves[U] && pi->Moves[U];
					pk->Moves[L] = pj->Moves[L] && pi->Moves[L];
					pk->Moves[R] = pj->Moves[R] && pi->Moves[R];
					pk->Moves[T] = pj->Moves[T] && pi->Moves[T];
#ifdef DEBUG
					dupselim++;
#endif
				}
skip:				continue;
			}
		}
	}
#ifdef DEBUG
	printf("dups %d out of %d\n", dupselim, nn);
#endif
#endif
}

/* Determines if position has symmetry around the main diagonal. */
int symmet(pos)
INFO pos[25];
{
	int i, j;
	/* Don't have to check diagonal elements! */
	for (i = 0; i < 5; i++) {
		for (j = 0; j < i; j++) {
			INFO *p1 = &pos[CPOS(i, j)];
			INFO *p2 = &pos[CPOS(j, i)];
			if (p1->x != p2->y) return(0);
			if (p2->x != p1->y) return(0);
		}
	}
	return(1);
}

/* Reads initial grid and fills in most of the initial position. */
void
read_grid(filename, p)
char *filename;
POSIT *p;
{
	register int i, j, k;
	int fp;
	char buffer[29];

	fp = open(filename, O_RDONLY);
	(void)read(fp, buffer, 29);
	for (i = 0; i < 5; i++) {
		for (j = 0; j < 5; j++) {
			int x,y;
			INFO *ip = &p->pos[CPOS(i,j)];

			char c = buffer[6*i + j];
			if (c == '+') {
				p->X = i;
				p->Y = j;
			} else {
				k = c - 'A';
				x = k/5;
				y = k%5;
				ip->x = x;
				ip->y = y;
				ip->dist = getdist(i, j, x, y);
				p->Dist += ip->dist;
				if (x*y == 0) p->Adist += ip->dist;
				else if ((x-1)*(y-1) == 0) p->Bdist += ip->dist;
			}
		}
	}
#ifdef KNOTS
	for (i = 0; i < 5; i++) {
		p->knotx[i] = get_knots(p, i, 0);
		p->knoty[i] = get_knots(p, i, 1);
		p->Kdist += (p->knotx[i] + p->knoty[i]);
	}
#endif
#ifdef FDIST
	p->distmax = 55;
#endif
	for (i = 0; i < NMOVES; i++) {
		p->Moves[i] = 1;
	}
	if (symmet(p->pos)) {
		p->Moves[D] = p->Moves[U] = 0;
	}
}

void dumpit(p)
POSIT *p;
{
#ifdef DEBUG
/*
	register int i, j;
	printf("\n");
	for (i = 0; i < 5; i++) {
		for (j = 0; j < 5; j++) {
			int c;
			if (i == p[0].X && j == p[0].Y) {
				c = '+';
			} else {
			c = 'A' + 5*(int)p[0].pos[CPOS(i, j)].x + (int)p[0].pos[CPOS(i, j)].y;
			}
			printf("%c", c);
		}
		printf("\n");
	}
*/
	printf("trans= %d\n", (int)p[0].tturns);
#endif
	/* Print out move sequence. */
	printf("%s\n", p[0].moves);
}

/* gets the minimum number of times the letter must be moved. */
char getdist(i, j, x, y)
char i, j, x, y;
{
	/* i, j, is current position.  x, y is destination. */
	char d1, d2;

	/* Try it without 't' move */
	d1 = gdist(i, j, x, y);

	/* Try it with final 't' move from center */
	d2 = gdist(i, j, (char)2, (char)2) + 1;

	/* Pick the better of the two */
	if (d2 >= d1) return(d1);
	else return(d2);
}

/* gets the minimum number of times the letter must be moved
 with 't' moves disallowed */
char gdist(i, j, x, y)
char i, j, x, y;
{
	/* i, j, is current position.  x, y is destination. */
	char d1, d2;
	d1 = (i >= x ? i-x : x-i);
	d2 = (j >= y ? j-y : y-j);
	return(d1 + d2);
}

/* makes a move */
/* return 0 if move not made, 1 if made. */
int
moveit(p1, p2, m)
POSIT *p1; /* old position (input) */
POSIT *p2; /* new mosition (output) */
int m; 	   /* move D, U, L, R, or T */
{
	INFO *ip1;	/* ptr to pos[] of letter to be moved */
	INFO *ip2;	/* ptr to pos[] of (empty) square letter moved to */
	char c;
	int n;

#define SIZE (sizeof(POSIT) + Turns - MMAX)
	switch(m) {
	case R:
		/* Impossible if blank in column 0 */
		if (p1->Y == 0) goto bad;

		/* Pos[] of letter to be moved */
		ip1 = &p1->pos[CPOS(p1->X, p1->Y-1)];

		/* If letter to be moved R is in its final dest. square
		 * in the leftmost column, disallow the move.
		 * Also, if the letter to be moved R is in its final dest.
		 * square in the second column from the left, and
		 * the first row and column are completely done,
		 * disallow the move.
		 */
		if (ip1->dist == 0) {
			if (p1->Y == 1) goto bad;
			if (p1->Adist == 0 && p1->Y == 2) goto bad;
		}
		/* Copy position from old to new */
		memcpy(p2, p1, SIZE);

		/* Pos[] of letter that was moved */
		ip2 = &p2->pos[CPOS(p2->X, p2->Y)];

		/* Adjust position of blank */
		p2->Y--;
		c = 'r';
		break;
	case L:
		/* Impossible if blank in column 4 */
		if (p1->Y == 4) goto bad;

		/* Pos[] of letter to be moved */
		ip1 = &p1->pos[CPOS(p1->X, p1->Y+1)];

		/* Copy position from old to new */
		memcpy(p2, p1, SIZE);

		/* Pos[] of letter that was moved */
		ip2 = &p2->pos[CPOS(p2->X, p2->Y)];

		/* Adjust position of blank */
		p2->Y++;
		c = 'l';
		break;
	case U:
		/* Impossible if blank in row 4 */
		if (p1->X == 4) goto bad;

		/* Pos[] of letter to be moved */
		ip1 = &p1->pos[CPOS(p1->X+1, p1->Y)];

		/* Copy position from old to new */
		memcpy(p2, p1, SIZE);

		/* Pos[] of letter that was moved */
		ip2 = &p2->pos[CPOS(p2->X, p2->Y)];

		/* Adjust position of blank */
		p2->X++;
		c = 'u';
		break;
	case D:
		/* Impossible if blank in row 0 */
		if (p1->X == 0) goto bad;

		/* Pos[] of letter to be moved */
		ip1 = &p1->pos[CPOS(p1->X-1, p1->Y)];

		/* If letter to be moved D is in its final dest. square
		 * in the top row , disallow the move.
		 * Also, if the letter to be moved D is in its final dest.
		 * square in the second row from the top, and
		 * the first row and column are completely done,
		 * disallow the move.
		 */
		if (ip1->dist == 0) {
			if (p1->X == 1) goto bad;
			if (p1->Adist == 0 && p1->X == 2) goto bad;
		}
		/* Copy position from old to new */
		memcpy(p2, p1, SIZE);

		/* Pos[] of letter that was moved */
		ip2 = &p2->pos[CPOS(p2->X, p2->Y)];

		/* Adjust position of blank */
		p2->X--;
		c = 'd';
		break;
	case T:
		/* Impossible if blank in center square */
		/* Disallowed if blank is adjacent to center square
		 * (equivalent to D, U, L or R in that case)
		 */
		if (p1->X == 2 && p1->Y >= 1 && p1->Y <= 3) goto bad;
		if (p1->Y == 2 && p1->X >= 1 && p1->X <= 3) goto bad;

		/* Pos[] of letter to be moved */
		ip1 = &p1->pos[CPOS(2, 2)];

		/* Copy position from old to new */
		memcpy(p2, p1, SIZE);

		/* Pos[] of letter that was moved */
		ip2 = &p2->pos[CPOS(p2->X, p2->Y)];
		/* parity may change */
		if (!((p2->X + p2->Y) & 1)) p2->parity = 1 - p2->parity;

		/* Adjust position of blank (to center square) */
		p2->X = (char)2;
		p2->Y = (char)2;
		p2->tturns++; /* increment no. of 't' moves */
		c = 't';
		break;
	}
	/* Copy moved letter from old location to new location */
	*ip2 = *ip1;

	/* save last move */
	p2->prev = m;

	/* New distance of moved letter */
	ip2->dist = getdist(p1->X, p1->Y, ip2->x, ip2->y);

	/* Adjust total distances */
	p2->Dist += (ip2->dist - ip1->dist);
	if (ip1->x == 0 || ip1->y == 0) p2->Adist += (ip2->dist - ip1->dist);
	else if (ip1->x == 1 || ip1->y == 1) p2->Bdist += (ip2->dist - ip1->dist);
	{
		/* Distance of blank always set to zero */
		INFO *p = &p2->pos[CPOS(p2->X, p2->Y)];
		p->dist = 0;
		/* Destination of blank is lower right corner */
		p->x = p->y = 4;
	}
#ifdef KNOTS
	switch(m) {
	case R:
	case L:
		p2->knotx[p1->Y] = get_knots(p2, p1->Y, 0);
		p2->knotx[p2->Y] = get_knots(p2, p2->Y, 0);
		p2->Kdist += (p2->knotx[p1->Y] - p1->knotx[p1->Y]);
		p2->Kdist += (p2->knotx[p2->Y] - p1->knotx[p2->Y]);
		break;
	case U:
	case D:
		p2->knoty[p1->X] = get_knots(p2, p1->X, 1);
		p2->knoty[p2->X] = get_knots(p2, p2->X, 1);
		p2->Kdist += (p2->knoty[p1->X] - p1->knoty[p1->X]);
		p2->Kdist += (p2->knoty[p2->X] - p1->knoty[p2->X]);
		break;
	case T:
		p2->knoty[p1->X] = get_knots(p2, p1->X, 1);
		p2->knotx[p1->Y] = get_knots(p2, p1->Y, 0);
		p2->Kdist += (p2->knoty[p1->X] - p1->knoty[p1->X]);
		p2->Kdist += (p2->knotx[p1->Y] - p1->knotx[p1->Y]);
		break;
	}
#endif
	/* Set Evaluation function to total distance, for now */
	p2->FDist = p2->Dist;
#ifdef KNOTS
	p2->FDist += p2->Kdist;
#endif
	/* Positions with evaluation function less than 7 and the wrong
	 * parity must have their evaluation function raised.
	 */
	if (p2->parity && p2->FDist < 7) {
		if (p2->X == 2 && p2->Y == 2) p2->FDist = 7;
		if (p2->FDist < 6) {
			if ((p2->X + p2->Y) & 1) p2->FDist = 6;
		}
		if (p2->FDist < 5) p2->FDist = 5;
	}
#ifdef FDIST
	if (p2->FDist <= p2->distmax && p2->FDist > 1) {
		int i, j, d;
		int dist = 0;
		int dst[6];
		dst[0] = dst[1] = dst[2] = dst[3] = dst[4] = dst[5] = 0;
		for (i = 0; i < 5; i++) {
			for (j = 0; j < 5; j++) {
				d = p2->pos[CPOS(i, j)].dist;
				dst[d]++;
				if (d > 1) dist = 4;
			}
		}
		dst[4] += dst[5];
		dst[3] += dst[4];
		for (i = 0; i < 8; i++) {
			if (i < dst[5]) {
				dist += (12 - i);
			} else if (i < dst[4]) {
				dist += (8 - i);
			} else if (i < dst[3] && i < 4) {
				dist += (4 - i);
			} else {
				break;
			}
		}
		p2->distmax = dist;
		if (p2->FDist < dist) {
			p2->FDist = dist;
		}
	}
#endif
	/* Add weightings to eval. function. */
	p2->FDist += p2->Bdist;
	p2->FDist += 2 * p2->Adist;

	/* Add last move to move history */
	n = strlen(p1->moves);
	p2->moves[n] = c;
	p2->moves[n+1] = 0;

	return(1);
bad:
	/* impossible or disallowed move */
	return(0);
}

/*
 * Calculates parity of position.
 */
int
parity(p)
POSIT *p;
{
	int i, j;
	int parity = 0;
	INFO info;
	POSIT *pp;

	pp = (POSIT *)calloc(1, sizeof(POSIT));
	*pp = *p;
	pp->pos[CPOS(pp->X, pp->Y)].x = 4;
	pp->pos[CPOS(pp->X, pp->Y)].y = 4;
	for (i = 4; i >= 0; i--) {
		for (j = 4; j >= 0; j--) {
			INFO *ip;
			INFO *ip2;
again:
			ip = &pp->pos[CPOS(i, j)];
			ip2 = &pp->pos[CPOS(ip->x, ip->y)];
			if (ip2 == ip) {
				continue;
			}
			if (i == 4 && j == 4 && ip2->x == 4 && ip2->y == 4) {
				if (!((ip->x + ip->y) & 1)) parity = 1 - parity;
			} else {
				parity = 1 - parity;
			}
			info = *ip;
			*ip = *ip2;
			*ip2 = info;
			goto again;
		}
	}
	free(pp);
	return(parity);
}

#ifdef KNOTS
int get_knots(p, i, y)
POSIT *p;
int i;
int y;
{
	int j, k, n;
	INFO *ip;

	short arr[5];
	if (i == 2) return(0);
	if (y) {
		for (j = k = 0; j < 5; j++) {
			ip = &p->pos[CPOS(i, j)];
			if (ip->x == i) {
				if (i != 4 || ip->y != 4) arr[k++] = ip->y;
			}
		}
	} else {
		for (j = k = 0; j < 5; j++) {
			ip = &p->pos[CPOS(j, i)];
			if (ip->y == i) {
				if (i != 4 || ip->x != 4) arr[k++] = ip->x;
			}
		}
	}
	n = knots (arr, k);
	if (i == 0 || i == 4) n *= 2;
	return(n);
}

int
knots(arr, n)
short arr[5];
short n;
{
	int i, j, k, ret, best;
	short narr[5];

	if (n <= 1) return(0);
	best = 0;
	for (i = 0; i < n; i++) {
		int ai = arr[i];
		for (j = 0; j < i; j++) {
			if (ai < arr[j]) best++;
		}
	}
	if (best <= 1) return(best);
	for (i = 0; i < n; i++) {
		for (j = k = 0; j < n; j++) {
			if (j == i) continue;
			narr[k] = arr[j];
			k++;
		}
		ret = knots(narr, n - 1) + 1;
		if (ret < best) best = ret;
	}
	return(best);
}
#endif

#ifndef DUPS
unsigned
short
hash(pos)
INFO *pos;
{
	unsigned short c;
	INFO *ip;
	INFO *ppos = pos + 25;
	unsigned int h = 0;
	unsigned int hh;
	for (ip = pos; ip < ppos; ip++) {
		h += ip->x; 
		h += (ip->y << 3); 
		h *= 5381;
	}
	hh = (h) ^ (h >>HASHBITS) ^ (h >>(HASH2)) ^ (h >>HASH3);
	c = (hh & HASHMASK);
	return(c);
}
#endif DUPS
Make your own free website on Tripod.com