Debido a todas las idas y vueltas, dejo el script completo como quedó finalizado (tener en cuenta que no logra sudokus muy complicados, y si bien intenta llegar a la dificultad que se le pide, no siempre lo consigue):
Código PHP:
<?php
// Generador de sudokus. Versión original por dggluz (http://www.primolius.com/).
// Enlace:
// De uso libre mientras se cite la fuente y no sea para fines comerciales.
// Puede llegar a ocurrir que el script demore demasiado,
// por eso ponemos un tiempo límite de 10 segundos.
set_time_limit(30);
// La función shuffle de PHP no tiene una buena aleatoriedad,
// por eso generé esta otra. El array resultante tiene índice numérico ordenado y consecutivo comenzando en 0.
function shuffle2(&$arr)
{
if(empty($arr))
{
return array();
}
$arr2=array();
while(count($arr2)<count($arr))
{
// Alimento (pongo la semillita :D) la aleatoriedad con un número
// que depende de los microsegundos del epoch
srand(ceil(999999999*microtime()));
$i=rand(0, count($arr)-1);
if(!in_array($i, $arr2))
{
array_push($arr2, $i);
}
}
$arr=array_combine($arr2, $arr);
ksort($arr);
$arr=array_values($arr);
}
function completarMatrizSudoku()
{
$A=array();
$B=array();
$C=array();
$D=array();
$E=array();
$F=array();
$G=array();
$H=array();
$I=array();
$_a=array();
$_b=array();
$_c=array();
$_d=array();
$_e=array();
$_f=array();
$_g=array();
$_h=array();
$_i=array();
$_1=array();
$_2=array();
$_3=array();
$_4=array();
$_5=array();
$_6=array();
$_7=array();
$_8=array();
$_9=array();
$j=0;
$original=array(1, 2, 3, 4, 5, 6, 7, 8, 9);
$letras=array(1=>'a', 2=>'b', 3=>'c', 4=>'d', 5=>'e', 6=>'f', 7=>'g', 8=>'h', 0=>'i');
$cuadrado=array(
1=>'A', 2=>'A', 3=>'A', 4=>'B', 5=>'B', 6=>'B', 7=>'C', 8=>'C', 9=>'C',
10=>'A', 11=>'A', 12=>'A', 13=>'B', 14=>'B', 15=>'B', 16=>'C', 17=>'C', 18=>'C',
19=>'A', 20=>'A', 21=>'A', 22=>'B', 23=>'B', 24=>'B', 25=>'C', 26=>'C', 27=>'C',
28=>'D', 29=>'D', 30=>'D', 31=>'E', 32=>'E', 33=>'E', 34=>'F', 35=>'F', 36=>'F',
37=>'D', 38=>'D', 39=>'D', 40=>'E', 41=>'E', 42=>'E', 43=>'F', 44=>'F', 45=>'F',
46=>'D', 47=>'D', 48=>'D', 49=>'E', 50=>'E', 51=>'E', 52=>'F', 53=>'F', 54=>'F',
55=>'G', 56=>'G', 57=>'G', 58=>'H', 59=>'H', 60=>'H', 61=>'I', 62=>'I', 63=>'I',
64=>'G', 65=>'G', 66=>'G', 67=>'H', 68=>'H', 69=>'H', 70=>'I', 71=>'I', 72=>'I',
73=>'G', 74=>'G', 75=>'G', 76=>'H', 77=>'H', 78=>'H', 79=>'I', 80=>'I', 81=>'I');
$resultado=array();
$i=1;
while($i<=81)
{
$idxLetra=$i%9;
$letra='_'.$letras[$idxLetra];
$idxNro=ceil($i/9);
$nro='_'.$idxNro;
$cuadro=$cuadrado[$i];
// Si no sabes lo que significa $$letra, busca "variables variables PHP" en Google
$copia=array_diff($original, $$letra, $$nro, $$cuadro);
shuffle2($copia);
$valor=array_pop($copia);
array_push($$letra, $valor);
array_push($$nro, $valor);
array_push($$cuadro, $valor);
$resultado[$i]=$valor;
if($valor==NULL)
{
// Si algo va saliendo mal, empezamos todo de nuevo
$i=1;
$j++;
$A=array();
$B=array();
$C=array();
$D=array();
$E=array();
$F=array();
$G=array();
$H=array();
$I=array();
$_a=array();
$_b=array();
$_c=array();
$_d=array();
$_e=array();
$_f=array();
$_g=array();
$_h=array();
$_i=array();
$_1=array();
$_2=array();
$_3=array();
$_4=array();
$_5=array();
$_6=array();
$_7=array();
$_8=array();
$_9=array();
}
else
{
$i++;
}
}
# echo "intentos: ".$j;
return $resultado;
}
function borrarCasilleros($sudokuOriginal, $dificultad=NULL)
{
if($dificultad==NULL)
{
srand(ceil(999999999*microtime()));
$dificultad=rand(1, 28);
}
$arrPosiciones=array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81);
$arrOrden=$arrPosiciones;
shuffle2($arrOrden);
$quitados=0;
$sudokuResultante=$sudokuOriginal;
$limite=$dificultad+35;
$i=1;
while($quitados<$limite && $i<81)
{
$sudokuAnt=$sudokuResultante;
$quitadosAnt=$quitados;
$idx=$arrOrden[$i];
$sudokuResultante[$idx]=NULL;
if(!solucionUnica($sudokuResultante, $quitados, $idx))
{
$quitados=$quitadosAnt;
$sudokuResultante=$sudokuAnt;
}
$i++;
}
$dificultad=$quitados-35;
echo "<br />Dificultad: $dificultad<br />";
return $sudokuResultante;
}
function solucionUnica($sudoku, &$quitados, $idx)
{
$original=array(1, 2, 3, 4, 5, 6, 7, 8, 9);
$cols=array(1=>1, 2=>2, 3=>3, 4=>4, 5=>5, 6=>6, 7=>7, 8=>8, 0=>9);
$cuadrado=array(
1=>'A', 2=>'A', 3=>'A', 4=>'B', 5=>'B', 6=>'B', 7=>'C', 8=>'C', 9=>'C',
10=>'A', 11=>'A', 12=>'A', 13=>'B', 14=>'B', 15=>'B', 16=>'C', 17=>'C', 18=>'C',
19=>'A', 20=>'A', 21=>'A', 22=>'B', 23=>'B', 24=>'B', 25=>'C', 26=>'C', 27=>'C',
28=>'D', 29=>'D', 30=>'D', 31=>'E', 32=>'E', 33=>'E', 34=>'F', 35=>'F', 36=>'F',
37=>'D', 38=>'D', 39=>'D', 40=>'E', 41=>'E', 42=>'E', 43=>'F', 44=>'F', 45=>'F',
46=>'D', 47=>'D', 48=>'D', 49=>'E', 50=>'E', 51=>'E', 52=>'F', 53=>'F', 54=>'F',
55=>'G', 56=>'G', 57=>'G', 58=>'H', 59=>'H', 60=>'H', 61=>'I', 62=>'I', 63=>'I',
64=>'G', 65=>'G', 66=>'G', 67=>'H', 68=>'H', 69=>'H', 70=>'I', 71=>'I', 72=>'I',
73=>'G', 74=>'G', 75=>'G', 76=>'H', 77=>'H', 78=>'H', 79=>'I', 80=>'I', 81=>'I');
$cuadrados=array(
'A'=>array(1, 2, 3, 10, 11, 12, 19, 20, 21),
'B'=>array(4, 5, 6, 13, 14, 15, 22, 23, 24),
'C'=>array(7, 8, 9, 16, 17, 18, 25, 26, 27),
'D'=>array(28, 29, 30, 37, 38, 39, 46, 47, 48),
'E'=>array(31, 32, 33, 40, 41, 42, 49, 50, 51),
'F'=>array(34, 35, 36, 43, 44, 45, 52, 53, 54),
'G'=>array(55, 56, 57, 64, 65, 66, 73, 74, 75),
'H'=>array(58, 59, 60, 67, 68, 69, 76, 77, 78),
'I'=>array(61, 62, 63, 70, 71, 72, 79, 80, 81)
);
$idxColumna=$cols[$idx%9];
$idxFila=ceil($idx/9);
$idxCuadrado=$cuadrado[$idx];
$idxsColumna=array();
$idxsFila=array();
for($j=1; $j<=9; $j++)
{
array_push($idxsColumna, ($idxColumna+9*($j-1)));
array_push($idxsFila, ($j+($idxFila-1)*9));
}
$idxsCuadrado=$cuadrados[$idxCuadrado];
$columna=obtenerPorcion($sudoku, $idxsColumna);
$fila=obtenerPorcion($sudoku, $idxsFila);
$cuadro=obtenerPorcion($sudoku, $idxsCuadrado);
$opciones=array_diff($original, $columna, $fila, $cuadro);
if(count($opciones)==0)
{
echo "ERROR.";
}
elseif(count($opciones)==1)
{
$quitados++;
return true;
}
else
{
return false;
}
}
function obtenerPorcion($arrOriginal, $arrIdx)
{
$arr=array();
foreach($arrIdx as $idx)
{
array_push($arr, $arrOriginal[$idx]);
}
return $arr;
}
?>
En la respuesta siguiente está el resto del código del ejemplo completo (se puede probar la implementación de ese ejemplo en
.