SUDOKU 1K para ZX-81

Foro dedicado a la programación en todo tipo de sistemas clásicos.
dancresp
Amiga 1200
Amiga 1200
Mensajes: 1393
Registrado: 23 Dic 2008, 17:53
Sistema Favorito: MSX
primer_sistema: ZX81
Primera consola: Atari 2600
Gracias dadas: 3 veces
Gracias recibidas: 20 veces

SUDOKU 1K para ZX-81

Mensajepor dancresp » 21 Jul 2013, 21:33

DSC_1227.jpg
DSC_1227.jpg (57.4 KiB) Visto 8191 veces


EL JUEGO
El objetivo de un SUDOKU es rellenar una cuadrícula de 9x9, que a su vez está dividida en 9 regiones de 3x3, con números de 1 al 9 sin repetir ninguna cifra en la misma fila, columna y región.

El sistema muestra una cantidad de números, que no podemos modificar, y debemos deducir el resto hasta completar todas las casillas.

Controles
En la zona inferior se deben entrar tres valores juntos:
1. Fila (0 a 8).
2. Columna (0 a 8).
3. Valor de la celda. Puede ser un número (1 a 9) o cualquier otro carácter.


BLOQUES
He dividido el listado en 5 bloques:

- Definición del Sudoku modelo.
- Desordenar las celdas del Sudoku.
- Mostrar el Sudoku por pantalla.
- Introducir las coordenadas y el valor.
- Revisar si el Sudoku es correcto.


COMO FUNCIONA
El programa ocupa 20 líneas.
A continuación detallo, línea por línea, el funcionamiento del programa.

Se utilizan las siguientes variables:
F – Variable para bucles.
I – Variable de apoyo.
U – Variable que contiene el valor “1”.
N – Variable que contiene el valor “9”.
A$ - Variable donde se guarda el Sudoku correcto.
B$ - Variable donde se guarda el Sudoku como se ve en pantalla.
C$ - Variable donde se guarda la casilla del movimiento.

10 – Introducimos un Sudoku correcto.
14 – Guardamos el valor 1 en la variable “U”.
16 – Guardamos el valor 9 en la variable “N”.
20 – Desordenamos las líneas del Sudoku. El método se detalla más adelante.
22 – Se vuelve a la línea 20 si un número aleatorio entre 0 y 8 es mayor de 1.
30 – Guardamos el Sudoku generado en la variable B$.
40 – Inicio de un bucle entre los valores 0 y 26 usado para ocultar celdas.
42 – Ocultamos una celda del Sudoku guardado en B$.
44 – Final del bucle.
50 – Se muestra el identificador de las casillas horizontales.
52 – Inicio de un bucle entre los valores 0 y 8.
54 – Imprimimos la posición vertical y una tira del Sudoku con 9 valores.
56 – Final del bucle.
60 – Introducimos la casilla en la variable “C$”.
62 - Si la longitud de “C$” es distinta de 3 se vuelve a 60.
64 – Guardamos en “I” el primer carácter de “C$”, que es la fila.
66 – Guardamos en “F” el segundo carácter de “C$”, que es la columna.
68 – Ponemos el valor introducido en la posición de pantalla correspondiente.
70 – Añadimos el valor introducido en la posición correspondiente de la variable “B$”.
86 – Si el Sudoku completo guardado en “A$” es distinto al guardado en B$ se salta a la línea 60, sino finaliza el programa.


EL PROGRAMA
Listado.jpg
Listado.jpg (140.36 KiB) Visto 8191 veces


APUNTES FINALES
Jamás me había planteado meter un Sudoku en una máquina con 1 KB de RAM, pero visto que conseguí hacer funcionar un Mine-Field en 1 KB me lo tomé como un reto.

Partiendo del listado de la versión para el VIC-20 programada previamente, empecé a tachar líneas y bloques de código. Presentación, corrección, movimiento con teclas, etc. Todo fuera.

Al final quedó un listado mini, que había que conseguir encajarlo en el ZX-81. La verdad es que ha costado.

Peleando con la memoria (otra vez):
- Un ZX-81 básico dispone de 1024 bytes de RAM libres.
- Las variables de sistema ocupan 125 bytes.
- La memoria de video para un tablero de juego de 9x9 más los indicadores de fila y columna ocupan un total de 10x10=100 bytes, más los 25 bytes de final de línea, ocupan 125 bytes. Más unos 5 ó 6 de la zona del INPUT.

Total unos 130 bytes menos.
He conseguido reducir el uso de variables numéricas a 4, pero el problema está en las alfanuméricas que contienen el Sudoku correcto y el mostrado en pantalla. Almacenar la variable “A$” en el listado ocupa unos 90 bytes, pero a parte ocupa unos 85 bytes más en la zona de variables, más otros 85 para la variable “B$”. Y unos 8 para “C$”. En total las variables ocupan unos 300 bytes entre el listado y la zona de variables. Y esto ha ido un auténtico quebradero de cabeza.

Como va la memoria: 1024 – 125 – 125 - 300 = 474 bytes para el programa.

¿Como se hace?
El programa se ha desarrollado en un TIMEX SINCLAIR 1000 con 2 KB para evitar el engorro de programar con 1 KB y su molesta “escalada” de líneas en pantalla mientras programas. Así daba uso a este equipo, con el que nunca había programado.

Durante el desarrollo existía una línea:
4 DIM Z$(1012)

... que me ha permitido dejar el equipo con 1 KB durante la ejecución del programa. Si funciona aquí tendrá que funcionar en un ZX-81 con 1 KB sin ningún problema, como realmente ha sucedido.

¿Cómo generar un SUDOKU con un BASIC lento y sin memoria?
Analizando bien un SUDOKU ya completado veo que realmente se pueden dividir en 3 filas o en 3 columnas, como se puede ver en la siguiente imagen. Si se mueven estas filas o columnas, el SUDOKU sigue siendo perfectamente válido.

sudoku_1_estructura.gif
sudoku_1_estructura.gif (8.87 KiB) Visto 8191 veces


Aquí he aplicado la misma técnica que había usado previamente en la versión del COMMODORE VIC-20 y del SHARP MZ-80B. Básicamente consiste en introducir un Sudoku correcto, desordenarlo, y al final ocultar parte de las celdas. El Sudoku es correcto pero distinto del modelo base.

En esos programas se aplicaban tres técnicas:
1. Invertir el Sudoku diagonalmente.
2. Mover las columnas verticalmente.
3. Mover las filas horizontalmente.

Las primeras versiones del ZX-81 usaban las dos primeras técnicas, pero daba un error de memoria 4 durante la ejecución.
Así que he aplicado solo la técnica 2 pero he añadido una nueva que consiste en desordenar las filas de un bloque.

Con esto consigo hacer 9 combinaciones distintas a partir de un Sudoku original, y como cada vez se ocultan celdas distintas... parecen distintos.

sudoku_3_vertical.gif
sudoku_3_vertical.gif (7.79 KiB) Visto 8191 veces


¿Como ahorrar memoria?
Visto que almacenar el Sudoku en el listado más lo que ocupan las variables A$ y B$ en la zona de variables se comen casi una tercera parte de la memoria, la primera decisión ha sido el “como guardarlo”. Tras muchas vueltas he prescindido de la opción REM como en el “Mine-Field” ya que su gestión posterior ocupaba demasiadas líneas de código.

Como usaré el valor 1 y 9 varias veces, he declarado una variable “U” y otra “N” con ese valor. Se ha declarado mediante un VAL porque el valor ocupa 7 bytes en la zona de variables, pero en la zona del programa ocupa 3 bytes menos que de la forma tradicional y queda más claro que con un CODE.

Como sustitutivo del valor 0 uso NOT PI ya que solo ocupa 2 bytes en lugar de los 5 habituales, y con combinaciones o cálculos de las variables “U” y “N” consigo otros valores.

He evitado el uso de GOSUB-RETURN y otras cosas y en todo momento el programa se ejecuta de arriba a abajo.

Todo el código que desordena el Sudoku base ocupa 2 líneas (22 - 22) y es bastante rápido.

La parte de entrada de datos es casi igual a la del “Mine-Field” pero aquí si válido la entrada. Más o menos.

Para forzar la detención del programa he hecho la validación del Sudoku al final del programa. Si no es correcto vuelve a la línea 60 y si es correcto acaba porque no hay más líneas.

Por suerte, el programa ha ido justo de memoria pero he podido usar VAL y no CODE en la mayoría de los casos. El programa es más comprensible.

¿Que me gustaría que hiciera y no hace?
A continuación hago una lista de los que me habría gustado poder incorporar en esta versión:
- Poder introducir varios Sudoku distintos y poder aplicar más métodos de desorden.
- Mostrar un tablero más claro.
- Validar los valores de entrada: “0 a 8” en los dos primeros valores, y “1 a 9 o espacio” en el tercero.
- Revisar el estado de juego.
- Mostrar el Sudoku correcto.
- Incorporar pantalla de presentación y niveles de dificultad.


Con todo, el programa ocupa menos de 500 bytes, funciona perfectamente y con una excelente velocidad, así que, al final…

Otra prueba superada ¡!!


Os invito a probarlo.

DSC_1225.jpg
DSC_1225.jpg (93.25 KiB) Visto 8191 veces

DSC_1231.jpg
DSC_1231.jpg (63.12 KiB) Visto 8191 veces

DSC_1229.jpg
DSC_1229.jpg (80.4 KiB) Visto 8191 veces
Buscando la IP de la W.O.P.R.

Avatar de Usuario
mcleod_ideafix
Amiga 2500
Amiga 2500
Mensajes: 5316
Registrado: 06 Oct 2009, 04:12
Sistema Favorito: Spectrum 16Kb/48Kb
primer_sistema: Spectrum 16Kb/48Kb
consola_favorita: Vectrex
Primera consola: TV Games/Pong Clone
Ubicación: Jerez de la Frontera
Gracias dadas: 12 veces
Gracias recibidas: 54 veces
Contactar:

Re: SUDOKU 1K para ZX-81

Mensajepor mcleod_ideafix » 22 Jul 2013, 02:30

Impresionante :O

Una pregunta: en el Spectrum se puede ahorrar memoria definiendo las variables antes (con LET's escritos desde el modo inmediato) y luego grabando el programa, ya que con él se graban las variables.

En tu programa veo que hay algunas variables que se usan como constantes, tales como U y N. Dado que al grabar un programa en ZX81 se graba el código BASIC más las variables que estén definidas, ¿se podría reducir aún más la cantidad de memoria usada si se definen U y N aparte, sin que ocupen espacio en el BASIC? Lo mismo pasa con A$, que aunque se modifica en el código, no pasa nada si se ejecuta el programa varias veces, ya que lo que se hace con ella es "barajarla" para crear los distintos Sudokus.

Respondiéndome a mi mismo, con todo esto he grabado una versión del programa en formato .P , la cual sigue siendo jugable, y ocupa en total 662 bytes, variables incluidas. Al ejecutarse, lo que se expandirá es el área de pantalla en la forma en que has indicado, pasando de ocupar 25 bytes a 128 bytes (mirando un snapshot de memoria durante la ejecución del juego).

Si no he hecho mal los cálculos, en esta versión, mientras está ejecutándose, tras las variables usadas (es decir, hasta donde indica E_LINE) aún quedan 249 bytes hasta llegar al final del Kilobyte. Con la versión que has publicado, quedan 147 bytes.

Versión en formato .P "recortada" (autoejecutable. Para volver a jugar no usar RUN, sino GO TO 20)
Versión en formato .P del listado original de Dancresp (autoejecutable. Para volver a jugar, hacer RUN)

-- Actualizado 22 Jul 2013, 04:04 --

dancresp escribió:¿Que me gustaría que hiciera y no hace?
- Mostrar un tablero más claro.

Igual aquí puedo echar una mano :)

Con los bytes que he podido arañar al tener las variables exclusivamente en el área de variables, sin consumir espacio de código, he tocado un poquito la rutina de visualización para que los 9 grupos de números del Sudoku se vean más claramente. Esto hace que el programa ocupe más memoria no solamente por el código que he incluido, sino también porque la pantalla ahora ocupa un poco más que antes.

Queda así:
Imagen

Así las cosas, he tenido que sacrificar la velocidad de ejecución para aprovechar mejor la rutina de visualizado del tablero: en la versión original sólo se actualiza en pantalla la casilla que se acaba de introducir, por lo que la respuesta es muy rápida. Yo lo que he hecho es borrar la línea donde se hace esa actualización (que además no me valdría tal y como está, y tal y como está el tablero dibujado ahora), dejando solamente la actualización en B$, y a cambio, vuelvo a llamar a la rutina de visualización, que repinta el tablero actualizado. El repintado completo es más lento que imprimir unicamente un número, pero he podido salvar aún más memoria.

La otra cosa que he hecho es borrar B$ al principio del programa. Así hay espacio en memoria para generar la cadena que se calcula en la línea 10, y que implica tener durante un momento dos copias de A$.

El listado queda así:
Imagen

Este programa tal y como está, necesita que se definan las siguientes variables (si se teclea):

Código: Seleccionar todo

LET A$ = "todo el sudoku completo, como en la versión original"
LET N = 9
LET U = 1
LET T = 3


O bien, cargar la versión .P que contiene el código, las variables, y autoejecuta en la línea 1. Para jugar otra vez, GOTO 1

Tras esta pijotería, y durante su ejecución, al programa le quedan 124 bytes libres. Casi casi la mitad de lo que tenía antes (249 bytes).
Recuerda: cada vez que se implementa un sistema clásico en FPGA, Dios mata a un purista

Avatar de Usuario
scooter
Amiga 1200
Amiga 1200
Mensajes: 1031
Registrado: 17 Jul 2012, 09:25
primer_sistema: C64
Ubicación: Alicante

Re: SUDOKU 1K para ZX-81

Mensajepor scooter » 22 Jul 2013, 08:10

Eso es hilar fino. Lo mismo que se hace hoy en día, vamos.

dancresp
Amiga 1200
Amiga 1200
Mensajes: 1393
Registrado: 23 Dic 2008, 17:53
Sistema Favorito: MSX
primer_sistema: ZX81
Primera consola: Atari 2600
Gracias dadas: 3 veces
Gracias recibidas: 20 veces

Re: SUDOKU 1K para ZX-81

Mensajepor dancresp » 22 Jul 2013, 12:20

mcleod,

El tema de guardar las variables en memoria, pero no en el listado, para ahorrar unos 100 bytes es una de las primeras cosas que contemplé, pero no nos engañemos, cuando alguien carga un programa del ZX-81 lo primero que hace es RUN, y con tu versión deja de funcionar. Así que lo descarté. :?

Lo suyo sería hacer un SAVE con autoarranque y al completar el SUDOKU poner un mensaje, un PAUSE y un GOTO al inicio para que siempre funcione bien.

Como también intento que el listado sea lo más claro posible, el hacer referencias a variables que aparentemente no existen solo añade confusión. :-s

Respecto al aspecto (rima), tu versión queda más clara y bonita sin dudarlo, pero mis esfuerzos se han centrado a intentar conseguir el número máximo de variaciones sobre el SUDOKU base. La cosa ha quedado en 9 porque me quedé con las ganas de aplicar el "modo inversión diagonal" que lo habría dejado en 18.

Lo que no me acaba de cuadrar es la cantidad de memoria libre que dices que queda en la mía...
Doy fé que intentando meter el código del "modo inversión diagonal" que es muy corto:

20 IF INT RND THEN GOTO VAL "30"
22 LET B$=""
24 FOR F=U TO N*N
26 LET B$=A$(F)+B$
28 NEXT F

(Hay que tener en cuenta que la numeración de las líneas era distinta)

... me apareció un error 4, y según tus cálculos tendría que haber memoria suficiente.
No lo entiendo. :cry:

Y me ha hecho gracia ver como te has currado una versión en tan poquito tiempo. A ver si alguien más se anima. Se agradece que hayas subido tu ".P" y el mio. =D>

Un saludo, y no hay dos sin tres. :?:
Buscando la IP de la W.O.P.R.

Avatar de Usuario
UFO
Atari 1040 STf
Atari 1040 STf
Mensajes: 803
Registrado: 19 Feb 2010, 15:16
Sistema Favorito: PC
primer_sistema: MSX
consola_favorita: Sega Genesis/Megadrive
Primera consola: TV Games/Pong Clone
Gracias dadas: 43 veces
Gracias recibidas: 4 veces

Re: SUDOKU 1K para ZX-81

Mensajepor UFO » 22 Jul 2013, 13:14

Curradísimo e impresionante. Dancresp en su linea :)
-

Avatar de Usuario
mcleod_ideafix
Amiga 2500
Amiga 2500
Mensajes: 5316
Registrado: 06 Oct 2009, 04:12
Sistema Favorito: Spectrum 16Kb/48Kb
primer_sistema: Spectrum 16Kb/48Kb
consola_favorita: Vectrex
Primera consola: TV Games/Pong Clone
Ubicación: Jerez de la Frontera
Gracias dadas: 12 veces
Gracias recibidas: 54 veces
Contactar:

Re: SUDOKU 1K para ZX-81

Mensajepor mcleod_ideafix » 22 Jul 2013, 16:52

dancresp escribió:El tema de guardar las variables en memoria, pero no en el listado, para ahorrar unos 100 bytes es una de las primeras cosas que contemplé, pero no nos engañemos, cuando alguien carga un programa del ZX-81 lo primero que hace es RUN, y con tu versión deja de funcionar. Así que lo descarté. :?

Lo suyo sería hacer un SAVE con autoarranque y al completar el SUDOKU poner un mensaje, un PAUSE y un GOTO al inicio para que siempre funcione bien.

Eso es lo que *casi* tenía hecho: todos los .P que puse son autoarrancables, pero no me cabía el mensaje + el goto :( De ahí que en las "instrucciones" que escribí al lado de cada enlace al .P pusiera de qué forma se puede arrancar una nueva partida :)

dancresp escribió:Como también intento que el listado sea lo más claro posible, el hacer referencias a variables que aparentemente no existen solo añade confusión. :-s

Bueno... pensé que el reto era que cupiera el programa en 1K, y dado que es una técnica harto conocida en el BASIC del Spectrum, supuse que era válida en este caso :) Obviamente cualquier cosa que se haga para arañar bytes redunda en la menor legibilidad del código: a un profano en el ZX81 le costaría trabajo adivinar que el valor de N es 9 por ejemplo. De hecho al ver el listado y ver ese CODE con el símbolo gráfico, pensé que N tenía un valor mucho mayor, acostumbrado como estoy al BASIC del Spectrum.

dancresp escribió:Lo que no me acaba de cuadrar es la cantidad de memoria libre que dices que queda en la mía...
Doy fé que intentando meter el código del "modo inversión diagonal" que es muy corto:

20 IF INT RND THEN GOTO VAL "30"
22 LET B$=""
24 FOR F=U TO N*N
26 LET B$=A$(F)+B$
28 NEXT F

... me apareció un error 4, y según tus cálculos tendría que haber memoria suficiente.
No lo entiendo. :cry:

En los cálculos de la memoria libre entra todo lo que hay después de E_LINE, y eso incluye, por desgracia, la pila de máquina, la pila del calculador, el espacio temporal para cadenas y vaya usted a saber cuántas cosas más. Todo eso ocupa espacio, y tu código, además de ocupar espacio, hace uso de la pila del calculador, el espacio temporal de cadenas, etc. Probablemente, con el manejo de números y cadenas que ya se hace en cualquiera de las versiones que funciona, el espacio para añadir código sin que nos quedemos sin espacio para estas otras cosas sea realmente mucho más reducido de lo que calculé. Por cierto.... ¿la línea 20 salta alguna vez a la 30? Me parece que INT RND nunca llega a ser 1, siempre será 0, ¿no?

dancresp escribió:(Hay que tener en cuenta que la numeración de las líneas era distinta)

Ehmmmm... sí :) Resulta que estaba quedándome sin números de línea, y editar en el ZX81 con 1K de RAM estando al límite de uso es un poco incómodo, así que opté por coger el fichero .P de la versión tuya, editarlo con un editor hexadecimal, y cambiar a mano los números de línea en el espacio de BASIC :D
Recuerda: cada vez que se implementa un sistema clásico en FPGA, Dios mata a un purista

dancresp
Amiga 1200
Amiga 1200
Mensajes: 1393
Registrado: 23 Dic 2008, 17:53
Sistema Favorito: MSX
primer_sistema: ZX81
Primera consola: Atari 2600
Gracias dadas: 3 veces
Gracias recibidas: 20 veces

Re: SUDOKU 1K para ZX-81

Mensajepor dancresp » 22 Jul 2013, 23:07

mcleod_ideafix escribió:Bueno... pensé que el reto era que cupiera el programa en 1K, y dado que es una técnica harto conocida en el BASIC del Spectrum, supuse que era válida en este caso :) Obviamente cualquier cosa que se haga para arañar bytes redunda en la menor legibilidad del código: a un profano en el ZX81 le costaría trabajo adivinar que el valor de N es 9 por ejemplo. De hecho al ver el listado y ver ese CODE con el símbolo gráfico, pensé que N tenía un valor mucho mayor, acostumbrado como estoy al BASIC del Spectrum.

En este caso el fin justifica los medios, con lo que tu solución es tan válida como la mía. Faltaría. =D>
Solo que, para mi gusto, las posibilidades de que el programa dejara de funcionar (haciendo RUN) me hicieron decantar por mi solución.

mcleod_ideafix escribió:En los cálculos de la memoria libre entra todo lo que hay después de E_LINE, y eso incluye, por desgracia, la pila de máquina, la pila del calculador, el espacio temporal para cadenas y vaya usted a saber cuántas cosas más. Todo eso ocupa espacio, y tu código, además de ocupar espacio, hace uso de la pila del calculador, el espacio temporal de cadenas, etc. Probablemente, con el manejo de números y cadenas que ya se hace en cualquiera de las versiones que funciona, el espacio para añadir código sin que nos quedemos sin espacio para estas otras cosas sea realmente mucho más reducido de lo que calculé. Por cierto.... ¿la línea 20 salta alguna vez a la 30? Me parece que INT RND nunca llega a ser 1, siempre será 0, ¿no?

Me di cuenta de esto con el "Mine Field" y una línea que contiene varias sumas que dependes de AND. Siempre me daba error 4 allí.
Y lo de la línea 20 no hagas caso. A veces escribo el programa en un papel, en el tren, y cuando llego a casa lo paso al equipo. Siempre falla algo... >|

mcleod_ideafix escribió:Ehmmmm... sí :) Resulta que estaba quedándome sin números de línea, y editar en el ZX81 con 1K de RAM estando al límite de uso es un poco incómodo, así que opté por coger el fichero .P de la versión tuya, editarlo con un editor hexadecimal, y cambiar a mano los números de línea en el espacio de BASIC :D

Lo de la numeración me refería a mi versión del programa, ya que hay saltos en la numeración de las líneas.

¿Y con que programa "remenas" los programas ".P" del ZX-81?

Y si te quieres entretener más, "atrévete" a mete mano al "Mine Field" antes de que llegue el tercer programa de 1 KB... :jumper:
Buscando la IP de la W.O.P.R.

Avatar de Usuario
mcleod_ideafix
Amiga 2500
Amiga 2500
Mensajes: 5316
Registrado: 06 Oct 2009, 04:12
Sistema Favorito: Spectrum 16Kb/48Kb
primer_sistema: Spectrum 16Kb/48Kb
consola_favorita: Vectrex
Primera consola: TV Games/Pong Clone
Ubicación: Jerez de la Frontera
Gracias dadas: 12 veces
Gracias recibidas: 54 veces
Contactar:

Re: SUDOKU 1K para ZX-81

Mensajepor mcleod_ideafix » 22 Jul 2013, 23:28

dancresp escribió:¿Y con que programa "remenas" los programas ".P" del ZX-81?

Con un editor hexadecimal que tengo para Windows, concretamente el HxD. Abro el .P que es pequeñito (cabe en una pantalla), busco la zona donde comienza el BASIC mirando el valor de la variable del sistema correspondiente (también guardada en el .P), y lo "parseo" a ojo buscando los números de línea para cambiarlos.
Recuerda: cada vez que se implementa un sistema clásico en FPGA, Dios mata a un purista


Volver a “Programación”

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 8 invitados