/*<script>*/
/****************************************************************************
Name    : Online Sudoku
Author  : Binny V A
Version : 1.03.B
Website : http://www.geocities.com/binnyva
This code is Copyright (c) 2005 Binny V Abraham
License is granted to user to reuse this code on other Web site
if, and only if, this entire copyright notice is included. The Web Site
containing this script must be a not-for-profit(non-commercial) web site 
unless I gave permission for the use of the script.
End copyright - This must be retained and posted as is to use this script
along with a link to the original location.
****************************************************************************/

//////////////////////////////////// Global Variables /////////////////////////////////////////
var debug = 0;

var horizondal_line_width = 350;
var vertical_line_height = 370;
var default_number_per_box = 4;
var repeated_at = ""; //The ID of the location where number was repeated.
var compleated = 0;   //This will be one when the User hits the 'I give up' Button

//Create a 9x9 2 dimentional Array
var xy = new Array(9);
for(i=0;i<9;i++) {
	xy[i] = new Array(9);
}

//Base Games
var puzzles = new Array(
"abc.gdefihdeif.hagcbfgh.cibadebf.iehdcg.ae.acbf.gid.hghdiace.bficgdabhefh.b.f.cg.eaidde.ahfibcg", //14
"a.bfhiec.g.ddcg.fbai.heiehdg.cabfdegi.hc.fabai.cb.df.eghhf.be.agcidbfi.edhgcac.adgf.iheb.g.hebcaf.di", //19
"bfi.edhgcac.adgf.iheb.g.hebcaf.dia.b.fhiec.g.ddcg.fbai.heiehdg.cabfdegi.hc.fabai.cb.df.eghhf.be.agcid", //20
"ib.ad.c.g.efhe.hc.fbiagd.fg.daehcibbhcgd.faiegf.ei.ab.dchdai.hcebfgfgdcai.he.bbeahd.gc.ifih.c.e.bf.gda", //21
"aeh.dfi.cgbcf.b.e.hgiad.gidc.abfeh.fc.g.b.aeihdb.eifdha.gchda.ig.cebfg.bfhd.aeic.dcegi.f.hbaah.ibce.dfg",
"dah.iegfcbbigfd.cehae.fchab.gi.dbh.fe.gacidcedi.bhgaf.ag.i.d.cfbh.egb.ch.dea.f.ihf.e.aci.d.gbid.a.fbgceh",
"cf.b.ag.eidhhd.eif.cbaga.g.ibdh.fceg.acb.ifhe.d.dehgcafb.i.ifbe.hdg.acdc.iebg.f.haegf.ahd.cibhba.ci.f.deg",
"chfgeai.d.be.gdib.hcf.ab.iacf.dh.gefa.d.hcg.ebigic.bd.eahfeb.hfa.i.gdcd.gh.bica.fe.fei.hagd.cb.a.cbdefihg",
"adbfheig.c.icfba.gdeh.he.gcidfa.bcb.ide.ah.fgag.dh.fbei.cefhigcbd.abi.he.cd.gafg.de.fbach.i.acf.g.hid.b.e",
"ied.a.bfghchcfgeia.b.dagbh.cdi.efb.ied.ca.f.ghcga.ef.hdib.fd.hbig.cae.hdie.fg.cabb.a.e.ihcf.dggfcd.b.aehi",
"h.g.bc.ea.idfa.ci.bf.dgehfe.dih.gcbab.fi.ehg.acdh.dcfabi.geag.edc.ib.fhdah.gbe.ficebf.ci.ad.hggi.ch.df.e.ab",
".e.cadbhfgi.fbhegiad.cg.di.afch.bei.h.fba.g.cd.e.badhceif.g.ec.g.dif.b.hag.ibhe.da.fc.chagifde.bfedcabi.g.h",
"ge.c.h.i.fdabaif.bg.d.echdh.bae.cf.gic.he.fbdig.a.dbageifh.c.ifgca.hb.deb.dh.efg.acicf.e.ia.bhdggia.h.c.d.ebf",
"deb.cg.a.ihff.ah.dbi.egccgi.h.fe.bda.gcdf.bihae.a.fb.hd.ec.i.geihg.acfb.dbi.ca.d.hefgge.dbc.fi.haah.f.ie.gdcb"
);

var chosen = 0;
var this_puzzle = ""; //The currently played game's fingerprint
var orginal_game = ""; //The current puzzle

//////////////////////////////////// Cookie Functions ////////////////////////////////
var bites = document.cookie.split(";");   // This is to break cookie down into an array of bites
var sudoku_fingerprint = getCookie("sudoku_fingerprint"); 	  // retrieve all the values
if (sudoku_fingerprint == null || sudoku_fingerprint == "") sudoku_fingerprint=""; //or define default values
function getCookie(cookie) {
	for (i=0; i < bites.length; i++) {
		nextbite = bites[i].split("=");         // break into the "name & value"
		if (nextbite[0] == cookie)              // if the name = true
			return unescape(nextbite[1]);       // return this value
	}
	return null;                             // if there is no match return null value
}
var today = new Date();
var expiry = new Date(today.getTime() + 28 * 24 * 60 * 60 * 1000); // 28 days

// Set cookie with given name = value pair
function setCookie(name,value) {
	if((value!=null) && (value!="")) document.cookie = name + "=" + escape(value) + "; expires=" + expiry.toGMTString();
	bites = document.cookie.split(";"); 	  // update all the bites
}

//////////////////////////////////// Co-Ordinates Functions ///////////////////////////////
//Get the index from the given two x and y co-ordinates and return it.
//	  - Takes 2 and 3 and returns 6.
function getIndex(x,y) {
	var index = (x*3) + y - 3;
	return index;
}

//Returns the x and y co-ordinates based on the index given as argument.
//		- Takes 6 and returns 2 and 3 as an array
function getXY(index) {
	var x=1,y=1;
	switch (index) { 
		case 1: x=1; y=1; break;
		case 2: x=1; y=2; break;
		case 3: x=1; y=3; break;
		case 4: x=2; y=1; break;
		case 5: x=2; y=2; break;
		case 6: x=2; y=3; break;
		case 7: x=3; y=1; break;
		case 8: x=3; y=2; break;
		case 9: x=3; y=3; break;
	}
	var xy_coods = new Array(x,y);
	return xy_coods;
}

////////////////////////////////////// Random Number Genarators //////////////////////////////////////
//Returns a random number between 1 and 9(inclusive)
function rand() {
	var number = Math.round(Math.random()*10);
	while (number < 1 || number > 9) { //If the number is 0 or 10, get another number.
		number = Math.round(Math.random()*10);
	}
	return number; 
}

//Returns a random number(1-9) that is not in the list given as the argument
function uniqueRand(list) {
	var number = rand();

	for(var a=0;a<list.length;a++) {
		if(list[a] == number) { //If the random number was found in the list,
			number = rand(); //get a new number,
			a=-1; //and start over again.
		}
	}
	return number;
}

/////////////////////////////////////// GUI Functions ////////////////////////////////////
//Display the given data in the said colour 
function show(data,color) {
	document.getElementById("display_area").innerHTML = data + "<br />\n";
	document.getElementById("display_area").style.color = color;
}

//Display the Vertical help line
function helpLineV(line) {
	var item = document.getElementById("lv-"+line).style;
	if(item.position == "absolute") {
		item.position = "relative";
		item.height = "10px";
	} else {   
		item.position = "absolute";
		item.height = vertical_line_height + "px";
	}
}
//Display the Horizondal Help line
function helpLineH(line) {
	var item = document.getElementById("lh-"+line).style;
	if(item.position == "absolute") {
		item.position = "relative";
		item.width = "5px";
	} else {
		item.position = "absolute";
		item.width = horizondal_line_width + "px";
	}
}

//Returns false if the 'number' given as the argument appears anywhere in the previous row or column.
function checkForUnique(box,cell,number) {
	//There is nothing before 1, so we don't have to check it.
	if(box>1) {
	//All the boxes we will have to check for duplicates for this box - all boxes directly above or left of this box.
	var boxes_to_check = new Array(); 
	switch (box) {
		case 2 : boxes_to_check.push(1);break;
		case 3 : boxes_to_check.push(1,2);break;
		case 4 : boxes_to_check.push(1);break;
		case 5 : boxes_to_check.push(2,4);break;
		case 6 : boxes_to_check.push(3,4,5);break;
		case 7 : boxes_to_check.push(1,4);break;
		case 8 : boxes_to_check.push(2,5,7);break;
		case 9 : boxes_to_check.push(3,6,7,8);break;
	}

	var id="", value="";
	for(i=0;i<boxes_to_check.length;i++) {
		xy_coods = getXY(cell);
		for(j=1; j<=3; j++) {
			//Horizondal Checks
			if(box==2 || box==3 || box==5 || box==6 || box==8 || box==9) { 
				//Nullifier conditions - don't do the checking if the following conditions are true.
				if(box==5 && boxes_to_check[i]==2);
				else if(box==6 && boxes_to_check[i]==3);
				else if(box==8 && (boxes_to_check[i]==2 || boxes_to_check[i]==5));
				else if(box==9 && (boxes_to_check[i]==3 || boxes_to_check[i]==6));
				else {
					//Check the data for redundency here.
					cell_to_check = getIndex(xy_coods[0],j);
					id = "c" + boxes_to_check[i] + cell_to_check; //Get the id of the cells that must be checked.
					value = document.getElementById(id).value;

					if(value == number) {
						repeated_at = id;
						return false; //There is number repeatation - return with error.
					}
				}
			}

			//Verical Checks
			if(box>=4) {
				//Nullifier conditions - don't do the checking if the following conditions are true.
				if(box==5 && (boxes_to_check[i]==4)); //The 5th box should only check the 2nd Box in vertical mode
				else if(box==6 && (boxes_to_check[i]==4 || boxes_to_check[i]==5));
				else if(box==8 && boxes_to_check[i]==7);
				else if(box==9 && (boxes_to_check[i]==7 || boxes_to_check[i]==8));
				else {
					cell_to_check = getIndex(j,xy_coods[1]);
					id = "c" + boxes_to_check[i] + cell_to_check; //Get the id of the cells that must be checked.
					value = document.getElementById(id).value;

					if(value == number) {
						repeated_at = id;
						return false; //There is number repeatation - return with error.
					}
				}
			}
		}
	}
	}
	
	//Check within the box
	for(k=1;k<=9;k++) {
		id = "c" + box + k;
		if(number == document.getElementById(id).value && k != cell) { //If the nubmer is found at some other cell
			repeated_at = id;
			return false;
		}
	}

	return true;
}

//Insert the value given as the 'value' argument into the entry by the id of 'id'
function insert(id,value) {
	document.getElementById(id).value = value
	if(value)
		document.getElementById(id).disabled = true; //Make it uneditable
	else 
		document.getElementById(id).disabled = false; //Make the fomerly uneditable cells editable
}

//Clear all the fields - and enable all the disabled cells
function clearer() {
	var id = ""
	document.f.reset(); //Clear the fields
	for(var i=1; i<=9; i++) {
		for(var j=1; j<=9; j++) {
			id = "c"+i+j;
			document.getElementById(id).disabled = false;
		}
	}
}
//Change the background color of cells to white
function discolorCells(cell1,cell2) {
	document.getElementById(cell1).style.background = "#fff";
	document.getElementById(cell2).style.background = "#fff";
}

//Records the values of all box and cells by coping all values to the 'xy' array.
function recordPosition() {
	var value=0;
	for(a=0;a<9;a++) {
		for(b=0;b<9;b++) {
			value = document.getElementById("c"+(a+1)+(b+1)).value;
			if(!isNaN(value) && value.length==1) {
				xy[a][b] = value;
			} else {
				xy[a][b] = 0;
			}
		}
	}
}

//Convert the current position to a string that we can save and use latter on. This function will create a 
//	finger print for this game and will save that as a Cookie. The finger print will have two types of data...
//		Number(1-9) - The number for that cell. If the number is 3, a 3 digit will be entered there.
//		Aphabet(a-z)- The number of empty cells that must be left before the next number is inserted.
//Argument : Action - 1 = Record the game to a database. 
//					- 0 = Just get the fingerprint of the game and return it 
function pos2str(action) {
	var str = "";
	var zero_count = 0;
	var alpha = " abcdefghijklmnopqrstuvwxyz";

	if(action) {
		recordPosition(); //Get the position in the array
	}

	for(a=0;a<9;a++) {
		for(b=0;b<9;b++) {
			if(xy[a][b]) {
				if(zero_count) {
					str += alpha.charAt(zero_count); //Include the number of continous zeros - a means 1 zero, b means 2 etc.
					zero_count = 0;
				}
				str += xy[a][b];
			} else {
				zero_count++; //Count the empty places
			}
		}
	}

	return str;
}

//Convert the given string to a position that can be displayed on the board 
//Argument : str 	- FINGERPRINT = Use this fingerprint to create the game. 
function str2pos(str) {
	var zero_flag = 0;
	var alpha = "abcdefghijklmnopqrstuvwxyz";
	var pos = 0;
	//The id of the current cell will be calculated form these varabales. 
	var a = 0;
	var b = 0;
	str = str.replace(/\./g,"");

	while(a<9) {
		ch = str.charAt(pos);
		id = "c" + (a+1) + (b+1);

		if(!isNaN(ch) && !zero_flag) {
			insert(id,ch);
		} else if(zero_flag) {
			insert(id,"");
			zero_flag--;
		} else {
			insert(id,"");
			zero_flag = alpha.indexOf(ch);
		}

		//Get next number if the zero flag is not up
		if(!zero_flag) {
			pos++;
		}

		//Update the positions
		b++;
		if(b>=9) {
			b=0;
			a++;
		}
	}
}

//Create a string we will put into the cookie - a little different for the pos2str()
//		- This format will have the values of all cells sperated by a ';' char. The provided 
//			numbers will be prefixed with a dot like - '.5'
function makeCookieString() {
	var str = "";
	var puzzle_cells = new Array(0);

	for(a=0;a<9;a++) {
		for(b=0;b<9;b++) {
			value = document.getElementById("c"+(a+1)+(b+1)).value;
			if(document.getElementById("c"+(a+1)+(b+1)).disabled) {
				value = "."+value;
			}
			puzzle_cells.push(value);
		}
	}
	str = puzzle_cells.join(';');
	//alert(str);

	return str;
}

//Save the Cookie Format string created with the makeCookieString() function to a cookie
function save() {
	var str = makeCookieString();
	setCookie("sudoku_fingerprint",str);
	alert("Game saved.");
}
//Load the game from the cookie string
function load() {
	//Clear the previous data
	clearer();

	//Get the game fingerprint from the Cookie.
	var str = getCookie("sudoku_fingerprint");
	if(str == "" || str == null) {
		alert("No saved games found!")
		return false;
	}

	var index = 0;
	var puzzle_cells = str.split(";");
	for(a=0;a<9;a++) {
		for(b=0;b<9;b++) {
			id = "c"+(a+1)+(b+1);//The cell location

			number = puzzle_cells[index];
			if(number.charAt(0) == ".") { //If there is a '.' char, it ia a provided number
				number = number.charAt(1);
				document.getElementById(id).disabled = true;
			}

			if(number) {
				//alert(id + " = " + number);
			}

			document.getElementById(id).value = number;
			index++;
		}
	}
}

//Check all the Rows/Cols for a repeated number
function checker() {
	var found = 0;
	
	show("Checking game...","#a84efa");
	loop:
	for(a=1;a<=9;a++) {
		for(b=1;b<=9;b++) {
			id = "c"+a+b;
			value = document.getElementById(id).value
			if(isNaN(value) || value > 9 || value < 1) {
				if(value == "") {
					show("Empty cells found.","#d78601");
				} else {
					show("Invalid entries found.","#d78601");
				}
				found = 1;

				document.getElementById(id).style.background = "red"; //Repated number found here.
				setTimeout("discolorCells(id,id)",2000);
				
				break loop;
			}
			else if(value) {
				if(!checkForUnique(a,b,value)) {
					document.getElementById(id).style.background = "red"; //Repated number found here.
					document.getElementById(repeated_at).style.background = "red"; //and here.

					setTimeout("discolorCells(repeated_at,id)",2000);//Change the background back to white after 2 secs
					found++;
					break loop;
				}
			} else {
				show("Empty cells were found. Please complete the puzzle.","#d78601");
				document.getElementById(id).style.background = "red"; //Empty
				setTimeout("discolorCells(id,id)",2000);				
				found = 1;
				break loop;
			}  
		}
	}

	if(!found) {
		if(compleated)
			alert("Sorry - you can't win after giving up. But it is solved.");
		else
			alert("Congratulations - You have completed the puzzle.");
		show("Game Over","#000000"); 
	}
}

//Clear all the unwanted cells.
function reloadGame() {
	str2pos(orginal_game); //Use that to rebuild the game
}

//Solve the game
function solve() {
	show("Finding the solution to the game...");
	if(confirm("This will automaticaly solve the puzzle for you.\nAre you sure you want to do this?\n")) {
		str2pos(this_puzzle);
		compleated = 1;
		show("Solved the puzzle.");
	}
}

//This will make new puzzles but replacing all numbers in a base game with other numbers - in effect creating
//	a entirely new game. This function is sneaky - Big Time
function makeNewOrder(str) {
	//Inits
	var alpha = " abcdefghijklmnopqrstuvwxyz";
	var new_order = "";

	//Get random numbers for all alphas - and store it in a array.
	numbers = new Array("0");
	for(j=0;j<9;j++) {
		new_numbers = uniqueRand(numbers); //Give random position for the numbers
		numbers.push(new_numbers);
	}

	//Now change all the alphas back to numbers - with new digits
	for(i=0;i<str.length;i++) {
		if(str.charAt(i)=="." || str.charAt(i)=="*" || str.charAt(i)=="x" || 
				str.charAt(i)=="_" || str.charAt(i)=="-" || str.charAt(i)=="+") { //It is a special char
			new_order += "."
		} else {
			new_order += numbers[alpha.indexOf(str.charAt(i))]
		}
	}

	return new_order;	
}

//Creates the puzzle
function init() {
	chosen = Math.floor((Math.random()*10) / (10/puzzles.length)); //'chosen' puzzle should be random
	chosen = 2;
	this_puzzle = makeNewOrder(puzzles[chosen]);
	
	//Clear the existing numbers first
	compleated = 0;
	clearer();

	var last_box_ended_at = 0;
	var extra_number_count = 0;
	for(var i=0;i<9;i++) { //Change 1 to 9 :DEBUG:
		//Initialisations
		var b=0,location_of_fixed_number=0;
		var arr_b = new Array();

		//Get the numbers for this box from the 'this_puzzle' varaible
		var limit = 9;
		var this_box = "";
		var dot_count = 0;
		for(var j=last_box_ended_at; j<last_box_ended_at+limit; j++) {
			if(this_puzzle.charAt(j) == ".") {
				limit++;
				dot_count++;
			}
			this_box = this_box + this_puzzle.charAt(j);
		}
		last_box_ended_at = last_box_ended_at + limit;
		
		//Decide how much numbers must appear in this box
		number_of_numbers = default_number_per_box - extra_number_count;
		extra_number_count = 0;
		
		// 'extra_number_count' is used for reducing the number of populated cells in the next box if 
		//		the current box has more than 4 numbers.
		
		//If the number of dots are more than 4, use it
		if (dot_count > number_of_numbers) {
			if(rand() > 5) {
				extra_number_count = dot_count - number_of_numbers;
			} 
			number_of_numbers = dot_count;
		}
		

		//Empty the array.
		arr_b = new Array();
		//Get the positions in the box and insert the numbers there
		for(b=0;b<number_of_numbers;b++) {
			location_of_fixed_number = this_box.indexOf(".");

			if(location_of_fixed_number + 1) {//If there are dots...
				//Remove this dot
				this_box =  this_box.substring(0,location_of_fixed_number) +
							this_box.substring(location_of_fixed_number+1,this_box.length);
			
			} else { //No more dots - get some random locations
				location_of_fixed_number = uniqueRand(arr_b); //Give random position for the numbers
				location_of_fixed_number--;//uniqueRand gives 1-9. We need it from 0
			}

			arr_b.push(location_of_fixed_number+1);//Put the number into the don't repeat array

			//Get the numbers that should be inserted
			insertion_number = this_box.charAt(location_of_fixed_number);

			location_of_fixed_number++;//We need this to start from 1 - not from 0

			id = "c" + (i+1) + location_of_fixed_number

			insert(id,insertion_number); //Show the numbers
		}
	}
	orginal_game = pos2str(2);//Save the game before beginning - for restarting the puzzle
}
window.onload=init;