	//game by benjamin bach
	//license: gpl
	//questions: benjamin___('-@-')___overtag___dot___dk

	
	//The reason for all these arrays? To hardcode information so that no on-the-fly calculations are needed within loops.
	
	var arr_sudo_index;			//Array(Array(possible values), index_column, index_row, index_region)
	var arr_sudo_solutions;		//an array containing solutions to each field
	var arr_sudo_input_fields;	//Array of input fields.
	
	var arr_sudo_columns;	//a convenient array of columns Array( columns => Array(index_values))
	var arr_sudo_rows;		//a convenient array of rows Array( rows => Array(index_values))
	var arr_sudo_regions;	//a convenient array of regions Array( regions => Array(index_values))
	
	var arr_sudo_elimated_columns;	//keep track of what numbers that have been eliminated so
	var arr_sudo_elimated_rows;		//we won't have to do that more than necessary
	var arr_sudo_elimated_regions;
	
	var i_sudo_size;			//size of the game
	var i_sudo_field_no;		//total number of fields
	var obj_sudo_placeholder;	//HTML Element with the game board
	
	var bol_sudo_show_info	= true;		//echo information about highlighted field
	var bol_sudo_test_input	= true;		//echo information about highlighted field

	var i_sudo_field_cnt;

	//Create HTML content and the array of values
	function sudo_init(i_size) {
	
		obj_sudo_placeholder = document.getElementById('sudo_gameplate')
		i_sudo_size		= i_size;	//Set global size of board
		i_sudo_field_no	= Math.pow(i_sudo_size, 4);
		arr_sudo_solutions		= sudo_init_solution_array();
		sudo_init_html();
		sudo_init_arrays();

		//Write solutions for each field that has input.value
		sudo_reset_solutions();
		
	}
	
	//Initialize all the arrays (except the solutions array)
	function sudo_init_arrays() {
	
		//Initialize global arrays
		arr_sudo_index				= new Array();
		arr_sudo_columns			= new Array();
		arr_sudo_rows				= new Array();
		arr_sudo_regions			= new Array();
		
		//Create the array for all fields in the game board
		for (i = 0; i < i_sudo_field_no; i++) {
			
			column_index = i % (i_sudo_size * i_sudo_size);
			row_index = Math.floor( i / (i_sudo_size * i_sudo_size) );
			region_index = (Math.floor(row_index / i_sudo_size)) * i_sudo_size + Math.floor(column_index / i_sudo_size);
			
			//[1] => Column membership
			//[2] => Row membership
			//[3] => Region membership
			arr_sudo_index[i] = Array(column_index, row_index, region_index);
			
			//Create sub-arrays if they don't exist!
			
			if (typeof(arr_sudo_columns[column_index])	!= 'object') arr_sudo_columns[column_index]	= new Array();
			if (typeof(arr_sudo_rows[row_index])		!= 'object') arr_sudo_rows[row_index]		= new Array();
			if (typeof(arr_sudo_regions[region_index])	!= 'object') arr_sudo_regions[region_index]	= new Array();
			
			arr_sudo_columns[column_index][arr_sudo_columns[column_index].length]	= i;
			arr_sudo_rows[row_index][arr_sudo_rows[row_index].length]				= i;
			arr_sudo_regions[region_index][arr_sudo_regions[region_index].length]	= i;
		}
		
	
	}
	
	function sudo_init_html() {
		
		//Write the HTML data of the game
		str_html = '<form><table cellpadding="0" cellspacing="0" class="sudotable" border="0"><form id="sudo_form">';
		for (i = 0; i < i_sudo_size * i_sudo_size; i++) {
						
			if (i > 0 && i % i_sudo_size == 0) {
				str_html += '<tr><td colspan="' + (i_sudo_size * i_sudo_size + i_sudo_size - 1) + '" class="hsep"></td></tr>'
			}
			
			str_html += '<tr>';
			for (row = 0; row < i_sudo_size * i_sudo_size; row++) {
				
				if (row > 0 && row % i_sudo_size == 0) {
					str_html += '<td class="vsep"></td>'
				}
				
				//The index of the field
				i_field_index = i * i_sudo_size * i_sudo_size + row;
				str_html +=	'<td><input type="text" class="sudo_field"' +
							' onkeydown="old_val = this.value"' +
							' onkeyup="sudo_input_change(true, ' + i_field_index + ', this.value)"' +
							' onfocus="sudo_field_hightlight(' + i_field_index + ')"' +
							' onblur="sudo_field_blur(' + i_field_index + ')"' +
							' /></td>';
			}
			str_html += '</tr>';
		}
		str_html += '</table></form>';

		//Put the HTML in the placeholder
		obj_sudo_placeholder.innerHTML = str_html;
		
		//Find the input fields...
		arr_sudo_input_fields = obj_sudo_placeholder.getElementsByTagName('input');

	}
	
	function sudo_init_solution_array() {
		var arr_fields = new Array(i_sudo_field_no);
		//Write down ALL possible solutions
		for (var i = 0; i < i_sudo_field_no; i++) {
			var arr_solutions = new Array();
			for (a = 0; a < i_sudo_size * i_sudo_size; a++) {
				arr_solutions[a] = a;
			}
			//[0] => Solutions possible
			//[1] => Has been eliminated = false
			arr_fields[i] = [arr_solutions, false];
		}
		return arr_fields;
	}
	
	//Create a new game.
	//int_max_fields = maximum moves to solve the game (0-100 value)
	
	//The theory of this function:
	//Place a number. Then eliminate as much as possible.
	//Place a new number from the solution arrays.
	//Continue a number of times.
	//After a certain amount of numbers placed, start figuring out how many
	//moves it takes to solve the game. If it suddenly can't be solved, then start all over.
	//Otherwise stop when the number of moves has been reached and the game is solveable.
	function sudo_create(i_filled_fields_fac) {
	
		if (i_sudo_size > 3 && !confirm('No es posible')) {
			return;
		}
	
		setTimeout('sudo_create_do(' + i_filled_fields_fac + ')', 100);
	
	}
	
	function sudo_create_do(i_filled_fields_fac) {
		
		var bol_game_solvable = false;
		var i_filled_fields			= Math.pow(i_sudo_size, 3) + Math.round(i_sudo_size * i_filled_fields_fac / 10);
		var i_min_filled_fields	= 1; //Minimum number of moves before we start eliminating fields.
		
		while (!bol_game_solvable) {
		
			sudo_init_arrays();
		
			arr_sudo_solutions		= sudo_init_solution_array();
			var i_cnt				= 0;
			var arr_empty_fields	= new Array(i_sudo_field_no); //Keep track of empty fields
			var arr_filled_fields	= new Array();
			
			for (i = 0, i_length = arr_empty_fields.length; i < i_length; i++) arr_empty_fields[i] = i; //Insert field numbers.
			
			while (
				i_cnt < i_min_filled_fields && sudo_eliminate(true)
			) {
			
				//Find a random field in the array with empty fields
				i_rnd_field_index = sudo_random(0, arr_empty_fields.length);
				
				//Put a random number from the solutions array in the empty field
				i_field_index 				= arr_empty_fields[i_rnd_field_index];
				i_rnd_number_solu_index		= sudo_random(0, arr_sudo_solutions[i_field_index][0].length - 1);
				i_rnd_number_put_in_field	= arr_sudo_solutions[i_field_index][0][i_rnd_number_solu_index];
			
				arr_sudo_solutions[i_field_index][0] = [i_rnd_number_put_in_field];
				arr_filled_fields[arr_filled_fields.length] = i_field_index;
			
				//Remove this field from the empty fields array
				arr_empty_fields = sudo_array_delete(arr_empty_fields, i_rnd_field_index);
				
				//Remove the number from corresponding rows/columns/regions
				sudo_eliminate_solution(i_field_index, i_rnd_number_put_in_field, true);
			
				i_cnt++;
				
				if (i_cnt == i_min_filled_fields && !(bol_game_solvable = sudo_solve(true, true))) i_min_filled_fields++;
				
			}
		}
		//Do it all again if the game couldn't be solved...
		var arr_filled_fields_new = arr_filled_fields;
		var i_filled_cnt = 0;
		var bol_donttesttherest = false;
		
		for (var i = 0, i_length = arr_filled_fields.length; i < i_length; i++) {
			if (arr_filled_fields_new.length <= i_filled_fields) {
				bol_donttesttherest = true;
			}
			//Store the old solutions array
			var arr_old = arr_sudo_solutions;
			//Store the old filled fields and try out one without this member.
			var arr_filled_fields_old = arr_filled_fields_new;
			if (!bol_donttesttherest) {
				arr_filled_fields_new = sudo_array_delete_value(arr_filled_fields_new, arr_filled_fields[i]);
				arr_sudo_solutions = sudo_init_solution_array();
				for (var a = 0, a_length = arr_filled_fields_new.length; a < a_length; a++) {
						arr_sudo_solutions[arr_filled_fields_new[a]][0] = arr_old[arr_filled_fields_new[a]][0];
						sudo_eliminate_solution(arr_filled_fields_new[a], arr_old[arr_filled_fields_new[a]][0][0], true);
				}
			}
			//If the new solutions array can't be solved we'll rollback to the old arrays
			//And fill the field
			if (bol_donttesttherest || !sudo_solve(true)) {
				arr_sudo_solutions = arr_old;
				arr_filled_fields_new = arr_filled_fields_old;
				arr_sudo_input_fields[arr_filled_fields[i]].value = sudo_translate_input(arr_sudo_solutions[arr_filled_fields[i]][0][0], true);
				arr_sudo_input_fields[arr_filled_fields[i]].readOnly = "readonly";
				arr_sudo_input_fields[arr_filled_fields[i]].style.color = '#000000';
			}
		}
		arr_sudo_solutions = sudo_init_solution_array();
		sudo_eliminate_human(true);
		sudo_status('Listo!');
	}
	
	function sudo_array_delete(arr_perfom_in, i_member) {
	
		var arr_return = new Array();
		for (i = 0, i_length = arr_perfom_in.length; i < i_length; i++) {
			if (i != i_member) arr_return[arr_return.length] = arr_perfom_in[i];
		}
		return arr_return;
	
	}
	
	function sudo_array_delete_value(arr_perfom_in, i_value) {
	
		var arr_return = new Array();
		for (i = 0, i_length = arr_perfom_in.length; i < i_length; i++) {
			if (arr_perfom_in[i] != i_value) arr_return[arr_return.length] = arr_perfom_in[i];
		}
		return arr_return;
	
	}
	
	//Return some random number
	function sudo_random(i_from, i_to) {
		return Math.floor(Math.random() * (i_from - i_to + 1) + i_to);
	}
	
	//Write solutions array when game is created or input is changed
	function sudo_reset_solutions() {
		
		if (arguments.length == 0) {
			for (i = 0; i < i_sudo_field_no; i++) {
				i_input_val = arr_sudo_input_fields[i].value;
				if (i_input_val != '') {
					arr_sudo_solutions[i][0] = [sudo_translate_input(i_input_val, false)];
				}
			}
		} else {
			i_input_val = arr_sudo_input_fields[arguments[0]].value;
			if (i_input_val != '') {
				arr_sudo_solutions[arguments[0]][0] = [sudo_translate_input(i_input_val, false)];
			}
		}
	
	}
	
	
	//Solve af game or merely check if it can be solved..
	//bol_quiet = simply return true/false and don't echo anything to the user!
	//bol_skip_elimination = true if we don't have to eliminate first
	function sudo_solve(bol_quiet, bol_skip_elimination) {
	
		if (!bol_quiet && isNaN(i_sudo_size)) {
			sudo_status('No hay partida.');
			return;
		}
	
		//Eliminate solutions
		//Returns false if any field has no solutions
		bol_sudo_solved = (bol_skip_elimination || sudo_eliminate(bol_quiet));
		
		//Check that only 1 solution is left in ALL fields.
		for (i = 0; i < i_sudo_field_no && bol_sudo_solved; i++) {
			if (arr_sudo_solutions[i][0].length != 1) {
				bol_sudo_solved = false;
			} else if (!bol_quiet) {
				arr_sudo_input_fields[i].value = sudo_translate_input(arr_sudo_solutions[i][0], true);
			}
		}
		
		if (!bol_sudo_solved && !bol_quiet) {
			alert('No se encontró solución');
		} else if (!bol_quiet) {
			sudo_status('Sudoku resuelto.');
		}
		
		return bol_sudo_solved;
	
	}
	
	function sudo_test() {
	
		if (isNaN(i_sudo_size)) {
			sudo_status('No hay partida.');
			return;
		}
	
		var bol_solvable = sudo_eliminate(true);
	
		if (bol_solvable && sudo_solve(true)) {
			alert('Hay una solución posible.');
		} else if (bol_solvable) {
			alert('Hay varias soluciones.');
		} else {
			alert('No hay solución.');
		}
		
		//Return to human solving...
		sudo_eliminate_human(true, 0, 0);
	
	}
	
	//Eliminate solutions
	//Returns false if a field looses all of its solutions.
	//bol_quiet = false: solutions are written in input fields when found.
	function sudo_eliminate(bol_quiet) {

		//Start the elimination process
		//These recursive calls will return false if
		//no solutions are found
		return	sudo_eliminate_do(arr_sudo_columns, bol_quiet) &&
				sudo_eliminate_do(arr_sudo_rows, bol_quiet) &&
				sudo_eliminate_do(arr_sudo_regions, bol_quiet);
		
	}
	
	//Eliminate solutions recursively
	function sudo_eliminate_do(arr_perform_in, bol_quiet) {
		
		//Eliminate anything in the columns. start over if something is eliminated!
		for (i = 0, i_length = arr_perform_in.length; i < i_length; i++) {
			
			//Array for the first occurence of a solution
			var arr_solutions_first_occur = new Array(i_sudo_size * i_sudo_size);
			//Create an array of solutions so that we may count
			//the number of occurences. Give all members initiate value of 0.
			var arr_count = new Array(i_sudo_size * i_sudo_size);
			for (a = 0, a_length = arr_count.length; a < a_length; a++) arr_count[a] = 0;
			
			var arr_perform_in_i = arr_perform_in[i];
			
			//Go through members of the column
			for (a = 0, a_length = arr_perform_in_i.length; a < a_length; a++) {
				
				var arr_perform_in_i_a = arr_perform_in_i[a];
				
				//If the field only has 1 solution, it must be eliminated if it hasn't been already
				if (!arr_sudo_solutions[arr_perform_in_i_a][1] && arr_sudo_solutions[arr_perform_in_i_a][0].length == 1) {
					sudo_eliminate_solution(arr_perform_in_i_a, arr_sudo_solutions[arr_perform_in_i_a][0][0], bol_quiet);
					return sudo_eliminate_do(arr_perform_in, bol_quiet);
				}
				
				//Go through the solutions and increment the member of the solution array
				for (b = 0, b_length = arr_sudo_solutions[arr_perform_in_i_a][0].length; b < b_length; b++) {
					i_this_solution = arr_sudo_solutions[arr_perform_in_i_a][0][b];
					arr_count[i_this_solution]++;
					//If it's the first occurence we'll index it!
					if (arr_count[i_this_solution] == 1) arr_solutions_first_occur[i_this_solution] = arr_perform_in_i_a;
				}
			}
			//Go through solutions found
			for (a = 0, a_length = arr_count.length; a < a_length; a++) {
				if (arr_count[a] == 0) {
					//Oh horror, there's no fields with this particular number in it's solution array
					return false;
				} else if (arr_count[a] == 1 && !arr_sudo_solutions[arr_solutions_first_occur[a]][1]) {
					//Call function that eliminates occurences in the same column
					sudo_eliminate_solution(arr_solutions_first_occur[a], a, bol_quiet);
					return sudo_eliminate_do(arr_perform_in, bol_quiet);
				} else {
					//Nothing eliminated and so what?
				}
			}
		}
		
		return true;

	}
	
	//Remove a solution from row/column/region
	function sudo_eliminate_solution(i_elim_index, i_number_to_remove, bol_quiet) {

		arr_sudo_solutions[i_elim_index][0]	= [i_number_to_remove];
		arr_sudo_solutions[i_elim_index][1]	= true;
		
		!bol_quiet ? arr_sudo_input_fields[i_elim_index].value = sudo_translate_input(i_number_to_remove, true) : false;
		
		//Call the function that performs the actual removal in the parent
		//columns, rows and regions
		sudo_eliminate_solution_do(i_elim_index, i_number_to_remove, arr_sudo_columns[arr_sudo_index[i_elim_index][0]], arr_sudo_solutions);
		sudo_eliminate_solution_do(i_elim_index, i_number_to_remove, arr_sudo_rows[arr_sudo_index[i_elim_index][1]], arr_sudo_solutions);
		sudo_eliminate_solution_do(i_elim_index, i_number_to_remove, arr_sudo_regions[arr_sudo_index[i_elim_index][2]], arr_sudo_solutions);
	
	}
	
	//Remove a solution from row/column/region
	// - perform the actual elimination
	function sudo_eliminate_solution_do(i_elim_index, i_number_to_remove, arr_perform_in) {
		
		//Eliminate from columns
		for (i = 0, i_length = arr_perform_in.length; i < i_length; i++) {
		
			i_index = arr_perform_in[i];
		
			//Perform elimination on any field but this
			if (i_index != i_elim_index) {
				//Create a new array that holds all solutions but this
				arr_new_array = new Array();
				for (a = 0, a_length = arr_sudo_solutions[i_index][0].length; a < a_length; a++) {
					var i_this_solution = arr_sudo_solutions[i_index][0][a];
					i_this_solution != i_number_to_remove ? arr_new_array[arr_new_array.length] = i_this_solution : false;
				}
				arr_sudo_solutions[i_index][0] = arr_new_array;
			}
		}
	}

	//Stupid human elimination. Only eliminates what's typed
	//in the input fields. Used for displaying field info
	function sudo_eliminate_human(bol_start_over, i_index, i_value) {

		//If input is changed we need the reset all solutions and start over.
		if (bol_start_over) {
			arr_sudo_solutions = sudo_init_solution_array();
			sudo_reset_solutions();
			for (j = 0, j_length = i_sudo_field_no; j < j_length; j++) {
				if (arr_sudo_solutions[j][0].length == 1) {
					sudo_eliminate_solution(j, arr_sudo_solutions[j][0][0], true);
				}
			}
		//Otherwise just eliminate this number
		} else {
			sudo_eliminate_solution(i_index, i_value, true);
		}
		
	}
	
	function sudo_input_change(bol_start_over, i_index, i_value) {
	
		if (sudo_validate_input(i_value) || i_value == '') {
			sudo_eliminate_human(bol_start_over, i_index, sudo_translate_input(i_value, false));
			sudo_update_field_info(i_index);
			if (i_value != '') {
				sudo_test_input(i_index);
			}
		} else if (i_value != '') {
			arr_sudo_input_fields[i_index].value = old_val;
		}
		
		i_sudo_field_cnt = 0;
		for (i = 0; i < i_sudo_field_no; i++) {
			if (arr_sudo_input_fields[i].value != '') {
				i_sudo_field_cnt++;
			}
		}

		//If all fields have been filled then test the game
		if (i_sudo_field_cnt == i_sudo_field_no && sudo_solve(true)) {
			sudo_status('Juego resuelto correctamente!.');
		} else if (i_sudo_field_cnt == i_sudo_field_no) {
			sudo_status('Solución incorrecta.');
		}
	
	}
	
	function sudo_test_input(i_index) {
	
		if (bol_sudo_test_input) {
			if (sudo_solve(true)) {
				arr_sudo_input_fields[i_index].style.backgroundColor = '#0e0';
				sudo_status('Ok.');
			} else {
				arr_sudo_input_fields[i_index].style.backgroundColor = '#f99';
				sudo_status('Datos incorrectos.');
			}
		}
	}
	
	function sudo_toggle_test_input(bol_test) {
		bol_sudo_test_input = bol_test;
		if (!bol_sudo_test_input) sudo_status('Listo');
	}
	
	//Function to use in order to output or input numbers ([1-9]) or letters ([0-9A-G])
	//false = human -> zero-indexed; true = zero-indexed -> human
	arr_sudo_number_to_char = new Array(1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F', 'G')
	arr_sudo_char_to_number = new Array(arr_sudo_number_to_char.length);
	for (i = 0; i < arr_sudo_number_to_char.length; i++) {
		arr_sudo_char_to_number[arr_sudo_number_to_char[i].toString()] = i;
	}
	function sudo_translate_input(mixed_input, bol_in_out) {
		
		//Translate from input
		if (bol_in_out) {
			return arr_sudo_number_to_char[mixed_input];
		} else {
			return arr_sudo_char_to_number[mixed_input.toString()];
		}
	
	}
	
	//Validate silly human input
	function sudo_validate_input(s_input) {
		for (i = 0, i_length = i_sudo_size * i_sudo_size; i < i_length; i++) {
			if (s_input == arr_sudo_number_to_char[i]) return true;
		}
		return false;
	}
	
	//Return aan array containing all the fellow memberships of a given
	//field - meaning all other fields that also belong to the same
	//row/region/column
	function sudo_field_fellows(i_index) {
		return arr_sudo_columns[arr_sudo_index[i_index][0]].concat(arr_sudo_rows[arr_sudo_index[i_index][1]], arr_sudo_regions[arr_sudo_index[i_index][2]]);
	}
	
	//Hightlight co-member rows, columns and regions
	function sudo_field_hightlight(i_index) {
	
		arr_member_of = sudo_field_fellows(i_index);

		//Highlight columns
		for (i = 0; i < arr_member_of.length; i++) {
			if (arr_member_of[i] != i_index) arr_sudo_input_fields[arr_member_of[i]].style.backgroundColor = '#eee';
		}
		
		//Especially the selected field...
		arr_sudo_input_fields[i_index].style.backgroundColor = '#999';
		
		sudo_update_field_info(i_index);
	
	}
	
	//Hightlight co-member rows, columns and regions
	function sudo_field_blur(i_index) {
	
		arr_member_of = sudo_field_fellows(i_index);

		//Blur columns
		for (i = 0; i < arr_member_of.length; i++) {
			arr_sudo_input_fields[arr_member_of[i]].style.backgroundColor = '#fff';
		}
		sudo_update_field_info(-1);
		
	}
	
	//Show field info
	function sudo_update_field_info(i_index) {
		
		var str_html = '<b>Valores posibles:</b><br /><br />';
		
		sudo_eliminate_human(true, i_index, true);
		
		if (i_index > -1) {
			this_solution = arr_sudo_solutions[i_index][0];
			for (i = 0, i_length = this_solution.length; i < i_length; i++) {
				str_html += '<input type="text" class="sudo_field" value="' + sudo_translate_input(this_solution[i], true) + '" style="margin-bottom: 4px;" /> ';
			}
		}
		document.getElementById('sudo_field_info').innerHTML = str_html;
		
	}
	
	function sudo_toggle_field_info(bol_show) {
	
		bol_sudo_show_info = bol_show;
	
		if (bol_show) {
			sudo_eliminate_human(true);
			document.getElementById('sudo_field_info').style.visibility = 'visible';
		} else {
			document.getElementById('sudo_field_info').style.visibility = 'hidden';
		}
	
	}
	
	//Make everything hidden and copy the game board to a visible layer
	//Transfer all values of the old board to the copied board
	function sudo_print(obj_to_print) {
	
		if (isNaN(i_sudo_size)) {
			sudo_status('No hay partida.');
			return;
		}

		var obj_noprint = document.getElementById('sudo_noprint');
		var obj_body = document.getElementsByTagName('body');
	
		obj_noprint.style.display = 'none';
		obj_temp_print = document.createElement('div');
		obj_temp_print.innerHTML = obj_to_print.innerHTML;
		
		//Copy values from old board to the printed board
		var obj_old_plate = obj_temp_print.getElementsByTagName('input');
		for (i = 0; i < obj_old_plate.length; i++) {
			obj_old_plate[i].value = arr_sudo_input_fields[i].value;
		}
		
		obj_temp_print.style.display = 'block';
		obj_body[0].appendChild(obj_temp_print);
		window.print();
		obj_body[0].removeChild(obj_temp_print);
		obj_noprint.style.display = 'block';
	
	}

	var i_sudo_timer_id = 0;
	var i_sudo_second_count = 0;
	var obj_sudo_timer_form;
	function sudo_timer(i_command) {
		
		obj_sudo_timer_form = document.sudo_clock;
		
		//Whatever we do, the timer needs to be stopped
		clearTimeout(i_sudo_timer_id);
		
		switch (parseInt(i_command)) {
		
			case 0:
				i_sudo_second_count = parseInt(obj_sudo_timer_form.hh.value * 3600) + parseInt(obj_sudo_timer_form.mm.value * 60) + parseInt(obj_sudo_timer_form.ss.value);
				sudo_timer_do();
				break;
			case 1:
				break;
			case 2:
				i_sudo_second_count = 0;
				sudo_timer_do();
				break;
		}
		
	}

	function sudo_timer_do() {
	
		var i_hh = Math.floor(i_sudo_second_count / 3600);
		var i_mm = Math.floor((i_sudo_second_count % 3600) / 60);
		var i_ss = i_sudo_second_count - i_hh * 3600 - i_mm * 60;
		
		obj_sudo_timer_form.hh.value = sudo_format_number(i_hh);
		obj_sudo_timer_form.mm.value = sudo_format_number(i_mm);
		obj_sudo_timer_form.ss.value = sudo_format_number(i_ss);
		
		i_sudo_second_count++;
		i_sudo_timer_id = setTimeout('sudo_timer_do()', 1000);
	
	}

	function sudo_format_number(i_no) {
		if (i_no < 10) {
			return '0' + i_no;
		} else {
			return i_no;
		}
	}

	function sudo_save_game() {
		
		if (isNaN(i_sudo_size)) {
			sudo_status('No hay partida.');
			return;
		}
	
		var str_xml = '<sudoku>\n';
		
		str_xml += '\t<size>' + i_sudo_size + '</size>\n';
		
		for (i = 0; i < i_sudo_field_no; i++) {
			if (arr_sudo_input_fields[i].value != '') {
				str_xml += '\t<field>\n';
				str_xml += '\t\t<index>' + i + '</index>\n';
				str_xml += '\t\t<value>' + arr_sudo_input_fields[i].value + '</value>\n';
				str_xml += '\t</field>\n';
			}
		}
		
		str_xml += '</sudoku>';
		
		document.sudo_save.savetxt.value = str_xml;
		
		sudo_toggle_layers('sudo_blur', 'sudo_save');
	
	}

	function sudo_load_game_do(str_xml) {
	
		try {
			//Instead of using some XML parser we just use regular expressions
			str_xml = str_xml.replace(/[\n\t]/g, "");
			
			i_size = str_xml.match(/<size>(.*)<\/size>/)[1];
			sudo_init(parseInt(i_size));


			if (str_xml.search(/<field>(.*?)<\/field>/) > -1) {
				var arr_fields = str_xml.match(/<field>(.*?)<\/field>/g);
				for (i = 0; i < arr_fields.length; i++) {
					var i_index = arr_fields[i].match(/<index>(.*)<\/index>/)[1];
					var s_value = arr_fields[i].match(/<value>(.*)<\/value>/)[1];
					if (parseInt(i_index) < i_sudo_field_no) arr_sudo_input_fields[i_index].value = s_value;
				}
			}
		
			sudo_reset_solutions();
			//Do human elimination for field info..
			sudo_eliminate_human(true, 0, 0);
		
		} catch(e) {
			sudo_status('Ha ocurrido un error.');
		}
	
	}
	
	function sudo_status(str_mesg) {
		document.getElementById('sudo_status').innerHTML = str_mesg;
	}
	
	function sudo_toggle_layers() {
	
		for (i = 0, i_length = arguments.length; i < i_length; i++) {
			obj_layer = document.getElementById(arguments[i]);
			 if (obj_layer.style.display == 'block') {
			 	obj_layer.style.display = 'none';
			 	var s_msie = 'visible';
			 } else {
			 	obj_layer.style.display = 'block';
			 	var s_msie = 'hidden';
			 }
		}
	
		if (navigator.userAgent.indexOf('MSIE') > -1) {
			obj_selects = document.getElementsByTagName('select');
			for(i = 0; i < obj_selects.length; i++) {
				obj_selects[i].style.visibility = s_msie;
			}
		}
	}
	
	window.onload = function() {sudo_init(3)};