Sin revisar el código a fondo, parece que el problema del bucle infinito es porque en efecto trata de elegir 'al azar' un número donde solo existe una posibilidad de elección y resulta que esa elección ya está presente. Tal y como te comenta footer.
De entrada, no hace falta que busques el 9º número al azar, busca solo los 8 primeros, el 9º será el número que reste de la serie, así evitas el bucle infinito.
Y ahora que termina la segunda fila (y siguientes, pero no para la primera) debes verificar si verticalmente también existe conincidencia (y en cada uno de los 3 bloques implicados) en cuyo caso descarta de nuevo la fila actual y la regeneras al azar hasta que se valide, así vas avanzando con cada fila....
--------------------
Sin embargo el método, no es adecuado, vas a tirar mucho de azar para lograr cada Sudoku...
Un modo más inteligente es mantener con claridad 3 arrays para decidir entre qué números debe elegirse al azar.
tú ahora estás en la casilla x,y, pués rellenas 3 arrays (que serían Fila(), Columna(), Bloque()... (ver más abajo el pseudocódigo para la función de ejemplo para obtener el array de Fila: GetValoresEnFila))
Esos 3 arrays con los valores actuales en la fila, columna y bloque (si un valor no está tendrán valor 0), se deben luego unificar en uno solo array resumen donde aparezcan cada valor existente una sola vez
Ver más abajo la función (aquí la llamada con los 3 arrays como parámetros y la devolución de uno resumido:
Entonces llamas a una función preguntan do para ser rellenado con los valores ausentes, pasándole como entrada ese array unificado:
Presentes, es el array resultado de 'unificarCubiculos'...
Devuelve un array con los valores de la fila recibida.
l array 'resumen', simplemente toma todos los valores distintos presentes, sin repetición...
Unificando así los 3 arrays en uno de resumen...
Así el orden de estas funciones para tomar UN VALOR al azar sería, la siguiente función:
X,Y representan la casilla donde deseamos poner el valor
no se verifica que x e y estén en el rango correcto, se supone que son correctos,
que fueron verificados, antes de llamar aqui
La función que rellena el sudoku entero sería algo como esto:
c_Ocho, c_Nueve son constantes con esos valores que en este juego se van a usar muy a menudo. Igualmente constantes para 2 y 3 podrían ser usadas, para las consultas en los bloques.
--------------------------------------------------------------------------------------------------------------
p.d.: Actualizo la funcion para rellenar el Sudoku, para salir del bache ante bloqueos...
Fijarse en los cambios añadidos para Intentos, cuando el valor devuelto es 0.
Si se va dibujando desde esta función cuando sale un valor y se borra cuando se anulan los valores, se ve sobre la marcha como opera , se detiene y rada un poco en algunas filas, y a veces borra también la previa y luego sale del bache y lo completa siempre...
La función que rellena el sudoku entero sería algo como esto:
De entrada, no hace falta que busques el 9º número al azar, busca solo los 8 primeros, el 9º será el número que reste de la serie, así evitas el bucle infinito.
Y ahora que termina la segunda fila (y siguientes, pero no para la primera) debes verificar si verticalmente también existe conincidencia (y en cada uno de los 3 bloques implicados) en cuyo caso descarta de nuevo la fila actual y la regeneras al azar hasta que se valide, así vas avanzando con cada fila....
--------------------
Sin embargo el método, no es adecuado, vas a tirar mucho de azar para lograr cada Sudoku...
Un modo más inteligente es mantener con claridad 3 arrays para decidir entre qué números debe elegirse al azar.
tú ahora estás en la casilla x,y, pués rellenas 3 arrays (que serían Fila(), Columna(), Bloque()... (ver más abajo el pseudocódigo para la función de ejemplo para obtener el array de Fila: GetValoresEnFila))
Código [Seleccionar]
TmpFila = GetValoresEnFila(Entero Y) //solo pongo el psedocodigo para esta función, comprendiendo ésta es fácil deducir como han de ser las otras dos funciones...
TmpColumna = GetValoresEnColumna(Entero X)
TmpBloque = GetValoresEnBloque(Entero X, Entero Y)
Esos 3 arrays con los valores actuales en la fila, columna y bloque (si un valor no está tendrán valor 0), se deben luego unificar en uno solo array resumen donde aparezcan cada valor existente una sola vez
Ver más abajo la función (aquí la llamada con los 3 arrays como parámetros y la devolución de uno resumido:
Código [Seleccionar]
Tmp = UnificarCubiculos(TmpFila, TmpColumna, TmpBloque)
Entonces llamas a una función preguntan do para ser rellenado con los valores ausentes, pasándole como entrada ese array unificado:
Presentes, es el array resultado de 'unificarCubiculos'...
Código [Seleccionar]
Array Byte = Funcion TomarAusentes(Array Byte Presentes(), Out Entero NumAusentes)
Array tipo Byte V(0 a c_Ocho)
Entero k
NumAusentes=0
Bucle para k desde 1 a c_Nueve
Si (Presentes(k) = 0) luego // El valor está vacío como casilla, en fila, columna y bloque... se añade
V(NumAusentes) = k
NumAusentes +=1
Fin si
Fin Bucle
Devolver V
Fin Funcion
Código [Seleccionar]
Array Byte Sudoku(0 a 80) //es el array que mantiene internamente los valores.
Devuelve un array con los valores de la fila recibida.
Código [Seleccionar]
Array Byte = Funcion GetValoresEnFila(Entero n)
Array tipo Byte V(0 a c_Ocho)
Entero Index, k
Index= (n * c_Nueve)
n = 0 // Reutilizamos n (se recibe por valor)
Bucle para k desde Index a (Index + c_Ocho)
V(n) = Sudoku(k) //Aray que contiene los valores
n +=1
Fin Bucle
Devolver V
Fin Funcion
l array 'resumen', simplemente toma todos los valores distintos presentes, sin repetición...
Unificando así los 3 arrays en uno de resumen...
Código [Seleccionar]
Array Byte = Funcion UnificarCubiculos(Array Byte Fila(), Array Byte Columna(), Array Byte Bloque())
Array tipo Byte V(0 a c_Nueve)
// contar el número de presencias de cada valor...
Bucle para k desde 0 a c_Ocho
Si (Fila(k) > 0) luego // Si no está vacía la casilla...
V(Fila(k)) += 1
fin si
Si Columna(k) > 0 luego // Si no está vacía la casilla...
V(Columna(k)) += 1
fin si
Si (Bloque(k) > 0) luego // Si no está vacía la casilla...
V(Bloque(k)) += 1
fin si
Fin Bucle
// Ahora se reagrupan arriba en el array (las ausencias (valor 0), quedan 'huecas' en la parte alta del array.
V(0) = 0 //Realmente no es necesario, ya que no llevó la cuenta de 'ceros'
Bucle para k desde 1 a c_Nueve
Si (V(k) > 0) luego
V(k) = k
Fin si
Fin Bucle
Devolver V
Fin Funcion
Así el orden de estas funciones para tomar UN VALOR al azar sería, la siguiente función:
X,Y representan la casilla donde deseamos poner el valor
no se verifica que x e y estén en el rango correcto, se supone que son correctos,
que fueron verificados, antes de llamar aqui
Código [Seleccionar]
Byte = funcion SeleccionarValorAzar(Entero X, Entero Y)
Array tipo Byte TmpFila()
Array tipo Byte TmpColumna()
Array tipo Byte TmpBloque()
Array tipo Byte Tmp()
Array tipo Byte Azar()
// Primero consultamos que tenemos, en la 'vecindad' de esa casilla.
// Puede optimizarse si se mantienen en memoria 9 arrays de filas, 9 de columnas y los 9 de bloques y se actualizan convenientemente. Será mas veloz, pero más complejo y másgasto de memoria... Hacerlo, queda a tu esfuerzo si te interesa.
// Recuérdese que las casillas 'vacías', tienen valor 0
TmpFila = GetValoresEnFila(Entero Y)
TmpColumna = GetValoresEnColumna(Entero X)
TmpBloque = GetValoresEnBloque(Entero X, Entero Y)
// Ya tenemos, los 3 cubículos con los que comparar el valor al azar... pero...
// ya dije que no vamos a comparar, en su defecto, integraremos los 3 en 1 ( a modo de resumen, que simplica
// por completo la verificación a un simple bucle).
Tmp = UnificarCubiculos(TmpFila, TmpColumna, TmpBloque)
// Ahora rellenamos otro array con los valores ausentes en el array resumen unificado.
Byte n
Azar = TomarAusentes(Tmp, n)
// OJO: si el array queda vacío implica que hay que desecha los valores actuales de la fila y volver a reconstruirla entera
Si (n=0) luego devolver 0
// Finalmente se toma un valor al azar entre 0 y n-1, que representa el indice del array Azar. Este array tiene exclusivamente los números que no aparecen ni en la fila, ni en columna, ni en bloque.
k = Aleatorio( entre 0 y n-1)
Devolver Azar(k)
Fin Funcion
La función que rellena el sudoku entero sería algo como esto:
Código [Seleccionar]
Funcion RellenarSudokuAlAzar()
Entero Fila, columna
Byte Valor
Fila=0 // la primera fila, la podrías obtener al azar completamente en ese caso marcar aquí 1, para comenzar en la fila 1, la 0 no va a requerir todas las comprobaciones que pueden requerir las siguientes.
Hacer '// esto es fila, puede ser un bucle que va de 0 a 8
Columna = 0
Hacer
Valor = SeleccionarValorAzar(Columna, Fila) //la función que obtiene un valor de cada vez.
Si (Valor>0) luego
Sudoku((Fila * c_Nueve) + Columna) = valor //Sudoku() es el array que mantiene internamente los valores.
// Si el array es bidimensional se pondría así...
//Sudoku(Columna, Fila) = valor
Columna +=1
Si no
// Borra los valores de la fila actual
Bucle para k desde 0 a columna
Sudoku((Fila * c_Nueve) + k) = 0
Fin bucle
Columna = 0
Fin si
Repetir Mientras (Columna <9)
Fila +=1
Repetir Mientra (Fila<9)
Fin Funcion
c_Ocho, c_Nueve son constantes con esos valores que en este juego se van a usar muy a menudo. Igualmente constantes para 2 y 3 podrían ser usadas, para las consultas en los bloques.
--------------------------------------------------------------------------------------------------------------
p.d.: Actualizo la funcion para rellenar el Sudoku, para salir del bache ante bloqueos...
Fijarse en los cambios añadidos para Intentos, cuando el valor devuelto es 0.
Si se va dibujando desde esta función cuando sale un valor y se borra cuando se anulan los valores, se ve sobre la marcha como opera , se detiene y rada un poco en algunas filas, y a veces borra también la previa y luego sale del bache y lo completa siempre...
La función que rellena el sudoku entero sería algo como esto:
Código [Seleccionar]
Funcion RellenarSudokuAlAzar()
Entero Fila, columna
Byte Valor, Intentos
Fila=0 // la primera fila, la podrías obtener al azar completamente en ese caso marcar aquí 1, para comenzar en la fila 1, la 0 no va a requerir todas las comprobaciones que pueden requerir las siguientes.
Hacer '// esto es fila, puede ser un bucle que va de 0 a 8
Intentos = 0
Columna = 0
Hacer
Valor = SeleccionarValorAzar(Columna, Fila) //la función que obtiene un valor de cada vez.
Si (Valor>0) luego
Sudoku((Fila * c_Nueve) + Columna) = valor //Sudoku() es el array que mantiene internamente los valores.
Columna +=1
Si no
// Borra los valores de la fila actual
Bucle para k desde 0 a columna
Sudoku((Fila * c_Nueve) + k) = 0
Fin bucle
// Tras 10 intentos en la misma fila, borramos también la fila previa
Intentos += 1
Si (Intentos = 10) luego
Si (Fila > 0) luego // no podemos retroceder a filas más atrás que la primera.
Fila = (Fila - 1) // Borramos también la fila anterior (y si sucede otra vez, la previa, etc...)
Bucle para k desde 0 To c_Ocho
Sudoku((Fila * c_Nueve) + k) = 0
Fin Bucle
Fin si
Intentos = 0
Fin si
Columna = 0
Fin si
Repetir Mientras (Columna <9)
Fila +=1
Repetir Mientra (Fila<9)
Fin Funcion
// Al término de esta función se podría dibujar los valores en el tablero para verificar visualmente el resultado.