Mmm, veo cuasi-imposible arreglar esos "-1", y mucho menos evitarlos. Asi que como he visto que tras varios intentos el sodoku sale perfecto (tras muuchos intentos, a veces puede que 20 o más), pues he decidido hacer un bucle iterativo: hasta que el Sodoku no salga perfecto, que lo vuelva a intentar.
El codigo completo es el siguiente:
Código PHP:
<html>
<head>
<title>SODOKU (version final)</title>
<style>
.fila_celdas {
height:20px;
clear:both;
}
.fila_cuadrantes {
height:62px;
clear:both;
text-align:center;
}
.celda {
border: solid 1px black;
text-align:center;
width: 20px; height:20px;
font-size:10px;
font-family:tahoma;
float:left;
margin:0px;
}
.caja {
width: 75px; height:75px;
float:left;
text-align:center;
margin:0px;
}
</style>
</head>
<body>
<script>
var X=9; // celdas de lado
//INICIALIZACION DEL ARRAY SODOKU (Array de 4 dimensiones)
var sodoku=new Array(); //contiene todas las cajas (X cajas)
for(var x=0;x<X/3;x++) { //cajaX
sodoku[x]=new Array(X/3);
for(var y=0;y<X/3;y++) { //cajaY
sodoku[x][y]=new Array(X/3);
for(var i=0, n=0;i<3;i++) { //celdaX
sodoku[x][y][i]=new Array(3);
for(var j=0;j<3;j++) { //celdaY
sodoku[x][y][i][j]=new Array(3);
}
}
}
}
// asi el array queda sodoku[cajaX][cajaY][celdaX][celdaY]
//funcion document.write, no tan engorrosa y con opcion de salto <br />
function e(q,salto) { document.write(q); if(salto) document.write("<br />"); }
// imprimimos la estructura HTML de divs para luego solo tener que poner los valores
e('<div id="estado"><h2>Generando...</h2> Espere unos instantes...<br><br></div>');
for(var x=0; x<X/3; x++) { //cajaX
e('<div class="fila_cuadrantes">');
for(var y=0; y<X/3; y++) { //cajaY
e('<div class="caja" id="caja_'+x+'_'+y+'">');
for(var i=0;i<3;i++) { //celdaX
e('<div class="fila_cajas">');
for(var j=0;j<3;j++) { //celdaY
e('<div class="celda" id="sodoku_'+x+'_'+y+'_'+i+'_'+j+'"></div>');
}
e('</div>');
}
e('</div>');
}
e('</div>');
}
e(" ",1);
//actualiza los valores del array sodoku en la estructura de divs
function imprime_sodoku() {
for(var x=0;x<X/3;x++) //cajaX
for(var y=0;y<X/3;y++) //cajaY
for(var i=0;i<3;i++) //celdaX
for(var j=0;j<3;j++) //celdaY
if( (c=sodoku[x][y][i][j])==-1)
document.getElementById("sodoku_"+x+"_"+y+"_"+i+"_"+j).innerHTML="-";
else
document.getElementById("sodoku_"+x+"_"+y+"_"+i+"_"+j).innerHTML=c;
}
/**************************************************************************/
// FUNCIONES GET_____
//nos devuelve un array con todos los elementos de una fila respecto a una celda
function getFila(x,y,i,j) {
var dev=new Array();
for(_y=0; _y<X/3; _y++) //fila de cajas
for(_j=0; _j<X/3; _j++)
dev.splice(dev.length, 0, sodoku[x][_y][i][_j]); //cogemos la fila entera de celdas
return dev;
}
//nos devuelve un array con todos los elementos de una columna respecto a una celda
function getColumna(x,y,i,j) {
var dev=new Array();
for(_x=0; _x<X/3; _x++) //columna de cajas
for(_i=0; _i<3; _i++) // cada celda en la columna _j
dev.splice(dev.length, 0, sodoku[_x][y][_i][j]);
return dev;
}
//nos devuelve un solo array con la caja de la celda dada (solo tiene una dimension)
function getCaja(x,y) {
var dev=new Array();
for(_i=0; _i<3; _i++)
for(_j=0; _j<X/3; _j++)
dev.splice(dev.length, 0, sodoku[x][y][_i][_j]);
return dev;
}
/**************************************************************************/
// RELLENAR EL SODOKU
var nums_posibles=new Array(X);
for(var a=0;a<X;a++) nums_posibles[a]=a;
//busca un numero dentro de un array de numero
// y devuelve su indice. Si no lo encuentra devuelve -1
function numAt(arr,num) {
for(var a=0; a<arr.length; a++)
if(arr[a]==num)
return a;
return -1;
}
//elimina del array 'total' todos los numeros del array 'posibilidades', y devuelve el nuevo 'total'
// si posibilidades es un numero, no un array, tambien puede hacerlo
function eliminaPosibilidades(total, posibilidades) {
if(typeof posibilidades=="number") {
var esta=numAt(total,posibilidades);
if(esta!=-1)
total.splice(esta,1);
}
else {
for(var a=0; a<posibilidades.length; a++) {
var esta=numAt(total,posibilidades[a]);
if(esta!=-1)
total.splice(esta,1);
}
}
return total;
}
//copia un array y lo devuelve
function copia(arr) {
var dev=new Array();
for(var a=0;a<arr.length; a++)
dev[dev.length]=arr[a];
return dev;
}
function relleno() {
var hayMenosUno=false;
//rellenamos de "-1":
for(var x=0;x<X/3;x++) //cajaX
for(var y=0;y<X/3;y++) //cajaY
for(var i=0;i<3;i++) //celdaX
for(var j=0;j<3;j++) //celdaY
sodoku[x][y][i][j]= -1;
//rellenamos celda a celda
for(var x=0;x<X/3 && !hayMenosUno;x++) //cajaX
for(var y=0;y<X/3 && !hayMenosUno;y++) //cajaY
for(var i=0;i<3 && !hayMenosUno;i++) //celdaX
for(var j=0;j<3 && !hayMenosUno;j++) { //celdaY
var nums=copia(nums_posibles);
nums=eliminaPosibilidades(nums, getFila(x,y,i,j) );
nums=eliminaPosibilidades(nums, getColumna(x,y,i,j) );
nums=eliminaPosibilidades(nums, getCaja(x,y) );
if(nums.length!=0) //nos hemos quedado sin numeros para poder poner
sodoku[x][y][i][j]=nums[ parseInt(Math.random()*nums.length) ];
else
hayMenosUno=true;
}
imprime_sodoku();
if(hayMenosUno) {
document.getElementById("estado").innerHTML="<h2>Generando...</h2> Espere unos instantes...<br><br>";
setTimeout("relleno()", 50);
}
else {
document.getElementById("estado").innerHTML="<h2>Su sodoku</h2>Su Sodoku esta listo.";
}
}
relleno();
</script>
</body>
</html>
Se irá generando (lo he agilizado mucho creo, en cuanto detecta el error ya no sigue) hasta conseguir el perfecto.
Mi idea es una vez este generado el sodoku, dar la opcion de jugar a esa tabla en una nueva ventana. Se pasaria el juego entero mediante un formulario (metodo POST, supongo) a una ventana nueva.
El caso es que no tengo ni idea de cuántas celdas tengo que ocultar ni mostrar para que sea posible su resolución
Un saludete!