/*
 * @(#)MlinkS.c
 *
 * Taken from the algorithms in Ideal's you are about to find the
 * solution: The Missing Link (also looked at James G. Nourse's
 * The Simple Solutions to Cubic Puzzles).
 * Many speed-ups possible, for example: add more special cases,
 * use 2 ignored, speed up for non-oriented and non-middle, handle
 * reverse better so it does not repeat last move of 3 tiles.
 * Break ability taken from the X puzzle by Don Bennett, HP Labs
 *
 * Copyright 2003 - 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 Mlink */

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

#if 0
#define DEBUG
#endif

#define leftAway(w,r) moveTilePiece(w,TOP,(r)?w->mlink.tiles-1:0,False) /* push top away */
#define leftAway2(w,r) moveTilePiece(w,TOP,(r)?w->mlink.tiles-1:0,False);\
			moveTilePiece(w,TOP,(r)?w->mlink.tiles-1:0,False)
#define leftTowards(w,r) moveTilePiece(w,BOTTOM,(r)?w->mlink.tiles-1:0,False)
#define leftTowards2(w,r) moveTilePiece(w,BOTTOM,(r)?w->mlink.tiles-1:0,False);\
			moveTilePiece(w,BOTTOM,(r)?w->mlink.tiles-1:0,False)
#define rightAway(w,r) moveTilePiece(w,TOP,(r)?0:w->mlink.tiles-1,False)
#define rightAway2(w,r) moveTilePiece(w,TOP,(r)?0:w->mlink.tiles-1,False);\
			moveTilePiece(w,TOP,(r)?0:w->mlink.tiles-1,False)
#define rightTowards(w,r) moveTilePiece(w,BOTTOM,(r)?0:w->mlink.tiles-1,False)
#define rightTowards2(w,r) moveTilePiece(w,BOTTOM,(r)?0:w->mlink.tiles-1,False);\
			moveTilePiece(w,BOTTOM,(r)?0:w->mlink.tiles-1,False)
#define leftHalf(w,r); if(NRAND(2)) {leftAway2(w,r);} else {leftTowards2(w,r);}
#define rightHalf(w,r); if(NRAND(2)) {rightAway2(w,r);} else {rightTowards2(w,r);}

#define leftSlide(w,n,r) if(r) moveTilePiece(w,RIGHT,w->mlink.spacePosition-(n),False); \
else moveTilePiece(w,LEFT,w->mlink.spacePosition+(n),False)

#define rightSlide(w,n,r) if(r) moveTilePiece(w,LEFT,w->mlink.spacePosition+(n),False); \
else	moveTilePiece(w,RIGHT,w->mlink.spacePosition-(n),False)

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(MlinkWidget 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(MlinkWidget w)
{
	XEvent event;

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

static void
moveTilePiece(MlinkWidget w,
	const int direction, const int tile,
	const Boolean all)
{
#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
	movePuzzleDelay(w, direction, tile, all);
}

static int
findPiece(MlinkWidget w, int match)
{
	int tile, face = 0, pos = -1;

	for (tile = 0; tile < w->mlink.tiles; tile++) {
		for (face = 0; face < w->mlink.faces; face++) {
			if (w->mlink.tileOfPosition[face * w->mlink.faces + tile] - 1 == match) {
				pos = face * w->mlink.faces + tile;
				break;
			}
		}
		if (pos != -1)
			break;
	}
	return pos;
}

/* Page 6 */
static void
swap02_32(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);

	rightTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 2, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

static void
swap01_31(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightHalf(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);

	rightTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);

	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

/* Page 7 */
static void
swap00_30(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);

	rightAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);

	rightAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);

	if (reverse) {
		leftSlide(w, w->mlink.tiles - 1, !reverse);
	}
}

static void
swap02_22(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);

	rightHalf(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);

	rightHalf(w, reverse);
	leftSlide(w, 2, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);

	if (reverse) {
		leftSlide(w, w->mlink.tiles - 1, !reverse);
	}
}

/* Page 8 */
static void
swap01_21(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightHalf(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);

	rightTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);

	rightHalf(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);

	rightAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse); /* Extra non-documented move */
	}
}

static void
swap00_21(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);

	rightHalf(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);

	rightHalf(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);

	if (reverse) {
		leftSlide(w, w->mlink.tiles - 1, !reverse);
	}
}

/* Page 9 */
/* From here on "All" is simplified to a single tile move */
static void
swap32_30(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);

	if (reverse) {
		leftSlide(w, w->mlink.tiles - 1, !reverse);
	}
}

static void
swap32_21(MlinkWidget w, Boolean reverse)
{

	if (reverse) {
		rightSlide(w, 2, !reverse);
	} else {
		rightSlide(w, 1, reverse);
	}
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

static void
swap32_31(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

/* Page 10 */
static void
swap30_31(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

static void
swap31_20(MlinkWidget w, Boolean reverse)
{

	if (reverse) {
		rightSlide(w, 2, !reverse);
	} else {
		rightSlide(w, 1, reverse);
	}
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);

	if (reverse) {
		leftSlide(w, w->mlink.tiles - 1, !reverse);
	}
}

static void
swap21_20(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

/* Page 11 */
static void
swap31_22(MlinkWidget w, Boolean reverse)
{

	if (reverse) {
		rightSlide(w, 2, !reverse);
	} else {
		rightSlide(w, 1, reverse);
	}
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse); /* not Towards */
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);

	if (reverse) {
		leftSlide(w, w->mlink.tiles - 1, !reverse);
	}
}

static void
swap30_21(MlinkWidget w, Boolean reverse)
{

	if (reverse) {
		rightSlide(w, 2, !reverse);
	} else {
		rightSlide(w, 1, reverse);
	}
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

/* Page 12 */
static void
swap32_20(MlinkWidget w, Boolean reverse)
{

	if (reverse) {
		rightSlide(w, 2, !reverse);
	} else {
		rightSlide(w, 1, reverse);
	}
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

static void
swap30_22(MlinkWidget w, Boolean reverse)
{

	if (reverse) {
		rightSlide(w, 2, !reverse);
	} else {
		rightSlide(w, 1, reverse);
	}
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

/* Page 13 */
static void
swap22_32(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

static void
swap21_31(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

static void
swap20_30(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

/* Page 14 */
static void
swap22_21(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

/* Slight efficiency gain if used */
#if 0
static void
swap32_31_30(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

static void
swap32_30_31(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}
#endif

#if 0
/* move* and alt* From Simple Solutions to Cubic Puzzles */
/* 1A */
static void
moveRight2Toleft2(MlinkWidget w)
{
	const Boolean reverse = False;

	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, 1, reverse);
	leftAway(w, reverse);
	rightSlide(w, 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, 1, reverse);
}

/* 1B */
static void
moveRightMiddleToleft(MlinkWidget w)
{
	const Boolean reverse = False;

	rightTowards(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, 1, reverse);
	leftAway(w, reverse);
	rightSlide(w, 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, 1, reverse);
}

/* 1C */
static void
moveleftMiddleToleft(MlinkWidget w)
{
	const Boolean reverse = False;

	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, 1, reverse);
	leftAway(w, reverse);
	rightSlide(w, 1, reverse);
	leftAway(w, reverse);
	leftSlide(w, 1, reverse);
}

/* 2E (2B should be opposite but lists the same moves) */
static void
moveRightMiddleToleftMiddle(MlinkWidget w)
{
	const Boolean reverse = False;

	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftAway(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, w->mlink.tiles - 1, reverse);
	leftTowards(w, reverse);
	leftSlide(w, w->mlink.tiles - 1, reverse);
}

/* altSwap3?_3? same number of moves as swap3?_3? */
/* 4A */
static void
altSwap30_31(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, 2, !reverse);
	} else {
		rightSlide(w, 1, reverse);
	}
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse)
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	if (reverse) {
		leftSlide(w, w->mlink.tiles - 1, !reverse);
	}
}

/* 4B */
static void
altSwap32_30(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, w->mlink.tiles - 1, !reverse);
	}
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightHalf(w, reverse);

	if (reverse) {
		leftSlide(w, w->mlink.tiles - 1, !reverse);
	}
}

/* 4C */
static void
altSwap32_31(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, 2, !reverse);
	} else {
		rightSlide(w, 1, reverse);
	}
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	if (reverse) {
		leftSlide(w, w->mlink.tiles - 1, !reverse);
	}
}

/* altSwap3?_3?_3? 4 moves more than swap3?_3?_3? */
/* (2 single moves + 2 from an extra rightHalf counts as 2) */
/* 4D1 */
static void
altSwap32_30_31(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, 2, !reverse);
	} else {
		rightSlide(w, 1, reverse);
	}
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightAway(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}

/* 4D2 */
static void
altSwap32_31_30(MlinkWidget w, Boolean reverse)
{
	if (reverse) {
		rightSlide(w, 2, !reverse);
	} else {
		rightSlide(w, 1, reverse);
	}
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	leftSlide(w, 1, reverse);
	rightAway(w, reverse);
	rightSlide(w, 1, reverse);
	rightTowards(w, reverse);
	leftSlide(w, 1, reverse);
	rightTowards(w, reverse);
	rightSlide(w, 1, reverse);
	rightHalf(w, reverse);
	if (reverse) {
		leftSlide(w, 2, !reverse);
	} else {
		leftSlide(w, 1, reverse);
	}
}
#endif

/* Page 4 - 5, but did not really use it too much */
static void
solveWhite(MlinkWidget w)
{
	Boolean reverse = False;
	int current, loc, pos;

#ifdef DEBUG
	(void) printf("solveWhite\n");
#endif
	current = (w->mlink.faces - 1) * w->mlink.tiles;
	if (w->mlink.tileOfPosition[current] - 1 != current) {
		loc = findPiece(w, current);
		if (loc % w->mlink.tiles != 0) { /* not on left side */
			if (w->mlink.spacePosition / w->mlink.tiles !=
					loc / w->mlink.tiles) { /* not on same face */
				if (w->mlink.spacePosition % w->mlink.tiles > 0) {
					rightSlide(w, w->mlink.spacePosition %
						w->mlink.tiles, reverse);
				}
				pos = ((loc / w->mlink.tiles -
					w->mlink.spacePosition / w->mlink.tiles) +
					w->mlink.faces) % w->mlink.faces;
				if (pos == 1) {
					leftTowards(w, reverse);
				} else if (pos == 2) {
					leftHalf(w, reverse);
				} else if (pos == 3) {
					leftAway(w, reverse);
				}
			}
			if (w->mlink.spacePosition % w->mlink.tiles < w->mlink.tiles - 1) {
				if (loc > w->mlink.spacePosition)
					loc--;
				leftSlide(w, ((w->mlink.spacePosition / w->mlink.tiles) * w->mlink.tiles + w->mlink.tiles - 1) - w->mlink.spacePosition, reverse);
			}
		}
		while (loc % w->mlink.tiles != 0) {
			rightAway(w, reverse);
			rightSlide(w, w->mlink.tiles - 1, reverse);
			leftTowards(w, reverse);
			leftSlide(w, w->mlink.tiles - 1, reverse);
			loc--;
		}
		pos = (loc / w->mlink.tiles) % w->mlink.faces;
		if (pos == 0) {
			leftAway(w, reverse);
		} else if (pos == 1) {
			leftHalf(w, reverse);
		} else if (pos == 2) {
			leftTowards(w, reverse);
		}
	}
	if (w->mlink.spacePosition % w->mlink.tiles < w->mlink.tiles - 1) {
		leftSlide(w, ((w->mlink.spacePosition / w->mlink.tiles) *
			w->mlink.tiles + w->mlink.tiles - 1) -
			w->mlink.spacePosition, reverse);
	}
	pos = (w->mlink.spacePosition / w->mlink.tiles) % w->mlink.faces;
	if (pos == 0) {
		rightAway(w, reverse);
	} else if (pos == 1) {
		rightHalf(w, reverse);
	} else if (pos == 2) {
		rightTowards(w, reverse);
	}

	current++;
	if (w->mlink.tileOfPosition[current] - 1 != current) {
		loc = findPiece(w, current);
		if (w->mlink.faces - 1 !=
				loc / w->mlink.tiles) { /* not same face */
			if ((loc + 1) % w->mlink.tiles != 0) { /* not right side */
				int i;

				pos = loc / w->mlink.tiles;
				if (pos == 0) {
					rightTowards(w, reverse);
				} else if (pos == 1) {
					rightHalf(w, reverse);
				} else if (pos == 2) {
					rightAway(w, reverse);
				}
				for (i = 0; i < w->mlink.tiles - 1 -
						(loc % w->mlink.tiles); i++) {
					rightSlide(w, w->mlink.tiles - 1, reverse);
					leftAway(w, reverse);
					leftSlide(w, w->mlink.tiles - 1, reverse);
					rightTowards(w, reverse);
					leftTowards(w, reverse);
				}
				loc = (w->mlink.spacePosition + w->mlink.tiles) % w->mlink.tileFaces;
			}
			pos = w->mlink.spacePosition / w->mlink.tiles;
			if (pos == 0) {
				rightAway(w, reverse);
				loc = (loc + 3 * w->mlink.tiles) % w->mlink.tileFaces;
			} else if (pos == 1) {
				rightHalf(w, reverse);
				loc = (loc + 2 * w->mlink.tiles) % w->mlink.tileFaces;
			} else if (pos == 2) {
				rightTowards(w, reverse);
				loc = (loc + w->mlink.tiles) % w->mlink.tileFaces;
			}
			rightSlide(w, 1, reverse);
			pos = loc / w->mlink.tiles;
			if (pos == 0) {
				rightAway(w, reverse);
			} else if (pos == 1) {
				rightHalf(w, reverse);
			} else if (pos == 2) {
				rightTowards(w, reverse);
			}
			leftSlide(w, 1, reverse);
		}
		rightAway(w, reverse);
		rightSlide(w, w->mlink.tiles - 1, reverse);
		leftTowards(w, reverse);
		leftSlide(w, w->mlink.tiles - 1, reverse);
		leftAway(w, reverse);
	}

	current++;
	if (w->mlink.tileOfPosition[current] - 1 != current) {
		loc = findPiece(w, current);
		if ((loc + 1) % w->mlink.tiles != 0) { /* not right side */
			int i;

			pos = loc / w->mlink.tiles;
			if (pos == 0) {
				rightTowards(w, reverse);
			} else if (pos == 1) {
				rightHalf(w, reverse);
			} else if (pos == 2) {
				rightAway(w, reverse);
			}
			if (pos == 0) {
			for (i = 0; i < w->mlink.tiles - 1 -
					(loc % w->mlink.tiles); i++) {
				rightSlide(w, w->mlink.tiles - 1, reverse);
				leftTowards(w, reverse);
				leftSlide(w, w->mlink.tiles - 1, reverse);
				rightAway(w, reverse);
				leftAway(w, reverse);
			}
			loc = (w->mlink.spacePosition + 3 * w->mlink.tiles) % w->mlink.tileFaces;
			} else {
			for (i = 0; i < w->mlink.tiles - 1 - (loc % w->mlink.tiles); i++) {
				rightSlide(w, w->mlink.tiles - 1, reverse);
				leftAway(w, reverse);
				leftSlide(w, w->mlink.tiles - 1, reverse);
				rightTowards(w, reverse);
				leftTowards(w, reverse);
			}
			loc = (w->mlink.spacePosition + w->mlink.tiles) % w->mlink.tileFaces;
			}
		}
		pos = w->mlink.spacePosition / w->mlink.tiles;
		if (pos == 0) {
			rightAway(w, reverse);
			loc = (loc + 3 * w->mlink.tiles) % w->mlink.tileFaces;
		} else if (pos == 1) {
			rightHalf(w, reverse);
			loc = (loc + 2 * w->mlink.tiles) % w->mlink.tileFaces;
		} else if (pos == 2) {
			rightTowards(w, reverse);
			loc = (loc + w->mlink.tiles) % w->mlink.tileFaces;
		}
		rightSlide(w, 1, reverse);
		pos = loc / w->mlink.tiles;
		if (pos == 0) {
			rightAway(w, reverse);
		} else if (pos == 1) {
			rightHalf(w, reverse);
		} else if (pos == 2) {
			rightTowards(w, reverse);
		}
		leftSlide(w, 1, reverse);
	}
}

/* Page 6 - 14 */
static Boolean
solveSpecialCases(MlinkWidget w, const int pos1, const int pos2)
{
	Boolean reverse = False;
	int x_1 = pos1 % w->mlink.tiles;
	int y_1 = pos1 / w->mlink.tiles;
	int x_2 = pos2 % w->mlink.tiles;
	int y_2 = pos2 / w->mlink.tiles;

#ifdef DEBUG
	(void) printf("    solveSpecialCases pos %d, %d\n", pos1, pos2);
#endif
	if (x_1 >= w->mlink.tiles / 2) {
		x_1 = w->mlink.tiles - 1 - x_1;
		x_2 = w->mlink.tiles - 1 - x_2;
		reverse = !reverse;
	}
	if (x_1 > x_2 || (x_1 == x_2 && y_1 > y_2)) {
		int temp;

		temp = x_1;
		x_1 = x_2;
		x_2 = temp;
		temp = y_1;
		y_1 = y_2;
		y_2 = temp;
	}
	if (x_1 == 0) {
		if (y_1 == 0) {
			if (x_2 == 0) {
				if (y_2 == 1) {
					swap30_31(w, !reverse); /* 00_01 */
				} else if (y_2 == 2) {
					swap32_30(w, !reverse); /* 00_02 */
				} else {
					return False;
				}
			} else if (x_2 == 1) {
				if (y_2 == 0) {
					swap20_30(w, !reverse); /* 00_10 */
				} else if (y_2 == 1) {
					swap30_21(w, !reverse); /* 00_11 */
				} else if (y_2 == 2) {
					swap30_22(w, !reverse); /* 00_12 */
				} else {
					return False;
				}
			} else if (x_2 == 2) {
				if (y_2 == 1) {
					swap00_21(w, reverse);
				} else {
					return False;
				}
			} else if (x_2 == 3) {
				if (y_2 == 0) {
					swap00_30(w, reverse);
				} else {
					return False;
				}
			} else {
				return False;
			}
		} else if (y_1 == 1) {
			if (x_2 == 0) {
				if (y_2 == 2) {
					swap32_31(w, !reverse); /* 01_02 */
				} else {
					return False;
				}
			} else if (x_2 == 1) {
				if (y_2 == 0) {
					swap31_20(w, !reverse); /* 01_10 */
				} else if (y_2 == 1) {
					swap21_31(w, !reverse); /* 01_11 */
				} else if (y_2 == 2) {
					swap31_22(w, !reverse); /* 01_12 */
				} else {
					return False;
				}
			} else if (x_2 == 2) {
				if (y_2 == 1) {
					swap01_21(w, reverse);
				} else {
					return False;
				}
			} else if (x_2 == 3) {
				if (y_2 == 1) {
					swap01_31(w, reverse);
				} else {
					return False;
				}
			} else {
				return False;
			}
		} else if (y_1 == 2) {
			if (x_2 == 1) {
				if (y_2 == 0) {
					swap32_20(w, !reverse); /* 02_10 */
				} else if (y_2 == 1) {
					swap32_21(w, !reverse); /* 02_11 */
				} else if (y_2 == 2) {
					swap22_32(w, !reverse); /* 02_12 */
				} else {
					return False;
				}
			} else if (x_2 == 2) {
				if (y_2 == 2) {
					swap02_22(w, reverse);
				} else {
					return False;
				}
			} else if (x_2 == 3) {
				if (y_2 == 2) {
					swap02_32(w, reverse);
				} else {
					return False;
				}
			} else {
				return False;
			}
		} else {
			return False;
		}
	} else if (x_1 == 1) {
		if (y_1 == 0) {
			if (x_2 == 1) {
				if (y_2 == 1) {
					swap21_20(w, !reverse); /* 10_11 */
				} else {
					return False;
				}
			} else {
				return False;
			}
		} else if (y_1 == 1) {
			if (x_2 == 1) {
				if (y_2 == 2) {
					swap22_21(w, !reverse); /* 11_12 */
				} else {
					return False;
				}
			} else {
				return False;
			}
		} else {
			return False;
		}
	} else {
		return False;
	}
	return True;
}

static void
solveMiddleFromEnd(MlinkWidget w, int tile, int otherTile)
{
	int face, otherFace, pos, otherPos;
	Boolean found = True;

#ifdef DEBUG
	(void) printf("  solveMiddleFromEnd %d, %d\n", tile, otherTile);
#endif
	while (found) {
		found = False;
		for (face = 0; face < w->mlink.faces - 1; face++) {
			pos = face * w->mlink.tiles + tile;
			for (otherFace = 0; otherFace < w->mlink.faces - 1;
					otherFace++) {
				otherPos = otherFace * w->mlink.tiles + otherTile;
				if (w->mlink.tileOfPosition[pos] - 1 == otherPos) {
					found = solveSpecialCases(w, pos, otherPos);
				}
			}
		}
	}
}

static void
solveEnd(MlinkWidget w, int tile)
{
	int face, otherFace, pos, otherPos;
	Boolean found = True;

#ifdef DEBUG
	(void) printf("  solveEnd %d\n", tile);
#endif
	while (found) {
		found = False;
		for (face = 0; face < w->mlink.faces - 1; face++) {
			pos = face * w->mlink.tiles + tile;
			for (otherFace = 0; otherFace < w->mlink.faces - 1;
					otherFace++) {
				otherPos = otherFace * w->mlink.tiles + tile;
				if (pos != otherPos &&
						w->mlink.tileOfPosition[pos] - 1 == otherPos) {
					found = solveSpecialCases(w, pos, otherPos);
				}
			}
		}
	}
}

static void
solveEnds(MlinkWidget w, int tile, int otherTile)
{
	int face, pos, otherPos;
	Boolean found = True;

#ifdef DEBUG
	(void) printf("  solveEnds %d, %d\n", tile, otherTile);
#endif
	while (found) {
		found = False;
		for (face = 0; face < w->mlink.faces - 1; face++) {
			pos = face * w->mlink.tiles + tile;
			otherPos = face * w->mlink.tiles + otherTile;
			if (w->mlink.tileOfPosition[pos] - 1 == otherPos) {
				found = solveSpecialCases(w, pos, otherPos);
			}
		}
	}
#ifdef DEBUG
	(void) printf("  Finished solveEnds %d, %d\n", tile, otherTile);
	/*sleep(2);*/
#endif
}

static void
matchLeftRight(MlinkWidget w, int tile, int otherTile)
{
	int face, otherFace, pos, otherPos;
	Boolean found = True;

#ifdef DEBUG
	(void) printf("  matchLeftRight %d, %d\n", tile, otherTile);
#endif
	while (found) {
		found = False;
		for (face = 0; face < w->mlink.faces - 1; face++) {
			pos = face * w->mlink.tiles + tile;
			for (otherFace = 0; otherFace < w->mlink.faces - 1;
					otherFace++) {
				otherPos = otherFace * w->mlink.tiles + otherTile;
				if (pos != otherPos &&
						w->mlink.tileOfPosition[pos] - 1 == otherPos) {
					found = solveSpecialCases(w, pos, otherPos + 3);
				}
			}
		}
	}
}

static void
clearOut(MlinkWidget w, int tile, int otherTile, int shift)
{
	int face, otherFace, pos, otherPos;

#ifdef DEBUG
	(void) printf("  clearOut %d, %d, %d\n", tile, otherTile, shift);
#endif
	for (face = 0; face < w->mlink.faces - 1; face++) {
		pos = face * w->mlink.tiles + tile;
		for (otherFace = 0; otherFace < w->mlink.faces - 1;
				otherFace++) {
			otherPos = otherFace * w->mlink.tiles + otherTile;
			if (pos != otherPos &&
					w->mlink.tileOfPosition[pos] - 1 == otherPos) {
				(void) solveSpecialCases(w, pos, pos + shift);
			}
		}
	}
}

static void
solveLeftMiddle(MlinkWidget w)
{
#ifdef DEBUG
	(void) printf("solveLeftMiddle\n");
#endif
	solveMiddleFromEnd(w, 0, 1);
	clearOut(w, 1, 1, -1);
	solveMiddleFromEnd(w, 0, 1);
	clearOut(w, 3, 1, -3);
	solveMiddleFromEnd(w, 0, 1);
	clearOut(w, 2, 1, 1);
	clearOut(w, 3, 1, -3);
	solveMiddleFromEnd(w, 0, 1);
}

static void
solveLeft(MlinkWidget w)
{
#ifdef DEBUG
	(void) printf("solveLeft\n");
#endif
	solveEnd(w, 0);
	matchLeftRight(w, 3, 0);
	solveEnds(w, 3, 0);
	clearOut(w, 2, 0, 1);
	matchLeftRight(w, 3, 0);
	solveEnds(w, 3, 0);
}

static void
solveRightMiddle(MlinkWidget w)
{
#ifdef DEBUG
	(void) printf("solveRightMiddle\n");
#endif
	solveMiddleFromEnd(w, 3, 2);
	clearOut(w, 2, 2, 1);
	solveMiddleFromEnd(w, 3, 2);
}

static void
solveRight(MlinkWidget w)
{
#ifdef DEBUG
	(void) printf("solveRight\n");
#endif
	solveEnd(w, 3);
}

/* This procedure coordinates the solution process. */
void
solveSomeTiles(MlinkWidget w)
{
	setPuzzle(w, ACTION_RESET);
	if (solvingFlag)
		return;

#ifdef JMP
	if (!setjmp(solve_env))
#endif
	{
		solvingFlag = True;
		if (!checkSolved(w)) {
			solveWhite(w);
			solveLeftMiddle(w);
			solveLeft(w);
			solveRightMiddle(w);
			solveRight(w);
		}
	}
#ifdef JMP
	else {
		drawAllTiles(w);
	}
	abortSolvingFlag = False;
#endif
	solvingFlag = False;
	w->mlink.cheat = True; /* Assume the worst. */
	setPuzzle(w, ACTION_COMPUTED);
}
