Diferencias
Muestra las diferencias entre dos versiones de la página.
Ambos lados, revisión anterior Revisión previa Próxima revisión | Revisión previa Próxima revisiónAmbos lados, revisión siguiente | ||
cursos:z88dk:z88dklife [09-08-2007 12:24] – sromero | cursos:z88dk:z88dklife [09-08-2007 12:41] – sromero | ||
---|---|---|---|
Línea 3: | Línea 3: | ||
Hasta el momento hemos aprendido a instalar y compilar programas con z88dk, utilizando para ellos ejemplos prácticos basados en la creación de una Aventura Conversacional de Texto. En nuestros anteriores números, y gracias a los ejemplos de aventuras conversacionales, | Hasta el momento hemos aprendido a instalar y compilar programas con z88dk, utilizando para ellos ejemplos prácticos basados en la creación de una Aventura Conversacional de Texto. En nuestros anteriores números, y gracias a los ejemplos de aventuras conversacionales, | ||
+ | |||
+ | |||
===== El Juego de la Vida ===== | ===== El Juego de la Vida ===== | ||
Línea 14: | Línea 16: | ||
- | < | + | //El juego de la vida es un autómata celular diseñado por el matemático británico John Horton Conway en 1970. Es el mejor ejemplo de un autómata celular. Hizo su primera aparición pública en el número de octubre de 1970 de la revista Scientific American, en la columna de juegos matemáticos de Martin Gardner. Desde un punto de vista teórico, es interesante porque es equivalente a una máquina universal de Turing, es decir, todo lo que se puede computar algorítmicamente se puede computar en el juego de la vida.// |
- | El juego de la vida es un autómata celular diseñado por el matemático británico John Horton Conway en 1970. Es el mejor ejemplo de un autómata celular. Hizo su primera aparición pública en el número de octubre de 1970 de la revista Scientific American, en la columna de juegos matemáticos de Martin Gardner. Desde un punto de vista teórico, es interesante porque es equivalente a una máquina universal de Turing, es decir, todo lo que se puede computar algorítmicamente se puede computar en el juego de la vida. | + | |
- | Desde su publicación, | + | //Desde su publicación, |
- | + | ||
- | La vida tiene una variedad de patrones reconocidos que provienen de determinadas posiciones iniciales. Poco después de la publicación, | + | //La vida tiene una variedad de patrones reconocidos que provienen de determinadas posiciones iniciales. Poco después de la publicación, |
- | + | ||
- | Cabe decir que el "Juego de la Vida" causó furor en 1970, hasta el punto en que se convirtió en el divertimento de muchos " | + | //Cabe decir que el "Juego de la Vida" causó furor en 1970, hasta el punto en que se convirtió en el divertimento de muchos " |
- | </code> | + | |
===== Reglas básicas de El Juego de la Vida ===== | ===== Reglas básicas de El Juego de la Vida ===== | ||
Línea 28: | Línea 28: | ||
El Juego de la Vida se basa en una matriz de un tamaño determinado (como por ejemplo, 50x50, ó 32x32), que podríamos considerar nuestro "caldo de cultivo", | El Juego de la Vida se basa en una matriz de un tamaño determinado (como por ejemplo, 50x50, ó 32x32), que podríamos considerar nuestro "caldo de cultivo", | ||
- | Células vivas en la cuadrícula | + | |
- | Células vivas en la cuadrícula | + | {{ cursos: |
El Juego de la Vida no requiere interacción por parte del usuario: a partir de un estado inicial (células diseminadas por el caldo de cultivo) se aplican una serie de reglas y se obtiene una nueva generación de células en dicho " | El Juego de la Vida no requiere interacción por parte del usuario: a partir de un estado inicial (células diseminadas por el caldo de cultivo) se aplican una serie de reglas y se obtiene una nueva generación de células en dicho " | ||
Línea 42: | Línea 43: | ||
Con estas sencillas reglas se obtienen unos resultados sorprendentes, | Con estas sencillas reglas se obtienen unos resultados sorprendentes, | ||
- | Implementación del Juego de la Vida en el Spectrum | + | ===== Implementación del Juego de la Vida en el Spectrum |
Antes de codificar propiamente en C lo que es el " | Antes de codificar propiamente en C lo que es el " | ||
+ | < | ||
tablero[ANCHO][ALTO] | tablero[ANCHO][ALTO] | ||
Línea 56: | Línea 58: | ||
| | ||
fin repetir | fin repetir | ||
+ | </ | ||
El anterior sería el esqueleto de la función principal, que como podréis ver en el código, se corresponde con la siguiente función main(): | El anterior sería el esqueleto de la función principal, que como podréis ver en el código, se corresponde con la siguiente función main(): | ||
+ | <code c> | ||
//--- Funcion principal main() ---------------------------- | //--- Funcion principal main() ---------------------------- | ||
int main( void ) | int main( void ) | ||
Línea 76: | Línea 80: | ||
| | ||
} | } | ||
+ | </ | ||
Con esto, el programa principal realizaría la impresión en pantalla de la generación actual de células (para que podamos ver la evolución visualmente) mediante la función Dibujar_Generación_Actual(). Tras esto, se calcularía la siguiente generación de células aplicando las reglas anteriormente explicadas, dentro de la función Calcular_Siguiente_Generacion() . Si en cualquier momento se pulsa la tecla ' | Con esto, el programa principal realizaría la impresión en pantalla de la generación actual de células (para que podamos ver la evolución visualmente) mediante la función Dibujar_Generación_Actual(). Tras esto, se calcularía la siguiente generación de células aplicando las reglas anteriormente explicadas, dentro de la función Calcular_Siguiente_Generacion() . Si en cualquier momento se pulsa la tecla ' | ||
- | Función Crear_Generación_Aleatoria() | + | ===== Crear_Gen_Aleatoria() ===== |
La función Crear_Generación_Aleatoria() se encargaría de vaciar el " | La función Crear_Generación_Aleatoria() se encargaría de vaciar el " | ||
+ | < | ||
Crear_Generacion_Aleatoria: | Crear_Generacion_Aleatoria: | ||
Para todo ' | Para todo ' | ||
Línea 92: | Línea 99: | ||
ycel = y_aleatoria() | ycel = y_aleatoria() | ||
| | ||
+ | </ | ||
En nuestro pseudocódigo utilizamos una matriz [ancho*alto] para representar el caldo de cultivo. Dentro de esta matriz, cada posición matriz[x][y] puede contener o no una célula mediante los valores de 0 y 1 respectivamente. Así, en un tablero de 32x32, podemos poner una célula justo en la mitad del tablero ejecutando " | En nuestro pseudocódigo utilizamos una matriz [ancho*alto] para representar el caldo de cultivo. Dentro de esta matriz, cada posición matriz[x][y] puede contener o no una célula mediante los valores de 0 y 1 respectivamente. Así, en un tablero de 32x32, podemos poner una célula justo en la mitad del tablero ejecutando " | ||
+ | < | ||
dimensiones: | dimensiones: | ||
alto = 16 | alto = 16 | ||
Línea 101: | Línea 110: | ||
Crear célula: | Crear célula: | ||
Borrar célula: | Borrar célula: | ||
+ | </ | ||
En el caso de z88dk, en lugar de utilizar matrices bidimensionales del tipo [ancho][alto] necesitaremos utilizar un vector de tamaño [ancho*alto], | En el caso de z88dk, en lugar de utilizar matrices bidimensionales del tipo [ancho][alto] necesitaremos utilizar un vector de tamaño [ancho*alto], | ||
Línea 106: | Línea 116: | ||
De esta forma, ahora los accesos al mapa de células quedarían así: | De esta forma, ahora los accesos al mapa de células quedarían así: | ||
+ | < | ||
Tablero: | Tablero: | ||
Temporal: | Temporal: | ||
Crear célula: | Crear célula: | ||
Borrar célula: | Borrar célula: | ||
+ | </ | ||
Es decir, que un array unidimensional se puede tratar como un array bidimensional donde cada una de las líneas se coloca a continuación de la anterior, y acceder a cada uno de sus elementos mediante " | Es decir, que un array unidimensional se puede tratar como un array bidimensional donde cada una de las líneas se coloca a continuación de la anterior, y acceder a cada uno de sus elementos mediante " | ||
Línea 115: | Línea 127: | ||
Para facilitar el tratamiento de los datos, en el código definimos los siguientes macros o #defines (que hacen las veces de funciones, pero que en lugar de ser llamadas son " | Para facilitar el tratamiento de los datos, en el código definimos los siguientes macros o #defines (que hacen las veces de funciones, pero que en lugar de ser llamadas son " | ||
+ | <code c> | ||
#define Celula(x, | #define Celula(x, | ||
#define TempCelula(x, | #define TempCelula(x, | ||
+ | </ | ||
De esta forma en nuestro código podemos hacer simplemente: | De esta forma en nuestro código podemos hacer simplemente: | ||
+ | <code c> | ||
valor = Celula( 10, 10 ); | valor = Celula( 10, 10 ); | ||
| | ||
- | + | </ | |
Y al compilar, este código será sustituído por: | Y al compilar, este código será sustituído por: | ||
+ | <code c> | ||
valor = (mapa[((10)*32)+(10)]); | valor = (mapa[((10)*32)+(10)]); | ||
| | ||
+ | </ | ||
Obviamente el código utilizando nuestros #defines es mucho más claro y más fácil de mantener. Si encontramos una manera más óptima de acceder a las células, sólo hará falta modificarlo en el #define para que al recompilar, el cambio se aplique en todo el código fuente (en lugar de tener que ir cambiándolo llamada a llamada, como nos ocurriría en caso de no haber usado #defines). Y como ejemplo de " | Obviamente el código utilizando nuestros #defines es mucho más claro y más fácil de mantener. Si encontramos una manera más óptima de acceder a las células, sólo hará falta modificarlo en el #define para que al recompilar, el cambio se aplique en todo el código fuente (en lugar de tener que ir cambiándolo llamada a llamada, como nos ocurriría en caso de no haber usado #defines). Y como ejemplo de " | ||
+ | < | ||
x * 32 = x << 5 | x * 32 = x << 5 | ||
+ | </ | ||
Reemplazando esto en nuestro #define: | Reemplazando esto en nuestro #define: | ||
+ | < | ||
#define Celula(x, | #define Celula(x, | ||
#define TempCelula(x, | #define TempCelula(x, | ||
+ | </ | ||
Se deja al lector como ejercicio realizar la prueba de reemplazar <<5 por *32 en el código fuente y verificar que efectivamente, | Se deja al lector como ejercicio realizar la prueba de reemplazar <<5 por *32 en el código fuente y verificar que efectivamente, | ||
Línea 142: | Línea 163: | ||
El código final correspondiente para nuestra función de generación aleatoria de estados iniciales es el siguiente: | El código final correspondiente para nuestra función de generación aleatoria de estados iniciales es el siguiente: | ||
+ | <code c> | ||
//--- Rellenar el tablero con valores aleat. -------------- | //--- Rellenar el tablero con valores aleat. -------------- | ||
void GenLife( void ) | void GenLife( void ) | ||
Línea 162: | Línea 184: | ||
TempCelula(x, | TempCelula(x, | ||
} | } | ||
- | |||
} | } | ||
+ | </ | ||
Como cosas que destacar de esta función tenemos: | Como cosas que destacar de esta función tenemos: | ||
- | * srand(clock()) : Inicializa la semilla de números aleatorios usando como base el reloj del Spectrum (por reloj del Spectrum consideramos el contador que tiene el sistema y que contabiliza el número de segundos transcurridos desde el inicio del ordenador). Esto asegura que cada vez que ejecutemos el programa (en un Spectrum real o un emulador que no tenga carga automática del .tap al abrirlo con él), tengamos una secuencia de números aleatorios diferentes y no obtengamos siempre la misma generación inicial de células. Los números aleatorios los obtendremos posteriormente con rand(). | + | |
- | * BORDER() y CLS(): Estas 2 funciones cambian el color del borde y borran la pantalla respectivamente. Están implementadas en ensamblador, | + | |
- | * printf( " | + | |
+ | |||
+ | |||
+ | ===== Dibujar_Gen_Actual() ===== | ||
- | La función Dibujar_Generación_Actual() | ||
En cada paso del bucle principal tenemos que redibujar la colonia actual de células. El pseudocódigo para hacer esto es: | En cada paso del bucle principal tenemos que redibujar la colonia actual de células. El pseudocódigo para hacer esto es: | ||
+ | < | ||
Dibujar_Generacion_Actual: | Dibujar_Generacion_Actual: | ||
Para todo ' | Para todo ' | ||
Si tablero[x][y] = 0: Dibujar Blanco en (x,y) | Si tablero[x][y] = 0: Dibujar Blanco en (x,y) | ||
Si tablero[x][y] = 1: Dibujar Célula en (x,y) | Si tablero[x][y] = 1: Dibujar Célula en (x,y) | ||
+ | </ | ||
Traducido a código C: | Traducido a código C: | ||
+ | <code c> | ||
#define DrawCell(x, | #define DrawCell(x, | ||
*((unsigned char *) (0x4000 + 6144 + ((y)<< | *((unsigned char *) (0x4000 + 6144 + ((y)<< | ||
Línea 196: | Línea 223: | ||
} | } | ||
} | } | ||
+ | </ | ||
La clave de esta función está en la macro DrawCell, que es la que efectivamente pinta en pantalla las células. Lo interesante de la función es que no dibuja nada en pantalla, sino que modifica los atributos de la videomemoria para cambiar los " | La clave de esta función está en la macro DrawCell, que es la que efectivamente pinta en pantalla las células. Lo interesante de la función es que no dibuja nada en pantalla, sino que modifica los atributos de la videomemoria para cambiar los " | ||
Línea 205: | Línea 233: | ||
Cuando escribimos un byte en una de esas posiciones estaremos modificando los atributos del caracter (x,y) de la pantalla de forma que: | Cuando escribimos un byte en una de esas posiciones estaremos modificando los atributos del caracter (x,y) de la pantalla de forma que: | ||
+ | < | ||
| | ||
+ | </ | ||
El byte que escribamos define los atributos con el siguiente formato: | El byte que escribamos define los atributos con el siguiente formato: | ||
- | | + | ^ Bits ^ Significado ^ |
- | bits 3...5: papel (0 a 7, orden de los colores del Spectrum) | + | | 0...2 | tinta (0 a 7, orden de los colores del Spectrum) |
- | bit | + | | 3...5 | papel (0 a 7, orden de los colores del Spectrum) |
- | bit | + | | 6 | brillo (1 ó 0, con o sin brillo) |
+ | | 7 | flash (1 ó 09, con o sin parpadeo) | ||
Los colores están definidos igual que se detalla en el manual del Spectrum, es decir: | Los colores están definidos igual que se detalla en el manual del Spectrum, es decir: | ||
- | 0. - negro | + | ^ Valor ^ Color ^ |
- | | + | | 0 | negro | |
- | | + | | 1 | azul | |
- | | + | | 2 | rojo | |
- | | + | | 3 | púrpura o magenta |
- | | + | | 4 | verde | |
- | | + | | 5 | cyan | |
- | | + | | 6 | amarillo |
+ | | 7 | blanco | ||
Con los bits indicandos anteriormente, | Con los bits indicandos anteriormente, | ||
+ | <code c> | ||
| | ||
+ | </ | ||
Por ejemplo, para establecer el caracter (2,5) con color verde (4) sobre fondo rojo (2) y con flash, podemos utilizar el siguiente código: | Por ejemplo, para establecer el caracter (2,5) con color verde (4) sobre fondo rojo (2) y con flash, podemos utilizar el siguiente código: | ||
+ | <code c> | ||
// memaddr = 22528 + (y*32) + x | // memaddr = 22528 + (y*32) + x | ||
| | ||
Línea 236: | Línea 271: | ||
// escribir en memaddr (flash<< | // escribir en memaddr (flash<< | ||
| | ||
+ | </ | ||
De este modo podemos activar y desactivar cuadros completos de pantalla modificando su tinta y papel. Este método es mucho más rápido para nuestro programa que dibujar los 8x8 pixels de cada carácter para dibujar o apagar las células (una sóla escritura en memoria modifica el estado de 64 píxeles simultáneamente), | De este modo podemos activar y desactivar cuadros completos de pantalla modificando su tinta y papel. Este método es mucho más rápido para nuestro programa que dibujar los 8x8 pixels de cada carácter para dibujar o apagar las células (una sóla escritura en memoria modifica el estado de 64 píxeles simultáneamente), | ||
- | La función Calcular_Siguiente_Generación() | + | ===== Calcular_Siguiente_Gen() ===== |
En este momento ya tenemos una función que nos genera una colonia inicial de células (aleatoria), | En este momento ya tenemos una función que nos genera una colonia inicial de células (aleatoria), | ||
Línea 245: | Línea 282: | ||
El pseudocódigo es el siguiente: | El pseudocódigo es el siguiente: | ||
+ | < | ||
Calcular_Siguiente_Generación: | Calcular_Siguiente_Generación: | ||
Para todo ' | Para todo ' | ||
Línea 260: | Línea 298: | ||
Si no está habitada: | Si no está habitada: | ||
Si tiene 2 ó 3 vecinas : Creamos una célula | Si tiene 2 ó 3 vecinas : Creamos una célula | ||
+ | </ | ||
El código en C que implemente este algoritmo es: | El código en C que implemente este algoritmo es: | ||
+ | <code c> | ||
//--- Funcion donde se simula la vida --------------------- | //--- Funcion donde se simula la vida --------------------- | ||
void Life( void ) | void Life( void ) | ||
Línea 315: | Línea 355: | ||
} // fin for y | } // fin for y | ||
} | } | ||
+ | </ | ||
Para compilar el programa puede utilizarse el siguiente comando: | Para compilar el programa puede utilizarse el siguiente comando: | ||
+ | < | ||
zcc +zxansi -vn -O1 zxlife.c -o zxlife.bin -lndos | zcc +zxansi -vn -O1 zxlife.c -o zxlife.bin -lndos | ||
bin2tap zxlife.bin zxlife.tap | bin2tap zxlife.bin zxlife.tap | ||
rm -f zcc_opt.def | rm -f zcc_opt.def | ||
+ | </ | ||
Si ejecutamos el programa en nuestro Spectrum (o en un emulador) veremos la evolución de las células en tiempo real en nuestra pantalla: | Si ejecutamos el programa en nuestro Spectrum (o en un emulador) veremos la evolución de las células en tiempo real en nuestra pantalla: | ||
- | Simulaciones en nuestro Spectrum | + | |
- | Simulaciones en nuestro Spectrum | + | {{ cursos: |
- | Simulaciones en nuestro Spectrum | + | |
- | Simulaciones en nuestro Spectrum | + | |
+ | {{ cursos: | ||
Cada vez que pulsemos ' | Cada vez que pulsemos ' | ||
- | Optimizaciones del Juego de la Vida | + | |
+ | ===== Optimizaciones del Juego de la Vida ===== | ||
Todos los programas pueden ser optimizados, | Todos los programas pueden ser optimizados, | ||
Línea 336: | Línea 383: | ||
En el caso de zxlife, podemos también optimizar el código de obtención de nuevas generaciones evitando cálculos innecesarios. Concretamente, | En el caso de zxlife, podemos también optimizar el código de obtención de nuevas generaciones evitando cálculos innecesarios. Concretamente, | ||
+ | <code c> | ||
// Obtenemos el numero de celulas vecinas | // Obtenemos el numero de celulas vecinas | ||
| | ||
Línea 346: | Línea 394: | ||
| | ||
| | ||
+ | </ | ||
Cada vez que llamamos a Celula(x,y) estamos realizando el cálculo de la posición absoluta de la célula dentro del vector (en nuestra conversión bidimensional a unidimensional) mediante una serie de operaciones matemáticas. Si tenemos en cuenta que todas las células vecinas están a una distancia fija de -1, +2, -33, -32, -31 y +31, +32 y +33 de cada célula, podemos convertir esto a: | Cada vez que llamamos a Celula(x,y) estamos realizando el cálculo de la posición absoluta de la célula dentro del vector (en nuestra conversión bidimensional a unidimensional) mediante una serie de operaciones matemáticas. Si tenemos en cuenta que todas las células vecinas están a una distancia fija de -1, +2, -33, -32, -31 y +31, +32 y +33 de cada célula, podemos convertir esto a: | ||
+ | <code c> | ||
| | ||
Línea 356: | Línea 406: | ||
mapa[ offset+1 | mapa[ offset+1 | ||
mapa[ offset+32 ] + mapa[ offset+33 ]; | mapa[ offset+32 ] + mapa[ offset+33 ]; | ||
+ | </ | ||
Esto es así porque como podemos ver en la siguiente figura, podemos obtener las 8 células vecinal a partir de un mismo offset calculado: | Esto es así porque como podemos ver en la siguiente figura, podemos obtener las 8 células vecinal a partir de un mismo offset calculado: | ||
- | Offset de las 8 células vecinas de una dada | + | |
- | Offset de las 8 células vecinas de una dada | + | {{ cursos: |
El código resultante de la optimización sería el siguiente: | El código resultante de la optimización sería el siguiente: | ||
+ | <code c> | ||
//--- Funcion donde se simula la vida --------------------- | //--- Funcion donde se simula la vida --------------------- | ||
void Life( void ) | void Life( void ) | ||
Línea 411: | Línea 464: | ||
} // fin for y | } // fin for y | ||
} | } | ||
+ | </ | ||
Aparte de la optimización de la función de cálculo de nuevas generaciones, | Aparte de la optimización de la función de cálculo de nuevas generaciones, | ||
+ | <code c> | ||
//--- Dibujar en pantalla el array de celulas ------------- | //--- Dibujar en pantalla el array de celulas ------------- | ||
void DrawLife( void ) | void DrawLife( void ) | ||
Línea 432: | Línea 487: | ||
} | } | ||
} | } | ||
+ | </ | ||
Lo que hacemos en el ejemplo anterior es apuntar nuestra variable puntero memaddr a la posición de memoria donde comienzan los atributos (memaddr = posición de memoria del atributo del caracter 0,0 de pantalla), tras lo cual podemos escribir el atributo e incrementar el puntero para pasar al siguiente atributo que vamos a modificar. De este modo podemos redibujar nuestros 32x16 caracteres sin tener que recalcular memaddr para cada célula, como se hacía en el caso anterior. | Lo que hacemos en el ejemplo anterior es apuntar nuestra variable puntero memaddr a la posición de memoria donde comienzan los atributos (memaddr = posición de memoria del atributo del caracter 0,0 de pantalla), tras lo cual podemos escribir el atributo e incrementar el puntero para pasar al siguiente atributo que vamos a modificar. De este modo podemos redibujar nuestros 32x16 caracteres sin tener que recalcular memaddr para cada célula, como se hacía en el caso anterior. | ||
Línea 437: | Línea 493: | ||
En el fichero comprimido que acompaña a este artículo están almacenadas las 2 versiones de zxlife: la versión 1 (zxlife.c y zxlife.tap) que es la versión original del programa, y la versión 2 (zxlife2.c y zxlife2.tap) que es la versión optimizada con los cambios que hemos explicado en esta sección. | En el fichero comprimido que acompaña a este artículo están almacenadas las 2 versiones de zxlife: la versión 1 (zxlife.c y zxlife.tap) que es la versión original del programa, y la versión 2 (zxlife2.c y zxlife2.tap) que es la versión optimizada con los cambios que hemos explicado en esta sección. | ||
- | En conclusión | + | |
+ | ===== En conclusión | ||
Con el ejemplo de esta entrega hemos pretendido mostrar un ejemplo completo y práctico de cómo z88dk nos puede ayudar a implementar cualquier tipo de programa o algoritmo que deseemos fácilmente (zxlife.c tiene apenas 200 líneas de código contando comentarios y ha sido programado en apenas 30 minutos). Además, se ha podido ver cómo lo importante no es el lenguaje de programación utilizado, sino los algoritmos que se empleen. Por supuesto, zxlife puede optimizarse más aún: no se ha hecho porque el objetivo es que el programa fuera comprensible para los lectores, pero podemos combinar la potencia de C con funciones en ensamblador en aquellos puntos donde se considere oportuno, o utilizar una implementación diferente del algoritmo para calcular las generaciones de células, obteniendo mejores resultados. | Con el ejemplo de esta entrega hemos pretendido mostrar un ejemplo completo y práctico de cómo z88dk nos puede ayudar a implementar cualquier tipo de programa o algoritmo que deseemos fácilmente (zxlife.c tiene apenas 200 líneas de código contando comentarios y ha sido programado en apenas 30 minutos). Además, se ha podido ver cómo lo importante no es el lenguaje de programación utilizado, sino los algoritmos que se empleen. Por supuesto, zxlife puede optimizarse más aún: no se ha hecho porque el objetivo es que el programa fuera comprensible para los lectores, pero podemos combinar la potencia de C con funciones en ensamblador en aquellos puntos donde se considere oportuno, o utilizar una implementación diferente del algoritmo para calcular las generaciones de células, obteniendo mejores resultados. | ||
Línea 443: | Línea 501: | ||
En las próximas entregas comenzaremos a hablar de gráficos en el Spectrum, de forma que podamos comenzar a aplicar nuestros conocimientos de z88dk para hacer ya cosas visibles (gráficamente) en nuestro Spectrum. | En las próximas entregas comenzaremos a hablar de gráficos en el Spectrum, de forma que podamos comenzar a aplicar nuestros conocimientos de z88dk para hacer ya cosas visibles (gráficamente) en nuestro Spectrum. | ||
- | LINKS | ||
- | * Archivos fuente del ejemplo propuesto (zxlife.zip) | ||
- | * Wikipedia: http:// | ||
- | * Color Game Of Life Visual Exhibition: http:// | ||
- | * What is the Game of Life?: http:// | ||
- | Listado completo del programa | ||
+ | ===== Listado completo del programa ===== | ||
+ | |||
+ | |||
+ | <code c> | ||
/* | /* | ||
| | ||
Línea 634: | Línea 690: | ||
#endasm | #endasm | ||
} | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ===== Enlaces ===== | ||
- | SROMERO (NoP) | + | * [[http:// |
- | + | * [[http:// | |
- | Volver arriba | + | * [[http:// |
- | > ÍNDICE DE REVISTAS < | + | * [[http:// |
- | 2003-2007 Magazine ZX | + | |