Ir al contenido principal

Cómo resolver y/o crear un Sudoku usando PHP (parte 1)

C
omo programador, he tenido que realizar proyectos profesionalmente, algunos con mayores retos que otros. Pero aparte de los retos profesionales, existen retos personales, programas que me nace escribir ya sea porque necesito solucionar una necesidad puntual o solamente por el placer de hacerlo. Uno de esos últimos retos fue el de solucionar un Sudoku. Si ya se, existen muchas aplicaciones allí afuera que lo hacen, pero el reto es hacerlo, no copiarlo. Habiendo aclarado las intenciones al respecto, lo primero a tener claro es cómo se define un Sudoku. Para esto, voy a apoyarme en la siempre disponible (aunque no siempre fiable) Wikipedia:

Un Sudoku estándar contiene 81 celdas, dispuestas en una trama de 9×9, que está subdividida en nueve cajas. Cada caja está determinada por la intersección de tres filas con tres columnas. Cada celda puede contener un número del uno al nueve y cada número solo puede aparecer una vez en cada fila, cada columna o cada caja. Un sudoku comienza con algunas celdas ya completas, conteniendo números (pistas), y el objetivo es rellenar las celdas restantes. Los sudokus bien planteados tienen una única solución.

Wikipedia

Ojo con esa última línea: “Los sudokus bien planteados tienen una única solución”, la tendremos que considerar más adelante.

Un tablero de Sudoku en limpio

Solucionar un Sudoku no es algo trivial, aunque así lo parece. Lo primero que intenté fue replicar mi forma de abordar estos pasatiempos, pero entonces me di cuenta que plasmar mi forma de pensar en un “algoritmo coherente” no iba a resultar porque, bueno, los humanos tenemos formas de pensar muy particulares y no siempre se trasladan bien al lenguaje de una máquina (de allí la trascendencia, relevancia e importancia de lo que se ha logrado con ChatGPT). Esto me llevo a que muchas veces abandonara este proyecto. Y a que muchas veces lo intentara de nuevo, hasta que finalmente decidí que lo mejor era comenzar por el principio, con algo “sencillo”, como llenar una cuadricula de Sudoku en limpio. Decidido eso, lo siguiente fue trazar la ruta de trabajo a seguir, que fue la siguiente:

Teniendo esta ruta definida, comencé con lo básico en estos casos, porque la urgencia en estos proyectos personales suele ser sentarse a programar (usando PHP en mi caso) y comenzar a ver resultados, así no sea la presentación lo más elegante del mundo. Con esto en mente, comencé la creación de una clase que inicialmente permitiera:

  • Definir el tamaño base del Sudoku: 2 cajas de ancho, 4 celdas o 3 cajas de ancho, 9 celdas. Este último será la opción por defecto.
  • Registrar el tablero de Sudoku en un arreglo bidimensional. Probablemente no la más adecuada forma pero si la representación más aproximada a la cuadricula de Sudoku a la que estamos acostumbrados.
  • Generar un identificador único para cada celda y caja del tablero.
  • Generar un identificador único o checksum para el tablero en su conjunto (esto para futuras implementaciones donde se requiera diferenciar un tablero de otro).

Adicionalmente, la clase debe definir cada celda y asignarle atributos como:

  • Valor de la celda (usaremos el carácter “.” para identificar una celda si valor asignado).
  • Si es una celda fija (a usar cuando queramos solucionar un Sudoku existente).
  • Posibles valores permitidos en la celda (números del 1 al 9 que cambiarán conforme vayamos llenando el Sudoku).

Finalmente y aunque no es lo usual debido a que va en contra de algunos principios aplicables a las clases (¿has escuchado hablar de principios SOLID?), voy a incluir un método para visualizar el tablero de Sudoku en pantalla. Esto ayudará a visualizar el progreso de la solución. Posteriormente puede retirarse para tener una clase no dependiente de la salida a pantalla pero de momento nos servirá así.

Esto nos lleva a tener una clase con una estructura similar a la siguiente:

class miSudoku {
	// Numero de celdas a contener en cada grupo de celdas. Esto es, el tablero tendrá
	// $this->base cajas a lo ancho, cada una con $this->base celdas. Así, si la base es 
	// 2, el número de celdas a lo ancho (así como el número de celdas en cada caja) es
	// de 4, entanto que para base 3 el número de celdas es 9.
	public $base = 3;

	// Arreglo bidimensional con la información del tablero de Sudoku
	private $tablero = array();

	/**
	 * Construye base para el tablero de Sudoku.
	 * Las opciones validas para llenar cada celda son los números de 1 en adelante. El 
	 * valor máximo depende de la base usada (4 o 9, usualmente para una base de 2 o 3
	 * respectivamente).
	 */
	public function construirBase() {
		// ...
	}

	/**
	 * Genera texto HTML a pantalla para visualizar el tablero de Sudoku.
	 */
	public function render() {
		$salida = '';
		// ...
		echo $salida;
	}
}

Una vez codificada (puedes consultar el script completo en https://github.com/jjmejia/sudoku/tree/main/parte-1), tendremos una presentación en pantalla como la siguiente:

Y con esto podemos comenzar a llenar el tablero, pero eso lo veremos en detalle en la próxima entrega de esta serie.


Este artículo también se encuentra disponible en medium.com.

Comentarios

Entradas populares de este blog

Sesión de usuarios en aplicaciones web

Uno de los módulos más importantes y a la vez menospreciados cuando se aborda la tarea de crear un sitio web de servicios, ya sea para una intranet corporativa o un sistema de gestión de información ( SGI ) es la gestión y administración  requerida para una correcta implementación de sesiones de usuario. Y es que llevamos tanto tiempo usando usuarios y contraseñas en Internet, en cualquiera de sus muchas variaciones, que se asume muchas veces que esto ya forma parte del ADN de toda solución web y como tal, se destina muy poco tiempo y estudio a este apartado cuando se planifican las actividades de desarrollo. Lo cierto es que cada aplicación acostumbra desarrollar su propio esquema de manejo de sesiones y asumir que es algo superfluo puede equivaler a “pegarse un tiro en el pie”, especialmente cuando un módulo de este tipo se diseña desde ceros. Al referirse al manejo de sesiones de usuario suele pensarse únicamente en el proceso de capturar el nombre de usuario ( username ) y su cont

Configurando el servicio PHP

En el capítulo anterior ( PHP con Apache sobre Windows ) vimos como configurar PHP para ejecutarse desde un servidor web usando Apache. A continuación veremos los elementos a configurar directamente en PHP para garantizar una ejecución responsable y sin tropiezos de nuestros scripts. Algunos se preguntarán ¿ por qué  molestarse en configurar manualmente PHP cuando frameworks como Laravel  ya te entregan un docker con todo preinstalado y preconfigurado? Bueno, la verdad prefiero tener control de qué está ejecutándose en mi maquina y no me gusta, en lo particular, requerir de un entorno propietario para cada aplicación desarrollada cuando puedo tener uno para todas y no desperdiciar espacio en disco , memoria y/o procesador  ejecutando en cada proyecto un servidor wen y/o PHP por separado. Si, se que muy probablemente soy una minoría en este aspecto, mea culpa . Y en segundo lugar, nunca se sabe cuando tendrás que entrar y ajustar tu configuración de PHP, así que cuando ese día ll