/*
(c)2006 justwidgets.com. All rights reserved.

CHANGELOG:
[2006-06-13]v1 Original release version. Run from puz03_release01.html
[2006-06-15]v2 Added toggle() so user can customize which cells are allowed moves in puzzle grid.
			v3 Added "No solution found" output
[2006-06-19]v4 Added &shy; (causes line breaks when needed) so cout wraps in Safari v2.0
			   Added to css in .html: 
			   	margin-left:auto; margin-right:auto;  to  #outputbox and table
				so they are centered in Safari v2.0
[2006-08-12]v4 Only changed puz03_release01.html URLs to Wei-Hwa's original puzzle.
[2006-08-21]v5 Moved utility functions, except customized "debug()" into jw_util.js
[2006-09-13]   (minor change to puz03_release01.html) changed "jw_util.js" to "js_util.js"

*/
// Globals 
var MINVAL = 1;
var MAXVAL = 14;
var curVal;
var curLoc = new String();
var prevLoc = new Array();

// assumes a square gameboard since no separate xMIN and yMIN
var MINBOUND = new Number(0);
var MAXBOUND = new Number(4);

// arrays of possible moves along x,y coordinates. (0,0) is bottom left.
var xMove = new Array();
var yMove = new Array();
var iMove;
var mvStack = new Array();

var BLANKCELL = new String("00");
var cout = new Object();
var answer = new Object();

var dev_null;
var solved = new Boolean(false);

function main() 
{
	document.getElementById("solve_button").disabled = true;

	cout = document.getElementById("attempts");
	answer = document.getElementById("status");
	cout.innerHTML = "Number of Attempts (at each starting point for 1 of 13 sets of moves):<br />";
	
	// loop x and y to try all coordinates on gameboard as starting points:
	for (var x = MINBOUND; x <= MAXBOUND; x++) 
	{
		for (var y = MINBOUND; y <= MAXBOUND; y++) 
		{
			// puzzle has 14 different distances (possible Moves), but only 13 moves 
			// needed to finish the puzzle, so try all possible sets of 13 moves.
			for (var cutPoint = 0; cutPoint < 10; cutPoint++) 
			{
				xMove = [4, 4, 4, 3, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1];
				yMove = [4, 3, 2, 3, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0];
	            //index: 0  1  2  3  4  5  6  7  8  9  0  1  2  3 
			   //curVal 14 13 12 11 10  9  8  7  6  5  4  3  2  1
				dev_null = xMove.splice(cutPoint, 1);
				dev_null = yMove.splice(cutPoint, 1);
				curLoc = x.toString() + y.toString();
				if (isValid(curLoc) == true) 
				{
					curVal = MAXVAL;
					prevLoc.length = 0;
					iMove = 0;
					setCell(curLoc, curVal);
					mvStack.push(getMoves(xMove[iMove], yMove[iMove]));
					solve();
	}	}	}	}
	cout.innerHTML = cout.innerHTML.substr(0, cout.innerHTML.length - 7) + ". done.";
	if (solved == false)
		cout.innerHTML += '<div class="noans">No solution found.</div>';
		
	document.getElementById("reset_button").disabled = false;
}


function solve() 
{
	var counter = 0;
	while (mvStack.length != 0 && curVal > MINVAL && curVal <= MAXVAL) 
	{
		counter++;
		var loc = mvStack[mvStack.length - 1].pop();
		if (loc == null) // backtrack 
 		{
			backtrack();
			debug("BK-only", curLoc);
		} else {	// move forward
			++iMove;
			--curVal;
			setCell(loc, curVal);
			mvStack.push(getMoves(xMove[iMove], yMove[iMove]));
			debug("SET", curLoc);
		}
	}
	if (curVal == MINVAL) 
	{
		
		answer.innerHTML += "Solved!";
		solved = true;
		cout.innerHTML += '<b class="ans">' + counter + '</b>,';
	} else {
		cout.innerHTML += counter + ", &shy;";
	}
}

function getMoves(x, y) 
{
	var xA = new Number(parseInt(curLoc.substr(0, 1)) + parseInt(x));
	var yA = new Number(parseInt(curLoc.substr(1, 1)) + parseInt(y));

	var xB = new Number(parseInt(curLoc.substr(0, 1)) - parseInt(x));  // opposite direction of xA
	var yB = new Number(parseInt(curLoc.substr(1, 1)) - parseInt(y));  // opposite direction of yA

	var lMoveStack = new Array();
	lMoveStack.length = 0;
	buildStack(lMoveStack, xA, yA, xB, yB);

	// try the "flip-side" (flip x/y displacements)
	xA = parseInt(curLoc.substr(0, 1)) + parseInt(y);
	yA = parseInt(curLoc.substr(1, 1)) + parseInt(x);
	xB = parseInt(curLoc.substr(0, 1)) - parseInt(y);
	yB = parseInt(curLoc.substr(1, 1)) - parseInt(x);

	buildStack(lMoveStack, xA, yA, xB, yB);

	// remove duplicate moves
	dedupe(lMoveStack);
	return lMoveStack;
}

function buildStack(stack, xA, yA, xB, yB) 
{
	if (isValid(xA.toString() + yA.toString()) == true)
		stack.push(xA.toString() + yA.toString());

	if (isValid(xA.toString() + yB.toString()) == true)
		stack.push(xA.toString() + yB.toString());

	if (isValid(xB.toString() + yB.toString()) == true)
		stack.push(xB.toString() + yB.toString());

	if (isValid(xB.toString() + yA.toString()) == true)
		stack.push(xB.toString() + yA.toString());
}

// Ensure x,y location is within bounds AND on a valid (e.g. blank) cell
function isValid(xy) 
{
	var valid = new Boolean(false);
	if (inBounds(xy) == true) 
	{
		var ct = document.getElementById("c"+xy).firstChild;
		if (ct.nodeValue == BLANKCELL) 
			valid = true;
	}
	return valid;
}

// return true if within gameboard
function inBounds(xy) 
{
	var inside = new Boolean(false);
	if (parseInt(xy.substr(0, 1)) >= MINBOUND &&
		parseInt(xy.substr(0, 1)) <= MAXBOUND &&
		parseInt(xy.substr(1, 1)) >= MINBOUND &&
		parseInt(xy.substr(1, 1)) <= MAXBOUND) 
	{
		inside = true;
	}
	return inside;
}

// change the state (value and color) of a cell AND keep track of current position
function setCell(loc, value) 
{
	// add "c" since stupid XHTML id attributes can't begin with a number
	var cell = document.getElementById("c"+loc);
	var ct = cell.firstChild;
	ct.nodeValue = value;
	cell.style.backgroundColor = "lightgreen";
	document.getElementById("c"+curLoc).style.backgroundColor = "yellow";
	prevLoc.push(curLoc);
	curLoc = cell.id.substr(1,2);
}

// Change a cells state back to "blank" and 
// set current location back to previous location  
function unsetCell(loc) 
{
	// add "c" since stupid XHTML id attributes can't begin with a number
	var cell = document.getElementById("c"+ loc);
	var ct = cell.firstChild;
	ct.nodeValue = BLANKCELL;
	cell.style.backgroundColor = "white";
	curLoc = prevLoc.pop();
}


function backtrack() 
{
	--iMove;
	++curVal;
	unsetCell(curLoc);
	dev_null = mvStack.pop();
}

// UI toggle - lets users create their own grid
function toggle(id)
{
	var cell = document.getElementById(id);
	var txt = document.getElementById(id).firstChild;
	
	if (txt.nodeValue == BLANKCELL) // turn ON
	{
		cell.style.color = "#FFEDD6";
		cell.style.backgroundColor = "#FFEDD6";		
		txt.nodeValue = "xx";
	} else {					    // turn OFF
		cell.style.color = "black";
		cell.style.backgroundColor = "white";				
		txt.nodeValue = BLANKCELL;
	}
}

///////////////////////////////////
//// Custom Utility Functions /////
///////////////////////////////////
function debug(msg, lloc) 
{
//	if (msg == "post_SKIP" || msg == "pre_SKIP") 
//alert(msg +"\nprevLoc="+prevLoc + "\nattempt loc="+lloc  +"\ncurVal="+curVal+"\ncurLoc="+curLoc+"\niMv="+iMove+'\ndump mvStack:\n' + aDump(mvStack));
}

