Para motivarlos a participar, voy a explicar un poco más en qué consiste esta competencia.
La idea es hacer un juego en sólo 10 líneas de código BASIC, lo cual no quiere decir que sean apenas 10 instrucciones, sino que en cada línea puede haber muchas sentencias separadas por ":" (dos puntos), lo que permite multiplicar por harto la cantidad de instrucciones para un programa.
Depende del computador utilizado la cantidad máxima de caracteres que pueden ingresarse en una línea lógica. Por ejemplo, en el Atari son 120 (3 líneas físicas de 40 caracteres mediante edición en pantalla) y en Commodore me parece que son 80 (que me corrijan en esto, plis), sin embargo se pueden ingresar líneas directamente de archivo en lugar del editor en pantalla, con lo que en general se puede llegar a 256, y por eso se dividieron en categorías: 80, 120 y 256.
En general hay varias versiones de BASIC por máquina y dependiendo de la versión del BASIC que se escoja se pueden abreviar las instrucciones para agilizar el ingreso de código. En el caso de AtariBASIC y TurboBASIC XL en Atari se puede utilizar un punto después de las primeras letras (ej: "G." o "GO." equivalen a "GOTO"), y en Commodore BASIC V2 se hace usando la letra que corresponde a la posición del punto pero con SHIFT, lo que hace que aparezca un caracter gráfico en pantalla. Además, es posible omitir los espacios entre instrucciones y parámetros, pudiendo aprovechar mejor el espacio disponible, aunque haciendo la lectura casi ilegible. Pero ésto se ve así sólo en el ingreso, ya que las instrucciones son convertidas en
tokens de 1 byte, y al listar el programa ingresado, se expanden a la instrucción completa, por lo que la línea lógica tendrá un largo mayor al original y que lo permitido en la categoría respectiva, pero eso no importa... sólo es relevante el largo de línea al momento del ingreso. Si a eso le sumamos la posibilidad de usar variables con nombre de apenas 1 caracter, y de utilizar los números de línea de 0 al 9, vemos que contamos con mucho espacio para hacer verdaderas maravillas.
Pero BASIC tiene una restricción: el destino de los "GOTO" es un número de línea, y de esas tenemos pocas, por lo que lo conveniente es simplemente no utilizarlo y preferir la programación estructurada con "REPEAT-UNTIL", "WHILE-END" o incluso "FOR-NEXT" con "STEP 0" cuando no existan las primeras estructuras de control. Estos
loops pueden anidarse o ponerse secuencialmente en una misma línea de código o distribuirse entre varias, transformando los números de línea en sólo un trámite, lo que permite ponerlos donde sea para ajustar las líneas al máximo largo posible.
Por lo tanto, para el desarrollo de un juego en esta competencia, hay que elegir entre varias estrategias o combinar algunas de éstas:
Procedimiento completamente manual:
- Programar libremente en el compuador real (Atari, Commodore, MSX o lo que sea), usando una instrucción por línea y evitando el uso de "GOTO", "GOSUB" o cualquier instrucción que requiera especificar un número de línea.
- Estando satisfecho con el resultado, el listado final se traspasa a un editor de texto común y se comienza a transformar el código, abreviando cada instrucción y eliminando los espacios innecesarios. Si el programa puede ser cargado con "ENTER" (en Atari en lugar de "LOAD" o sus equivalentes), excelente, pero si algo falla, hay que revisar si fue ingresada una abreviación inválida o que se interpretó como otra (ej: "STOP" en lugar de "STEP" en Commodore) hasta que el programa funcione como corresponde.
- Reducir de la cantidad de líneas eliminando todos los números de línea y concatenando todas instrucciones en una única línea separando con ":" (dos puntos) y poniendo el número de línea cero al inicio. Verificar que el largo final de esa única línea no sea superior a 10 veces el largo permitido de la categoría seleccionada. Luego se debe buscar qué instrucción se encuentra en el largo máximo de línea de la categoría y cortar en el ":" anterior, eliminando éste y poniendo el número de línea correlativo a la nueva línea. Si lograste que la última línea fuera la 9 o inferior, ¡listo!
- Si teniendo un largo de línea única inferior a 10 veces máximo no se logró dividir en sólo 10 líneas, eso se debió a que sobró mucho espacio al final de algunas líneas porque la instrucción que iba ahí no cabía y debió ser pasada a la siguiente línea. Eso se puede resolver en muchos casos cambiando el orden de algunas instrucciones o estructuras completas. Por ejemplo si una línea tiene 10 espacios al final, siendo la última instrucción de esa línea "A=1" (largo 3) y la primera de la siguiente "B(M)=X+200*Y" (largo 12), basta intercambiarlas ya que no se afectan mutuamente y 12 es menor 3+10. Si no se puede cambiar el orden de las instrucción, es posible dividir una instrucción larga en dos y dejar un trozo en cada línea, por ejemplo: "IF(X+Y)/2>10" puede ser separada como "Z=X+Y" al final de la primera (con Z una variable auxiliar) e "IF10<Z/2" en la segunda.
- Por otro lado, si la idea era participar en la categoría PUR-120 pero el largo de la línea única resultó ser 1300, hay 2 alternativas: ver si se puede optimizar el código para bajar de 1200, o definitivamente pasar a la categoría EXTREM-256 y aprovechar los 1200 caracteres adicionales para incluir más cosas en el juego (esto fue lo que sucedió con mi juego "Where's my cheese?", que no pude bajar de 12 líneas de largo 120, así que aproveché el saldo para incorporar más elementos y opciones).
- Sin entrar a la optimización de código para reducir el número de instrucciones porque dependerá de cada caso, puedo mencionar otras cosas simples a tener en cuenta:
. Cuando un valor se repite varias veces en el código, se puede reemplazar por una variable a la que se le asigna el valor una vez. Por ejemplo, si el valor 40 aparece 8 veces en el código, esos son 16 bytes en total del listado, pero si se asigna a la variable Z, se ganan 3 bytes: "Z=40:" ocupa 5 bytes y 8 son los reemplazos en el código, totalizando 13. Puede parecer poco, pero si el valor tenía más digitos, la ganancia es mayor, y 1 o 2 bytes ganados en una línea puede permitir acomodar mejor las instrucciones en ella, trayendo la primera de la linea siguiente.
. Utilizar DPEEK y DPOKE para leer y escribir words (2 bytes a la vez), y evitar tener que multiplicar por 256 uno de ellos y sumar el otro, o bien para modificar 2 registros consecutivos de memoria a la vez.
. Modificar directamente los registros del sistema en lugar usar de instrucciones BASIC para hacer lo mismo. Por ejemplo, en Atari, un "DPOKE709,15" (11 caracteres) hace lo mismo que "SE.1,0,15:SE.2,0,0" (18 caracteres).
. Aprovechar intrucciones avanzadas como "MOVE" en TurboBASIC XL para Atari en vez de ciclos "FOR-NEXT" para mover bloques de memoria. Esto es útil para la manipulación de P/M (
sprites), redefinir el set de caracteres o incluso cambiar todos los registros de color de una vez.
. Usar "POKE" para modificar cosas en la pantalla en lugar de "POSITION" y "PRINT". Además de ocupar menos caracteres en el listado y ser más rápido de ejecutar, permite simplificar el código al no necesitar manipular estructuras de datos adicionales para almacenar información de los objetos en pantalla... ellos ya están ahí y se puede verificar fácilmente una colisión.
Procedimiento manual simplificado:
- Consiste en escribir directamente las 10 líneas de código en formato abreviado en un editor para ir viendo cómo va la cosa. Es más complejo de editar y mantener, por lo que lo recomiendo sólo cuando se usó el procedimiento anterior, y luego se quieren hacer mejoras al código ya reducido, pero tiene la ventaja que basta hacer un
copy&paste entre el notepad y el emulador para ir probando durante el desarrollo.
Procedimiento semiautomático:
- Para algunas plataformas hay utilitarios que permiten escribir código en notepad y procesarlo para generar directamente el programa compactado. Es el caso del
TurboBasic XL parser tool para Atari o el
CBM .prg Studio para Commodore.
- Si aún así se obtienen más de 10 líneas, hay que hacer algunas de las cosas que mencioné en el 1er método, además de cualquier optimización de código que puedan realizar.
Lo interesante de esta competencia es el desafío de hacer más de lo que uno conoce como restricción natural. Yo mismo me sorprendí con lo que pude hacer en los 3 juegos que ya presenté. El del perrito y las pulgas era originalmente con caracteres simples, pero como sobró espacio, pude agregar la modificación del set de caracteres y darle vida al personaje. Como dato adicional, éste fue mi primer programa hecho en TurboBASIC XL, y ahí uso y abuso de los ciclos "WHILE-WEND" y de DPEEK y DPOKE.
Algo similar ocurrió con el de los invasores, pero tuve que optimizar harto código para poder incorporar la modificación al set de caracteres y lamentablemente sólo pude poner uno de los 2 que se necesitaban para que tuvieran el movimiento del juego original, y obviamente las barreras fueron omitidas completamente. También quedó fuera el UFO y sólo se puede jugar una ronda... no hay puntaje y la consigna es ¡ganar o morir! Probablemente son elementos que se pueden incorporar cambiando de categoría a EXTREM-256, pero no es algo que me caliente la cabeza por ahora. A diferencia del juego del perrito, en éste utilicé P/M (
sprites) para el cañon y las municiones, y las características del hardware para posicionarlos y detectar colisiones, simplificando bastante mi código.
Ya... ¿nadie más se anima a participar? ¡Vamos! Inténtenlo y déjense sorprender con el resultado.
