/*
 * @(#)PanexS.c
 *
 * Taken from Some Results on the Panex Puzzle by Mark Manasse &
 * Danny Sleator of AT&T Bell Laboratories, and Victor K. Wei
 * of Bell Communications Research.
 * Thanks to Nick Baxter <nickb AT baxterweb.com> for debugging level n > 4
 * and vTrick.
 * Though most code by Rene Jansen <rene.j.jansen@bigfoot.com> is now
 * removed much inspiration was gained by his efforts implementing
 * an algorithm from Quantum January/February 1996 by Vladimir
 * Dubrovsky.
 * Break ability taken from the X puzzle by Don Bennett, HP Labs
 *
 * Copyright 1996 - 2008  David A. Bagley, bagleyd AT verizon.net
 *
 * All rights reserved.
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of the author not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * 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.
 */

/* Solver file for Panex */

#include "rngs.h"
#define JMP
#ifdef JMP
#include <setjmp.h> /* longjmp ... interrupt */
#endif
#include "PanexP.h"

static Boolean solvingFlag = False;
#ifdef JMP
static Boolean abortSolvingFlag = False;
static jmp_buf solve_env;

static void
abortSolving(void)
{
	if (solvingFlag)
		abortSolvingFlag = True;
}

#ifdef WINVER
static Boolean
processMessage(UINT msg)
{
	switch (msg) {
	case WM_KEYDOWN:
	case WM_CLOSE:
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
		abortSolving();
		return True;
	default:
		return False;
	}
}
#else
static void
processButton(void /*XButtonEvent *event*/)
{
	abortSolving();
}

static void
processVisibility(XVisibilityEvent *event)
{
	if (event->state != VisibilityUnobscured)
		abortSolving();
}

static void
getNextEvent(PanexWidget w, XEvent *event)
{
	if (!XCheckMaskEvent(XtDisplay(w), VisibilityChangeMask, event))
		(void) XNextEvent(XtDisplay(w), event);
}

static void
processEvent(XEvent *event)
{
	switch(event->type) {
	case KeyPress:
	case ButtonPress:
		processButton(/*&event->xbutton*/);
		break;
	case VisibilityNotify:
		processVisibility(&event->xvisibility);
		break;
	default:
		break;
	}
}

static void
processEvents(PanexWidget w)
{
	XEvent event;

	while (XPending(XtDisplay(w))) {
		getNextEvent(w, &event);
		processEvent(&event);
	}
}
#endif
#endif

#ifdef DEBUG
static int counter; /* debugging */
#endif

#if !defined(SIMPLE_ALG) && !defined(QUANTUM_ALG) && !defined(QUANTUM_JANSEN_ALG) && !defined(BAXTER_ALG) && !defined(ATT_ALG)
#if 0
#define SIMPLE_ALG
#define QUANTUM_ALG
#define QUANTUM_JANSEN_ALG
#define BAXTER_ALG
#endif
#define ATT_ALG
#endif

static Boolean
moveATile(PanexWidget w, int fromStack, int toStack)
{
	int fromPosition;

#ifdef JMP
#ifdef WINVER
	MSG msg;

	if (PeekMessage(&msg, NULL, 0, 0, 0)) {
		if (!processMessage(msg.message)) {
			if (GetMessage(&msg, NULL, 0, 0))
				DispatchMessage(&msg);
		}
	}
#else
	processEvents(w);
#endif
	if (solvingFlag && abortSolvingFlag)
		longjmp(solve_env, 1);
#endif
	if ((fromPosition = topOfStack(w, fromStack, 0)) < 0 ||
			movePuzzle(w, fromStack, fromPosition, toStack, DOUBLE) < 0) {
		(void) printf("move from %d to %d cannot be made.",
			fromStack, toStack);
		return False;
	}
	return True;
}

static void
solveHanoiMode(PanexWidget w)
{
	int triangleTurn;
	int triangleDirection;
	int triangleStack;
	int fromStack, toStack, temp1Stack, temp2Stack;
	int temp1Loc, temp2Loc;

	if (solvingFlag)
		return;
#ifdef JMP
	if (!setjmp(solve_env))
#endif
	{
		triangleTurn = TRUE;
		triangleDirection = (w->panex.tiles & 1) ? 2 : 1;
		triangleStack = 0;
		while (!checkSolved(w)) {
			solvingFlag = True;

			if (triangleTurn) {
				fromStack = triangleStack;
				toStack = (triangleStack + triangleDirection) %
					MAX_STACKS;
				triangleStack = toStack;
				triangleTurn = FALSE;
			} else {
				temp1Stack = (triangleStack + 1) % MAX_STACKS;
				temp2Stack = (triangleStack + 2) % MAX_STACKS;
				temp1Loc = topOfStack(w, temp1Stack, 0);
				temp2Loc = topOfStack(w, temp2Stack, 0);
				if (temp1Loc < 0 && temp2Loc < 0)
					return;
				else if (temp1Loc < 0) {
					fromStack = temp2Stack;
					toStack = temp1Stack;
				} else if (temp2Loc < 0) {
					fromStack = temp1Stack;
					toStack = temp2Stack;
				} else if (w->panex.tileOfPosition[temp1Stack][temp1Loc].loc <
				w->panex.tileOfPosition[temp2Stack][temp2Loc].loc) {
					fromStack = temp1Stack;
					toStack = temp2Stack;
				} else {
					fromStack = temp2Stack;
					toStack = temp1Stack;
				}
				triangleTurn = TRUE;
			}
			if (!moveATile(w, fromStack, toStack))
				return;
		}
	}
#ifdef JMP
	else {
		drawAllTiles(w);
	}
	abortSolvingFlag = False;
#endif
	solvingFlag = False;
	w->panex.cheat = True; /* Assume the worst. */
	setPuzzle(w, ACTION_COMPUTED);
}

#ifdef DEBUG
static void
printStacks(PanexWidget w)
{
	int position, stack;

	for (position = 0; position <= w->panex.tiles; position++)
		for (stack = 0; stack < MAX_STACKS; stack++) {
			char tile = '-';

			if (w->panex.tileOfPosition[stack][position].stack >= 0) {
				if (w->panex.tileOfPosition[stack][position].stack == 0)
					tile = 'A';
				else
					tile = 'a';
				tile += (char) w->panex.tileOfPosition[stack][position].loc;
			}
			(void) printf("%d", tile);
			if (stack == MAX_STACKS - 1)
				(void) printf("\n");
			else
				(void) printf(" ");
		}
}
#endif

static void
moveStack(PanexWidget w, int n, int a, int b) {
	int c;

	if ((a == 0 || b == 0) && (a == 1 || b == 1)) {
		c = 2;
	} else if ((a == 1 || b == 1) && (a == 2 || b == 2)) {
		c = 0;
	} else {
		c = 1;
	}
	if (n > 1)
		moveStack(w, n - 1, a, c);
	moveATile(w, a, b);
	moveATile(w, a, b);
	if (n > 1)
		moveStack(w, n - 1, c, b);
}

static void
cStack(PanexWidget w, int n, Boolean side, Boolean inv) {
	int first, last;
	/*int start = counter;*/

	if (side) {
		first = 2;
		last  = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		moveStack(w, n - 2, 1, first);
		moveATile(w, 1, last);
		moveStack(w, n - 2, first, last);
		moveATile(w, 1, first);
		if (n > 3)
			moveStack(w, n - 3, last, 1);
		moveATile(w, last, first);
	} else {
		moveATile(w, first, last);
		if (n > 3)
			moveStack(w, n - 3, 1, last);
		moveATile(w, first, 1);
		moveStack(w, n - 2, last, first);
		moveATile(w, last, 1);
		moveStack(w, n - 2, first, 1);
	}
}

static void
startPart(PanexWidget w, int n, Boolean side, Boolean inv) {
	int first, last, i;
	/*int start = counter;*/

	if (side) {
		first = 2;
		last  = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		if (n == 2) {
			moveATile(w, 1, last);
			moveATile(w, 1, first);
		} else if (n == 3) {
			cStack(w, n, side, inv);
		} else if (n > 3) {
			cStack(w, n, !side, inv);
			for (i = 1; i <= (n >> 1) - 2; i++)
				cStack(w, n - 2 * i, side, inv);
			startPart(w, (((n & 1) == 1) ? 3 : 2), side, inv);
		}
	} else {
		if (n == 2) {
			moveATile(w, first, 1);
			moveATile(w, last, 1);
		} else if (n == 3) {
			cStack(w, n, side, inv);
		} else if (n > 3) {
			startPart(w, (((n & 1) == 1) ? 3 : 2), side, inv);
			for (i = (n >> 1) - 2; i > 0; i--)
				cStack(w, n - 2 * i, side, inv);
			cStack(w, n, !side, inv);
		}
	}
	/*if (debug)
		(void) printf("startPart %d\n", (counter - start));*/
}

static void
finishPart(PanexWidget w, int n, Boolean side, Boolean inv) {
	int first, last, i;
	/*int start = counter;*/

	if (side) {
		first = 2;
		last  = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		if (n == 3) {
			moveStack(w, n - 2, 1, first);
			moveATile(w, 1, last);
			moveATile(w, first, 1);
			moveATile(w, first, last);
			moveATile(w, 1, last);
			moveATile(w, 1, first);
			moveATile(w, last, first);
		} else if (n > 3) {
			moveStack(w, n - 2, 1, last);
			moveStack(w, 1, 1, first);
			moveStack(w, n - 2, last, 1);
			moveATile(w, first, last);
			for (i = 0; i <= ((n - 1) >> 1) - 2; i++)
				cStack(w, (n - 1) - 2 * i, !side, inv);
			startPart(w, (((n & 1) == 1) ? 2 : 3),
				!side, inv);
		}
	} else {
		if (n == 3) {
			moveATile(w, first, last);
			moveATile(w, first, 1);
			moveATile(w, last, 1);
			moveATile(w, last, first);
			moveATile(w, 1, first);
			moveATile(w, last, 1);
			moveStack(w, n - 2, first, 1);
		} else if (n > 3) {
			startPart(w, (((n & 1) == 1) ? 2 : 3),
				!side, inv);
			for (i = ((n - 1) >> 1) - 2; i >= 0; i--)
				cStack(w, (n - 1) - 2 * i, !side, inv);
			moveATile(w, last, first);
			moveStack(w, n - 2, 1, last);
			moveStack(w, 1, first, 1);
			moveStack(w, n - 2, last, 1);
		}
	}
	/*if (debug)
		(void) printf("finishPart %d\n", (counter - start));*/
}

static void
churn(PanexWidget w, int n, Boolean side, Boolean inv) {
	int first, last;
	/*int start = counter;*/
	/*moves = 12 * 2^(n-2)*/

	if (side) {
		first = 2;
		last  = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		/* base free to combine */
		moveATile(w, last, first);

		moveStack(w, n - 1, 1, last);
		moveATile(w, first, 1);
		moveATile(w, first, 1);
		moveStack(w, n - 1, last, first);
		moveATile(w, 1, last);
		moveATile(w, 1, last);
		moveStack(w, n - 1, first, 1);

		/* separate base */
		moveATile(w, last, first);
	} else {
		/* base free to combine */
		moveATile(w, first, last);

		moveStack(w, n - 1, 1, first);
		moveATile(w, last, 1);
		moveATile(w, last, 1);
		moveStack(w, n - 1, first, last);
		moveATile(w, 1, first);
		moveATile(w, 1, first);
		moveStack(w, n - 1, last, 1);

		/* separate base */
		moveATile(w, first, last);
	}
	/*if (debug)
		 (void) printf("churn %d\n", (counter - start)); */
}

static void
A(PanexWidget w, int n, Boolean side, Boolean inv) {
	int first, last;

	if (side) {
		first = 2;
		last  = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		if (n == 1) {
			moveATile(w, last, 1);
			moveATile(w, first, last);
			moveATile(w, 1, first);
		} else if (n == 2) {
			startPart(w, n, side, !inv);
			churn(w, n, side, !inv);
			startPart(w, n, side, inv);
		} else if (n > 2) {
			finishPart(w, n, side, !inv);
			churn(w, n, side, !inv);
			startPart(w, n, side, inv);
		}
	} else {
		if (n == 1) {
			moveATile(w, first, 1);
			moveATile(w, last, first);
			moveATile(w, 1, last);
		} else if (n == 2) {
			startPart(w, n, side, inv);
			churn(w, n, side, inv);
			startPart(w, n, side, !inv);
		} else if (n > 2) {
			startPart(w, n, side, inv);
			churn(w, n, side, inv);
			finishPart(w, n, side, !inv);
		}
	}
}

static void
solveAlgorithmeMode(PanexWidget w)
{
	if (solvingFlag)
		return;
#ifdef JMP
	if (!setjmp(solve_env))
#endif
	{
		solvingFlag = True;

#ifdef WINVER
		(void) SRAND(time(NULL));
#else
		(void) SRAND(getpid());
#endif
		if (w->panex.tiles > 0) {
			Boolean side = (NRAND(2) == 1);
			Boolean inv = (NRAND(2) == 1);
#ifdef DEBUG
			/* good for debugging */
#if 1
			Boolean side = LEFT;
#else
			Boolean side = RIGHT;
#endif
#if 1
			Boolean side = NORMAL;
#else
			Boolean side = INVERSE;
#endif
#endif

			if (w->panex.tiles > 0) {
				A(w, w->panex.tiles, side, inv);
			}
		}
	}
#ifdef JMP
	else {
		drawAllTiles(w);
	}
	abortSolvingFlag = False;
#endif
	solvingFlag = False;
	w->panex.cheat = True; /* Assume the worst. */
	setPuzzle(w, ACTION_COMPUTED);
#ifdef DEBUG
	counter = 0;
#endif
}

/*
	Recursive algorithm starts at level 5, shown by computer checking
	to be the shortest algorithm, i.e. the upper bound U(n), to at
	least to level 9.
	Lower bounds discussed by Manasse, Sleator, & Wei is given as L(n).
	T(n) = move a tower of height n
	X(n) = transfer or swap towers
	S(n) = sink a n level piece in center stack
	SQ(n) = move a single piece (for Quantum and Baxter alg.)
	DQ(n) = move a 2 n and n-1 and reorder (for Quantum and Baxter alg.)
	WB(n) = solve but one piece n in center (Baxter alg.)

	n	T(n)	X(n)	U(n)	L(n)
	1	1	3
	2	3	13
	3	9	42
	4	24	128
	5	58	343	343	320
	6	143	881	881	796
	7	345	2189	2189	1944
	8	836	5359	5359	4716
	9	2018	13023	13023	11408
	10	4875	?	31537	27564
*/

#ifdef DEBUG
static Boolean side = LEFT; /* False */
static Boolean side = RIGHT; /* True */
static Boolean side = NORMAL; /* False */
static Boolean side = INVERSE; /* True */
#endif

/* Randomize to make it interesting */
static void
centerSwap(PanexWidget w)
{
	if (NRAND(2) == 1) {
		(void) moveATile(w, 1, 0);
		(void) moveATile(w, 1, 2);
		(void) moveATile(w, 0, 1);
		(void) moveATile(w, 2, 1);
	} else {
		(void) moveATile(w, 1, 2);
		(void) moveATile(w, 1, 0);
		(void) moveATile(w, 2, 1);
		(void) moveATile(w, 0, 1);
	}
}

static void
sideSwap(PanexWidget w, Boolean side)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (NRAND(2) == 1) {
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, first);
	} else {
		(void) moveATile(w, first, 1);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, first);
	}
}

/*-
	n	T(n)	TBar(n)	S(n)	SBar(n)
	1	1	1	-	-
	2	3	2	2	2
	3	9	9	8	8
	4	24	23	22	22
	5	58	58	57	57
	6	143	142	141	141
	7	345	345	344	344
	8	836	835	834	834
	9	2018	2018	2017	2017
	10	4875	4874	4873	4873
*/

/* Changes to TBar, SBar, V to use the "vTrick" that        */
/* swaps B and b, rather than calling LinvTBar(3) and       */
/* RinvTBar(3).                                             */
/*                                                          */
/* All calls to TBar and SBar require a new parameter,      */
/* vTrick. This will always be False, except when called    */
/* from V, and when carefully passed down from within       */
/* TBar and SBar. When True, the last call to LInvTBar(3)   */
/* in LInvTBar(n-1) and the first call to RinvTBar(3)       */
/* in RInvSBar(n-1) are suppressed. V added the replacement */
/* 19 moves (with 18 moves removed).                        */

static void T(PanexWidget w, int n, Boolean side, Boolean inv);
static void TBar(PanexWidget w, int n,
	Boolean side, Boolean inv, Boolean vTrick, Boolean qTrick);
static void S(PanexWidget w, int n, Boolean side, Boolean inv);
static void SBar(PanexWidget w, int n,
	Boolean side, Boolean inv, Boolean vTrick);

static void
T(PanexWidget w, int n, Boolean side, Boolean inv)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
		}
	if (inv) {
		if (n == 1) {
			(void) moveATile(w, 1, first);
		} else if (n == 2) {
			(void) moveATile(w, 1, last);
			TBar(w, n, side, inv, False, False);
		} else if (n == 3) {
			(void) moveATile(w, 1, first);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, 1, last);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, last, first);
			T(w, 2, side, inv);
		} else if (n >= 4) {
			T(w, n - 2, side, inv); /* Paper says invert but... */
			centerSwap(w);
			if (((n & 1) == 0) && (NRAND(2) == 1)) {
				S(w, n - 1, side, inv);
				T(w, n - 1, side, inv);
			} else {
				SBar(w, n - 1, side, inv, False);
				TBar(w, n - 1, side, inv, False, False);
			}
		}
	} else {
		if (n == 1) {
			(void) moveATile(w, first, 1);
		} else if (n == 2) {
			TBar(w, n, side, inv, False, False);
			(void) moveATile(w, last, 1);
		} else if (n == 3) {
			T(w, 2, side, inv);
			(void) moveATile(w, first, last);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, last, 1);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, first, 1);
		} else if (n >= 4) {
			if (((n & 1) == 0) && (NRAND(2) == 1)) {
				T(w, n - 1, side, inv);
				S(w, n - 1, side, inv);
			} else {
				TBar(w, n - 1, side, inv, False, False);
				SBar(w, n - 1, side, inv, False);
			}
			centerSwap(w);
			T(w, n - 2, side, inv); /* Paper says invert but... */
		}
	}
}

static void
TBar(PanexWidget w, int n,
	Boolean side, Boolean inv, Boolean vTrick, Boolean qTrick)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		if (n == 1) {
			(void) moveATile(w, last, first);
		} else if (n == 2) {
			(void) moveATile(w, 1, first);
			(void) moveATile(w, last, first);
		} else if (n == 3) {
			if (!vTrick) {
				if (!qTrick)
					(void) moveATile(w, last, first);
				(void) moveATile(w, 1, first);
				(void) moveATile(w, 1, last);
				(void) moveATile(w, first, 1);
				(void) moveATile(w, first, 1);
				(void) moveATile(w, last, first);
				T(w, 2, side, inv);
			}
		} else if (n >= 4) {
			TBar(w, n - 2, side, inv, False, qTrick); /* Paper says invert but... */
			centerSwap(w);
			if (((n & 1) == 0) && (NRAND(2) == 1) && !vTrick) {
				S(w, n - 1, side, inv);
				T(w, n - 1, side, inv);
			} else {
				SBar(w, n - 1, side, inv, False);
				TBar(w, n - 1, side, inv, vTrick, False);
			}
		}
	} else {
		if (n == 1) {
			(void) moveATile(w, first, last);
		} else if (n == 2) {
			(void) moveATile(w, first, last);
			(void) moveATile(w, first, 1);
		} else if (n == 3) {
			if (!vTrick) {
				T(w, 2, side, inv);
				(void) moveATile(w, first, last);
				(void) moveATile(w, 1, first);
				(void) moveATile(w, 1, first);
				(void) moveATile(w, last, 1);
				(void) moveATile(w, first, 1);
				if (!qTrick)
					(void) moveATile(w, first, last);
			}
		} else if (n >= 4) {
			if (((n & 1) == 0) && (NRAND(2) == 1) && !vTrick) {
				T(w, n - 1, side, inv);
				S(w, n - 1, side, inv);
			} else {
				TBar(w, n - 1, side, inv, vTrick, False);
				SBar(w, n - 1, side, inv, False);
			}
			centerSwap(w);
			TBar(w, n - 2, side, inv, False, qTrick); /* Paper says invert but... */
		}
	}
}

static void
S(PanexWidget w, int n, Boolean side, Boolean inv)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		if (n == 2) {
			SBar(w, n, side, inv, False);
			(void) moveATile(w, last, 1);
		} else if (n == 3) {
			sideSwap(w, side);
			(void) moveATile(w, 1, last);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, last, first);
		} else if (n >= 4) {
			if (((n & 1) == 1) && (NRAND(2) == 1)) {
				T(w, n - 2, side, !inv);
				S(w, n - 1, side, !inv);
			} else {
				TBar(w, n - 2, side, !inv, False, False);
				SBar(w, n - 1, side, !inv, False);
			}
			centerSwap(w);
			S(w, n - 1, side, inv);
		}
	} else {
		if (n == 2) {
			(void) moveATile(w, 1, last);
			SBar(w, n, side, inv, False);
		} else if (n == 3) {
			(void) moveATile(w, first, last);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, last, 1);
			sideSwap(w, side);
		} else if (n >= 4) {
			S(w, n - 1, side, inv);
			centerSwap(w);
			if (((n & 1) == 1) && (NRAND(2) == 1)) {
				S(w, n - 1, side, !inv);
				T(w, n - 2, side, !inv);
			} else {
				SBar(w, n - 1, side, !inv, False);
				TBar(w, n - 2, side, !inv, False, False);
			}
		}
	}
}

static void
SBar(PanexWidget w, int n,
	Boolean side, Boolean inv, Boolean vTrick)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		if (n == 2) {
			(void) moveATile(w, first, last);
			(void) moveATile(w, 1, first);
		} else if (n == 3) {
			(void) moveATile(w, first, 1);
			(void) moveATile(w, first, last);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, last, 1);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, first, last);
			(void) moveATile(w, 1, first);
		} else if (n >= 4) {
			if (((n & 1) == 1) && (NRAND(2) == 1) && !vTrick) {
				T(w, n - 2, side, !inv);
				S(w, n - 1, side, !inv);
			} else {
				TBar(w, n - 2, side, !inv, vTrick, False);
				SBar(w, n - 1, side, !inv, False);
			}
			centerSwap(w);
			SBar(w, n - 1, side, inv, False);
		}
	} else {
		if (n == 2) {
			(void) moveATile(w, first, 1);
			(void) moveATile(w, last, first);
		} else if (n == 3) {
			(void) moveATile(w, first, 1);
			(void) moveATile(w, last, first);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, 1, last);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, last, first);
			(void) moveATile(w, 1, first);
		} else if (n >= 4) {
			SBar(w, n - 1, side, inv, False);
			centerSwap(w);
			if (((n & 1) == 1) && (NRAND(2) == 1) && !vTrick) {
				S(w, n - 1, side, !inv);
				T(w, n - 2, side, !inv);
			} else {
				SBar(w, n - 1, side, !inv, False);
				TBar(w, n - 2, side, !inv, vTrick, False);
			}
		}
	}
}

static void
W(PanexWidget w, Boolean side, Boolean inv) {
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, first, last);
	} else {
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, first);
	}
}

static void
VTrick(PanexWidget w, Boolean side, Boolean inv)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, last);
		W(w, side, inv);
	} else {
		W(w, side, inv);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, last, first);
	}
}

static void
V(PanexWidget w, int n, Boolean side, Boolean inv)
{
	if (inv) {
		SBar(w, n, !side, !inv, True);
		VTrick(w, side, inv);
		TBar(w, n - 1, side, !inv, True, False);
	} else {
		TBar(w, n - 1, side, !inv, True, False);
		VTrick(w, side, inv);
		SBar(w, n, !side, !inv, True);
	}
}

static void
Y(PanexWidget w, Boolean side, Boolean inv)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, last);
	} else {
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, first);
	}
}

static void
YBar(PanexWidget w, Boolean side, Boolean inv)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, last);
	} else {
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, first);
	}
}

static void
Z(PanexWidget w, Boolean side, Boolean inv)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, last);
		centerSwap(w);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, 1);
	} else {
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, first);
		centerSwap(w);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, last);
	}
}

#ifdef BAXTER_ALG
static void
SQ(PanexWidget w, int n, Boolean side, Boolean inv)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		if (n == 1) {
			(void) moveATile(w, 1, first);
		} else if (n == 2) {
			(void) moveATile(w, first, last);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, last, first);
		} else if (n >= 3) {
			if (((n & 1) == 0) && (NRAND(2) == 1)) {
				S(w, n, side, inv);
				T(w, n - 1, side, inv);
			} else {
				SBar(w, n, side, inv, False);
				TBar(w, n - 1, side, inv, False, False);
			}
		}
	} else {
		if (n == 1) {
			(void) moveATile(w, first, 1);
		} else if (n == 2) {
			(void) moveATile(w, first, last);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, last, first);
		} else if (n >= 3) {
			if (((n & 1) == 0) && (NRAND(2) == 1)) {
				T(w, n - 1, side, inv);
				S(w, n, side, inv);
			} else {
				TBar(w, n - 1, side, inv, False, False);
				SBar(w, n, side, inv, False);
			}
		}
	}
}

static void
DQ(PanexWidget w, int n, Boolean side, Boolean inv)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (n <= 1)
		return;
	if (inv) {
		if (n == 2) {
			centerSwap(w);
			(void) moveATile(w, 1, last);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, last, first);
		} else if (n >= 3) {
			centerSwap(w);
			if (((n & 1) == 0) && (NRAND(2) == 1)) {
				S(w, n - 1, side, inv);
				T(w, n - 1, side, inv);
			} else {
				SBar(w, n - 1, side, inv, False);
				TBar(w, n - 1, side, inv, False, False);
			}
		}
	} else {
		if (n == 2) {
			(void) moveATile(w, first, last);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, last, 1);
			centerSwap(w);
		} else if (n >= 3) {
			if (((n & 1) == 0) && (NRAND(2) == 1)) {
				T(w, n - 1, side, inv);
				S(w, n - 1, side, inv);
			} else {
				TBar(w, n - 1, side, inv, False, False);
				SBar(w, n - 1, side, inv, False);
			}
			centerSwap(w);
		}
	}
}

static void
WB(PanexWidget w, int n, Boolean side, Boolean inv)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		if (n == 1) {
			(void) moveATile(w, first, last);
			(void) moveATile(w, 1, first);
		} else if (n == 2) {
			sideSwap(w, side);
			(void) moveATile(w, last, 1);
			(void) moveATile(w, first, last);
			(void) moveATile(w, first, last);
			(void) moveATile(w, 1, last);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, last, first);
		} else if (n >= 3) {
			SQ(w, n, side, !inv);
			SQ(w, n - 1, !side, !inv);
			centerSwap(w);
			SQ(w, n - 1, !side, inv);
			SQ(w, n - 1, side, !inv);
			SQ(w, n - 2, !side, !inv);
			centerSwap(w);
			SQ(w, n - 2, !side, inv);
			WB(w, n - 2, side, inv);
			DQ(w, n, side, inv);
		}
	} else {
		if (n == 1) {
			(void) moveATile(w, first, 1);
			(void) moveATile(w, last, first);
		} else if (n == 2) {
			(void) moveATile(w, first, last);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, last, 1);
			(void) moveATile(w, last, first);
			(void) moveATile(w, last, first);
			(void) moveATile(w, 1, last);
			sideSwap(w, side);
		} else if (n >= 3) {
			DQ(w, n, side, inv);
			WB(w, n - 2, side, inv);
			SQ(w, n - 2, !side, inv);
			centerSwap(w);
			SQ(w, n - 2, !side, !inv);
			SQ(w, n - 1, side, !inv);
			SQ(w, n - 1, !side, inv);
			centerSwap(w);
			SQ(w, n - 1, !side, !inv);
			SQ(w, n, side, !inv);
		}
	}
}
#endif

#if defined(QUANTUM_ALG) || defined(QUANTUM_JANSEN_ALG)
static void
BQ(PanexWidget w, int n, Boolean side, Boolean inv)
{
	int first, last;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		SQ(w, n - 1, side, !inv);
		centerSwap(w);
		SQ(w, n - 1, side, inv);
		SQ(w, n - 1, !side, !inv);
	} else {
		SQ(w, n - 1, !side, !inv);
		SQ(w, n - 1, side, inv);
		centerSwap(w);
		SQ(w, n - 1, side, !inv);
	}
}
#endif

#ifdef SIMPLE_ALG
static void
XSimple(PanexWidget w, int n, Boolean side, Boolean inv)
{
	/* Yeah, it works but it takes a long time */
	if (inv) {
		XSimple(w, n - 1, side, inv);
		TBar(w, n - 1, !side, !inv, False, False);
		SBar(w, n, !side, !inv, False);
		TBar(w, n - 1, side, !inv, False, False);
		SBar(w, n, side, !inv, False);
		SBar(w, n, !side, inv, False);
		TBar(w, n - 1, !side, inv, False, False);
		SBar(w, n, side, inv, False);
		TBar(w, n - 1, side, inv, False, False);
	} else {
		TBar(w, n - 1, side, inv, False, False);
		SBar(w, n, side, inv, False);
		TBar(w, n - 1, !side, inv, False, False);
		SBar(w, n, !side, inv, False);
		SBar(w, n, side, !inv, False);
		TBar(w, n - 1, side, !inv, False, False);
		SBar(w, n, !side, !inv, False);
		TBar(w, n - 1, !side, !inv, False, False);
		XSimple(w, n - 1, side, inv);
	}
}
#endif

#ifdef BAXTER_ALG
/* From Nick Baxter */
static void
XBaxter(PanexWidget w, int n, Boolean side, Boolean inv)
{
	if (inv) {
		SQ(w, n, !side, !inv);
		WB(w, n, side, inv);
	} else {
		WB(w, n, side, inv);
		SQ(w, n, !side, !inv);
	}
}
#endif

#if defined(QUANTUM_ALG) || defined(QUANTUM_JANSEN_ALG)
/* From Quantum */
static void
XQuantum(PanexWidget w, int n, Boolean side, Boolean inv)
{
	int first, last, i;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (inv) {
		SQ(w, n, side, !inv);
		SQ(w, n, !side, !inv);

#ifdef QUANTUM_ALG
		/* Original Algorithm */
		for (i = n; i >= 3; i--)
			BQ(w, i, side, inv);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, first);
#else /* QUANTUM_JANSEN_ALG */
		/* Rene Jansen's one move improvement */
		for (i = n; i >= 4; i--)
			BQ(w, i, side, inv);
		sideSwap(w, side);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, first);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, first);
#endif
		(void) moveATile(w, last, first);
		if ((n & 1) == 1)
			(void) moveATile(w, 1, last);
		else
			(void) moveATile(w, 1, first);
/* qTrick combine next (first, last) move to save a move if n odd */
		TBar(w, n, !side, inv, False, ((n & 1) == 1));
	} else {
		TBar(w, n, !side, inv, False, ((n & 1) == 1));
/* qTrick combine previous (last, first) move to save a move if n odd */
		if ((n & 1) == 1)
			(void) moveATile(w, last, 1);
		else
			(void) moveATile(w, first, 1);
		(void) moveATile(w, first, last);

#ifdef QUANTUM_ALG
		/* Original Algorithm */
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, last, first);
		for (int i = 3; i <= n; i++)
			BQ(w, i, inv);
#else /* QUANTUM_JANSEN_ALG */
		/* Rene Jansen's one move improvement */
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, 1);
		(void) moveATile(w, last, first);
		(void) moveATile(w, 1, last);
		(void) moveATile(w, first, last);
		(void) moveATile(w, first, 1);
		(void) moveATile(w, first, last);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, 1, first);
		(void) moveATile(w, last, 1);
		sideSwap(w, side);
		for (i = 4; i <= n; i++)
			BQ(w, i, side, inv);
#endif
		SQ(w, n, !side, !inv);
		SQ(w, n, side, !inv);
	}
}
#endif

static void
X(PanexWidget w, int n, Boolean side, Boolean inv)
{
	int first, last, i;

	if (side) {
		first = 2;
		last = 0;
	} else {
		first = 0;
		last = 2;
	}
	if (n == 1) {
		if (inv) {
			T(w, 1, !side, !inv);
			(void) moveATile(w, first, last);
			T(w, 1, side, inv);
		} else {
			T(w, 1, side, inv);
			(void) moveATile(w, last, first);
			T(w, 1, !side, !inv);
		}
	} else if (n == 2) {
		if (inv) {
			TBar(w, 2, !side, !inv, False, False);
			(void) moveATile(w, first, last);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, first, last);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, last, first);
			(void) moveATile(w, last, 1);
			(void) moveATile(w, first, last);
			(void) moveATile(w, first, last);
			T(w, 2, side, inv);
		} else {
			T(w, 2, side, inv);
			(void) moveATile(w, last, first);
			(void) moveATile(w, last, first);
			(void) moveATile(w, 1, last);
			(void) moveATile(w, first, last);
			(void) moveATile(w, first, 1);
			(void) moveATile(w, last, first);
			(void) moveATile(w, 1, first);
			(void) moveATile(w, last, first);
			TBar(w, 2, !side, !inv, False, False);
		}
	} else if (n == 3) {
		if (inv) {
			Z(w, side, inv);
			Y(w, side, inv);
			TBar(w, 2, !side, inv, False, False);
			SBar(w, 3, side, inv, False);
			TBar(w, 2, side, inv, False, False);
		} else {
			TBar(w, 2, side, inv, False, False);
			SBar(w, 3, side, inv, False);
			TBar(w, 2, !side, inv, False, False);
			Y(w, side, inv);
			Z(w, side, inv);
		}
	} else if (n == 4) {
		if (inv) {
			TBar(w, 3, !side, !inv, False, False);
			SBar(w, 3, !side, !inv, False);
			centerSwap(w);
			S(w, 3, !side, inv);
			W(w, side, inv);
			SBar(w, 4, side, !inv, False);
			SBar(w, 4, !side, inv, False);
			TBar(w, 2, !side, inv, False, False);
			TBar(w, 2, side, !inv, False, False);
			YBar(w, side, inv);
			TBar(w, 2, !side, inv, False, False);
			centerSwap(w);
			SBar(w, 3, side, inv, False);
			TBar(w, 3, side, inv, False, False);
		} else {
			TBar(w, 3, side, inv, False, False);
			SBar(w, 3, side, inv, False);
			centerSwap(w);
			TBar(w, 2, !side, inv, False, False);
			YBar(w, side, inv);
			TBar(w, 2, side, !inv, False, False);
			TBar(w, 2, !side, inv, False, False);
			SBar(w, 4, !side, inv, False);
			SBar(w, 4, side, !inv, False);
			W(w, side, inv);
			S(w, 3, !side, inv);
			centerSwap(w);
			SBar(w, 3, !side, !inv, False);
			TBar(w, 3, !side, !inv, False, False);
		}
	} else if (n >= 5) {
		if (inv) {
			TBar(w, n - 1, !side, !inv, False, False);
			V(w, n, side, inv);
			SBar(w, n, side, !inv, False);
			SBar(w, n, !side, inv, False);
			TBar(w, 2, !side, inv, False, False);
			TBar(w, 2, side, !inv, False, False);
			YBar(w, side, inv);
			TBar(w, 2, !side, inv, False, False);
			TBar(w, 2, side, !inv, False, False);
			for (i = n - 5; i >= 1; i--) {
				SBar(w, n - 1 - i, side, !inv, False);
				SBar(w, n - 1 - i, !side, inv, False);
				TBar(w, n - 2 - i, !side, inv, False, False);
				TBar(w, n - 2 - i, side, !inv, False, False);
			}
			SBar(w, n - 1, side, !inv, False);
			SBar(w, n - 1, !side, inv, False);
			TBar(w, n - 2, !side, inv, False, False);
			centerSwap(w);
			SBar(w, n - 1, side, inv, False);
			TBar(w, n - 1, side, inv, False, False);
		} else {
			TBar(w, n - 1, side, inv, False, False);
			SBar(w, n - 1, side, inv, False);
			centerSwap(w);
			TBar(w, n - 2, !side, inv, False, False);
			SBar(w, n - 1, !side, inv, False);
			SBar(w, n - 1, side, !inv, False);
			for (i = 1; i <= n - 5; i++) {
				TBar(w, n - 2 - i, side, !inv, False, False);
				TBar(w, n - 2 - i, !side, inv, False, False);
				SBar(w, n - 1 - i, !side, inv, False);
				SBar(w, n - 1 - i, side, !inv, False);
			}
			TBar(w, 2, side, !inv, False, False);
			TBar(w, 2, !side, inv, False, False);
			YBar(w, side, inv);
			TBar(w, 2, side, !inv, False, False);
			TBar(w, 2, !side, inv, False, False);
			SBar(w, n, !side, inv, False);
			SBar(w, n, side, !inv, False);
			V(w, n, side, inv);
			TBar(w, n - 1, !side, !inv, False, False);
		}
	}
}

static void
solvePanexMode(PanexWidget w)
{
	if (solvingFlag)
		return;
#ifdef JMP
	if (!setjmp(solve_env))
#endif
	{
		solvingFlag = True;

#ifdef WINVER
		(void) SRAND(time(NULL));
#else
		(void) SRAND(getpid());
#endif
		if (w->panex.tiles > 0) {
			Boolean side = (NRAND(2) == 1);
			Boolean inv = (NRAND(2) == 1);
#ifdef DEBUG
			/* good for debugging */
#if 0
			Boolean side = LEFT;
#else
			Boolean side = RIGHT;
#endif
#if 0
			Boolean side = NORMAL;
#else
			Boolean side = INVERSE;
#endif
#endif

#ifndef ATT_ALG
/* Other, less efficient algorithms */
			if (w->panex.tiles >= 3) {
#ifdef SIMPLE_ALG
				XSimple(w, w->panex.tiles, side, inv);
#else
#ifdef BAXTER_ALG
				XBaxter(w, w->panex.tiles, side, inv);
#else /* QUANTUM_ALG || QUANTUM_JANSEN_ALG */
				XQuantum(w, w->panex.tiles, side, inv);
#endif
#endif
			} else
#else
			X(w, w->panex.tiles, side, inv);
#endif
		}
	}
#ifdef JMP
	else {
		drawAllTiles(w);
	}
	abortSolvingFlag = False;
#endif
	solvingFlag = False;
	w->panex.cheat = True; /* Assume the worst. */
	setPuzzle(w, ACTION_COMPUTED);
#ifdef DEBUG
	counter = 0;
#endif
}

void
solveTilesFromStart(PanexWidget w)
{
	if (w->panex.mode == PANEX)
		solvePanexMode(w);
	else if (w->panex.mode == ALGORITHME)
		solveAlgorithmeMode(w);
	else /*if (w->panex.mode == HANOI)*/
		solveHanoiMode(w);
}
