/*
	tettadom.js - JavaScript source file of tettadom
	Copyright (C) 2006 vaccha

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	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.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

	http://tetta.sourceforge.net/
	mailto:vaccha@users.sourceforge.net

	This line is reserved for my love - Wu Shan-Yun
*/
var cells = new Array(256), completes = new Array(98), maxmemory, maxmoves, memory, moves, pause, steps = new Array(1024), success, time, totalmoves = new Array(98), totaltime = new Array(98), zeros = new Array(1024);
function toradix(number,digits,radix) {
	for (var ret = ""; --digits > 0 && number < Math.pow(radix,digits); ret += "0")
		;
	return ret + number.toString(radix); }
function showtime() {
	if (pause || success && moves == maxmoves)
		return;
	if (++time >= 32 * Math.pow(order,2.5)) {
		alert(timeup);
		replay(); }
	else
		_time.value = (order > 6 ? Math.floor(time / 3600) + ":" : "") + (time % 3600 < 600 && order > 3 ? "0" : "") + Math.floor(time / 60 % 60) + (time % 60 < 10 ? ":0" : ":") + time % 60; }
function issuccess() {
	for (var fhs = 0, fhd = 0, bhs = 0, bhd = 0, fvs = 0, fvd = 0, bvs = 0, bvd = 0, all = (1 << order) - 1, i = order; --i >= 0; ) {
		for (var fh = 0, bh = 0, fv = 0, bv = 0, j = order; --j >= 0; ) {
			fh |= 1 << Math.floor(cells[i * order + j] / order);
			bh |= 1 << cells[i * order + j] % order;
			fv |= 1 << Math.floor(cells[j * order + i] / order);
			bv |= 1 << cells[j * order + i] % order; }
		fhs += 32768 % fh == 0;
		fhd += fh == all;
		bhs += 32768 % bh == 0;
		bhd += bh == all;
		fvs += 32768 % fv == 0;
		fvd += fv == all;
		bvs += 32768 % bv == 0;
		bvd += bv == all; }
	return success = rules & (fhs + bvs == (all = order * 2) | (fvs + bhs == all) << 1 | (fhs + bvd == all) << 2 | (fvs + bhd == all) << 3 | (fhd + bvs == all) << 4 | (fvd + bhs == all) << 5 | (fhd + fvd + bhd + bvd == all * 2) << 6); }
function showmoves() {
	_moves.value = order < 6 ? toradix(moves,2,10) : order < 16 ? toradix(moves,3,10) : toradix(moves,4,10);
	_start.disabled = _back.disabled = moves == 0;
	_end.disabled = _fore.disabled = moves == maxmoves;
	_fastfore.disabled = moves >= steps[maxmemory];
	_fastback.disabled = maxmemory == 0 || moves <= steps[1];
	for (memory = maxmemory; steps[memory] > moves; memory--)
		;
	_memorize.disabled = moves == steps[memory]; }
function showtetta() {
	showmoves();
	for (var _cell, k = order * order; --k >= 0; ) {
		_cell = _tetta.rows[Math.floor(k / order)].cells[k % order]
		_cell.firstChild.className = "c" + Math.floor(cells[k] / order).toString(16);
		_cell.lastChild.className = "c" + (cells[k] % order).toString(16); } }
function swap(k,k0) {
	showmoves();
	var _cell = _tetta.rows[Math.floor(k / order)].cells[k % order], _cell0 = _tetta.rows[Math.floor(k0 / order)].cells[k0 % order]; 
	_cell0.firstChild.className = _cell.firstChild.className;
	_cell0.lastChild.className = _cell.lastChild.className;
	_cell.lastChild.className = _cell.firstChild.className = "c0";
	cells[k0] = cells[k];
	cells[k] = 0; }
function move() {
	if (pause || success && moves == maxmoves)
		return;
	var i = this.parentNode.rowIndex, j = this.cellIndex, k = i * order + j, k0 = zeros[moves];
	if (k == k0) {
		if (moves > 0)
			back(); }
	else if (i == Math.floor(k0 / order) || j == k0 % order) {
		if (++moves >= 4 * order * order) {
			alert(toomany);
			replay();
			return; }
		maxmemory = memory;
		swap(zeros[maxmoves = moves] = k,k0);
		if (issuccess()) {
			var rule = Math.round(Math.LOG2E * Math.log(success)), cell = rule * 14 + order - 3;
			completes[cell]++;
			totalmoves[cell] += moves;
			totaltime[cell] += time;
			var cookie = "record=", date = new Date();
			for (i = 0; i < 98; i++)
				cookie += toradix(completes[i],4,16) + toradix(totalmoves[i],5,16) + toradix(totaltime[i],6,16);
			date.setTime(date.getTime() + expirerecord);
			document.cookie = cookie + "; expires=" + date.toUTCString();
			var complete, averagemoves, averagetime;
			if (rule < 2) {
				rule = 1;
				cell = order - 3;
				complete = completes[cell] + completes[cell + 14];
				averagemoves = totalmoves[cell] + totalmoves[cell + 14];
				averagetime = totaltime[cell] + totaltime[cell + 14]; }
			else if (rule < 6) {
				rule = 2; 
				cell = order + 25;
				complete = completes[cell] + completes[cell + 14] + completes[cell + 28] + completes[cell + 42];
				averagemoves = totalmoves[cell] + totalmoves[cell + 14] + totalmoves[cell + 28] + totalmoves[cell + 42];
				averagetime = totaltime[cell] + totaltime[cell + 14] + totaltime[cell + 28] + totaltime[cell + 42]; }
			else {
				rule = 3; 
				cell = order + 81;
				complete = completes[cell];
				averagemoves = totalmoves[cell];
				averagetime = totaltime[cell]; }
			var _row = _record.rows[order - 1];
			_row.cells[rule].firstChild.nodeValue = complete.toString();
			_row.cells[rule + 3].firstChild.nodeValue = (averagemoves / complete).toPrecision(3);
			_row.cells[rule + 6].firstChild.nodeValue = (averagetime / complete).toPrecision(3);
			alert(congrat); } } }
function fore() {
	moves++;
	swap(zeros[moves],zeros[moves - 1]); }
function back() {
	moves--;
	swap(zeros[moves],zeros[moves + 1]) }
function memorize() {
	_memorize.disabled = true;
	for (var i = maxmemory++; i > memory; i--)
		steps[i + 1] = steps[i];
	steps[++memory] = moves; }
function fastback() {
	for (memory -= steps[memory] == moves; moves > steps[memory]; moves--)
		cells[zeros[moves]] = cells[zeros[moves - 1]];
	cells[zeros[moves]] = 0;
	showtetta(); }
function fastfore() {
	for (++memory; moves < steps[memory]; moves++)
		cells[zeros[moves]] = cells[zeros[moves + 1]];
	cells[zeros[moves]] = 0;
	showtetta(); }
function start() {
	for (; moves > 0; moves--)
		cells[zeros[moves]] = cells[zeros[moves - 1]];
	cells[zeros[moves]] = 0;
	showtetta(); }
function end() {
	for (; moves < maxmoves; moves++)
		cells[zeros[moves]] = cells[zeros[moves + 1]];
	cells[zeros[moves]] = 0;
	showtetta(); }
function replay() {
	for (; moves > 0; moves--)
		cells[zeros[moves]] = cells[zeros[moves - 1]];
	time = success = steps[0] = pause = cells[zeros[moves]] = maxmoves = maxmemory = 0;
	showtetta(); }
function changerules() {
	rules = this.options[0].selected * 3 | this.options[1].selected * 60 | this.options[2].selected * 64;
	replay(); }
function resume() {
	if (pause = !pause)
		_start.disabled = _fastback.disabled = _back.disabled = _memorize.disabled = _fore.disabled = _fastfore.disabled = _end.disabled = true;
	else
		showmoves(); }
function createtetta() {
	while (_tetta.rows.length)
		_tetta.deleteRow(0);
	for (var _row, i = 0; i < order; i++) {
		_row = _tetta.insertRow(i);
		for (var _cell, j = 0; j < order; j++) {
			(_cell = _row.insertCell(j)).onclick = move;
			_cell.appendChild(document.createElement("span")).appendChild(document.createTextNode(top));
			_cell.appendChild(document.createElement("br"));
			_cell.appendChild(document.createElement("span")).appendChild(document.createTextNode(bottom)); } } }
function newgame() {
	do {
		for (var k = order * order; --k >= 0; )
			cells[k] = 0;
		for (var i = order * order; i; ) {
			var j = Math.floor(Math.random() * i--);
			for (k = 0; ; k++)
				if (cells[k] == 0)
					if (--j < 0) {
						cells[k] = i;
						break; } } }
	while (issuccess());
	zeros[0] = k;
	time = steps[0] = pause = moves = maxmoves = maxmemory = 0;
	showtetta(); }
function newtetta() {
	createtetta();
	newgame(); }
function changeorder() {
	order = this.selectedIndex + 3;
	newtetta(); }
// function load() {
	_tetta = document.getElementById("tetta");
	(_start = document.getElementById("start")).onclick = start;
	_start.value = _start_;
	(_fastback = document.getElementById("fastback")).onclick = fastback;
	_fastback.value = _fastback_;
	(_back = document.getElementById("back")).onclick = back;
	_back.value = _back_;
	(_memorize = document.getElementById("memorize")).onclick = memorize;
	_memorize.value = _memorize_;
	(_fore = document.getElementById("fore")).onclick = fore;
	_fore.value = _fore_;
	(_fastfore = document.getElementById("fastfore")).onclick = fastfore;
	_fastfore.value = _fastfore_;
	(_end = document.getElementById("end")).onclick = end;
	_end.value = _end_;
	(_time = document.getElementById("time")).onclick = resume;
	(_moves = document.getElementById("moves")).onclick = replay; 
	document.getElementById("new").onclick = newgame;
	document.getElementById("new").value = _new_;
	document.getElementById("save").onclick = save;
	document.getElementById("save").value = _save_;
	document.getElementById("about").onclick = about;
	document.getElementById("about").value = _about_;
	(_order = document.getElementById("order")).onchange = changeorder;
	(_rules = document.getElementById("rules")).onchange = changerules;
	_order.remove(0);
	for (i = 3; i < 17; i++) {
		(_option = document.createElement("option")).appendChild(document.createTextNode(i.toString()));
		_order.appendChild(_option); }
	_record = document.getElementById("record");
	if ((i = document.cookie.indexOf("record=")) != -1) {
		cookie = document.cookie.substring(i + 7);
		for (j = 0; j < 98; j++) {
			completes[j] = parseInt(cookie.substring(j * 15,j * 15 + 4),16);
			totalmoves[j] = parseInt(cookie.substring(j * 15 + 4,j * 15 + 9),16);
			totaltime[j] = parseInt(cookie.substring(j * 15 + 9,j * 15 + 15),16); } }
	else
		for (i = completes.length; --i >= 0; )
			totaltime[i] = totalmoves[i] = completes[i] = 0;
	for (i = 3; --i >= 0; )
		for (j = 1; j < 4; j++)
			_record.rows[1].appendChild(document.createElement("th")).appendChild(document.createTextNode(j.toString()));
	for (i = 0; i < 14; i++) {
		(_row = _record.insertRow(i + 2)).appendChild(document.createElement("th")).appendChild(document.createTextNode((i + 3).toString()));
		for (j = 1; j < 10; j++)
			_row.insertCell(j);
		_row.cells[1].appendChild(document.createTextNode((complete = completes[i] + completes[i + 14]).toString()));
		_row.cells[4].appendChild(document.createTextNode((complete > 0 ? (totalmoves[i] + totalmoves[i + 14]) / complete : 0).toPrecision(3)));
		_row.cells[7].appendChild(document.createTextNode((complete > 0 ? (totaltime[i] + totaltime[i + 14]) / complete : 0).toPrecision(3)));
		_row.cells[2].appendChild(document.createTextNode((complete = completes[i + 28] + completes[i + 42] + completes[i + 56] + completes[i + 70]).toString()));
		_row.cells[5].appendChild(document.createTextNode((complete > 0 ? (totalmoves[i + 28] + totalmoves[i + 42] + totalmoves[i + 56] + totalmoves[i + 70]) / complete : 0).toPrecision(3)));
		_row.cells[8].appendChild(document.createTextNode((complete > 0 ? (totaltime[i + 28] + totaltime[i + 42] + totaltime[i + 56] + totaltime[i + 70]) / complete : 0).toPrecision(3)));
		_row.cells[3].appendChild(document.createTextNode((complete = completes[i + 84]).toString()));
		_row.cells[6].appendChild(document.createTextNode((complete > 0 ? totalmoves[i + 84] / complete : 0).toPrecision(3)));
		_row.cells[9].appendChild(document.createTextNode((complete > 0 ? totaltime[i + 84] / complete : 0).toPrecision(3))); }
	if ((i = document.cookie.indexOf("tetta=")) != -1) {
		cookie = document.cookie.substring(i + 6);
		order = parseInt(cookie.substring(0,1),16) + 3;
		cookie = cookie.substring(1);
		for (j = 0; j < order * order; j++)
			cells[j] = parseInt(cookie.substring(j * 2,j * 2 + 2),16);
		cookie = cookie.substring(j * 2);
		maxmoves = parseInt(cookie.substring(0,3),16);
		cookie = cookie.substring(3);
		for (j = 0; j <= maxmoves; j++)
			zeros[j] = parseInt(cookie.substring(j * 2,j * 2 + 2),16);
		cookie = cookie.substring(j * 2);
		maxmemory = parseInt(cookie.substring(0,3),16);
		cookie = cookie.substring(3);
		for (j = 0; j <= maxmemory; j++)
			steps[j] = parseInt(cookie.substring(j * 3,j * 3 + 3),16);
		cookie = cookie.substring(j * 3);
		time = parseInt(cookie.substring(0,4),16);
		moves = parseInt(cookie.substring(4,7),16);
		memory = parseInt(cookie.substring(7,10),16);
		rules = parseInt(cookie.substring(10,12),16);
		pause = false;
		issuccess();
		createtetta();
		showtetta(); }
	else
		newtetta();
	setInterval(showtime,1000); 
	_rules.options[0].selected = (rules & 3) == 3;
	_rules.options[1].selected = (rules & 60) == 60;
	_rules.options[2].selected = (rules & 64) == 64;
	_order.selectedIndex = order - 3; // }
function save() {
	var cookie = "tetta=" + (order - 3).toString(16);
	for (var i = 0; i < order * order; i++)
		cookie += toradix(cells[i],2,16);
	cookie += toradix(maxmoves,3,16);
	for (i = 0; i <= maxmoves; i++)
		cookie += toradix(zeros[i],2,16);
	cookie += toradix(maxmemory,3,16);
	for (i = 0; i <= maxmemory; i++)
		cookie += toradix(steps[i],3,16);
	cookie += toradix(time,4,16) + toradix(moves,3,16) + toradix(memory,3,16) + toradix(rules,2,16);
	var date = new Date();
	date.setTime(date.getTime() + expiretetta);
	document.cookie = cookie + "; expires=" + date.toUTCString(); }
function about() {
	alert("About tettadom:\nCopyright \u00A9 2006 vaccha\nReleased under GPL Version 2\na Puzzle Game with Graeco-Latin Squares\nin Memory of Leonhard Euler\nhttp://tetta.sourceforge.net/"); }