cursos:basic:zxmines_ejemplo_comentado

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Próxima revisión
Revisión previa
cursos:basic:zxmines_ejemplo_comentado [20-03-2009 19:16] – creado sromerocursos:basic:zxmines_ejemplo_comentado [24-03-2009 09:29] (actual) sromero
Línea 1: Línea 1:
 ====== ZXMines: Juego Completo en BASIC comentado ====== ====== ZXMines: Juego Completo en BASIC comentado ======
  
-ZXMines es un juego en BASIC que se presentó al concurso de programación de Juegos en Basic 2003 de ByteManiacos. ZXMines implementa en BASIC un sencillo juego de Buscaminas en el cual debemos destapar todo el tablero sin levantar las casillas en que se alojan las minas (destapar una mina finaliza el juego). Para poder destapar totalmente el tablero sin levantar casillas con minas disponemos de una información providencial: cada casilla que no contiene una mina nos indica numéricamente cuántas minas hay en las 8 casillas de alrededor de la casilla actual. +//ZXMines es un juego en BASIC que se presentó al concurso de programación de Juegos en Basic 2003 de ByteManiacos. ZXMines implementa en BASIC un sencillo juego de Buscaminas en el cual debemos destapar todo el tablero sin levantar las casillas en que se alojan las minas (destapar una mina finaliza el juego). Para poder destapar totalmente el tablero sin levantar casillas con minas disponemos de una información providencial: cada casilla que no contiene una mina nos indica numéricamente cuántas minas hay en las 8 casillas de alrededor de la casilla actual.// 
-ZXMines: el clásico juego Buscaminas, en BASIC +\\  
-ZXMines: el clásico juego Buscaminas, en BASIC+\\ 
  
 Así, si destapamos una casilla y contiene el número 1, sabemos que alguna de las 8 casillas de alrededor de la misma contiene una mina. Utilizando la información numérica que nos proporcionan las diferentes casillas que vamos destapando podemos ser capaces de averiguar qué casillas contienen minas y evitarlas. El juego acaba cuando destapamos una mina (y perdemos) o bien cuando destapamos todas las casillas del tablero quedando sólo por destapar las casillas con minas (ganando el juego). Por último, una cosa a destacar es que si destapamos una casilla sin minas alrededor, se abre todo un área de juego a la vista, para acelerar el ritmo de juego. Así, si destapamos una casilla y contiene el número 1, sabemos que alguna de las 8 casillas de alrededor de la misma contiene una mina. Utilizando la información numérica que nos proporcionan las diferentes casillas que vamos destapando podemos ser capaces de averiguar qué casillas contienen minas y evitarlas. El juego acaba cuando destapamos una mina (y perdemos) o bien cuando destapamos todas las casillas del tablero quedando sólo por destapar las casillas con minas (ganando el juego). Por último, una cosa a destacar es que si destapamos una casilla sin minas alrededor, se abre todo un área de juego a la vista, para acelerar el ritmo de juego.
 +
 +\\ 
 +{{ :cursos:basic:zxmines_1.gif }}
 +;#;
 +//ZXMines: el clásico juego Buscaminas, en BASIC//
 +;#;
 +\\ 
  
 El objetivo del presente artículo es mostrar y explicar el código BASIC utilizado para programar ZXMINES, mostrando así algunos trucos que en BASIC proporcionan una mayor velocidad de ejecución. El objetivo del presente artículo es mostrar y explicar el código BASIC utilizado para programar ZXMINES, mostrando así algunos trucos que en BASIC proporcionan una mayor velocidad de ejecución.
  
-PSEUDOCÓDIGO DEL JUEGO+\\  
 + 
 +===== Pseudocódigo del juego =====
  
 Lo primero que hacemos es definir el juego mediante lenguaje humano, para posteriormente adaptar ese lenguaje humano a código en BASIC. Es muy importante hacer esto antes de escribir una sóla línea en BASIC. El programa se adapta al diseño y al pseudocódigo, y no al revés. Lo primero que hacemos es definir el juego mediante lenguaje humano, para posteriormente adaptar ese lenguaje humano a código en BASIC. Es muy importante hacer esto antes de escribir una sóla línea en BASIC. El programa se adapta al diseño y al pseudocódigo, y no al revés.
Línea 15: Línea 24:
 Veamos el pseudocódigo para nuestro juego buscaminas: Veamos el pseudocódigo para nuestro juego buscaminas:
  
 +<code>
 Inicio ZXMINES Inicio ZXMINES
  - Inicio del programa (declaracion de variables, etc.).  - Inicio del programa (declaracion de variables, etc.).
Línea 85: Línea 95:
  
 Fin ZXMINES Fin ZXMINES
 +</code>
  
 El pseudocódigo permite implementar el juego en cualquier lenguaje de una manera rápida: por ejemplo, el mismo pseudocódigo/diseño que se usó para ZXMINES 1 en BASIC se utilizó para programar ZXMINES 2 en C (mucho más rápido) en apenas un par de horas de codificación. Para que el pseudocódigo sea legible es recomendable mantenerlo conciso y no extenderse demasiado en detalles, ya que debe ser una visión general del programa. El pseudocódigo permite implementar el juego en cualquier lenguaje de una manera rápida: por ejemplo, el mismo pseudocódigo/diseño que se usó para ZXMINES 1 en BASIC se utilizó para programar ZXMINES 2 en C (mucho más rápido) en apenas un par de horas de codificación. Para que el pseudocódigo sea legible es recomendable mantenerlo conciso y no extenderse demasiado en detalles, ya que debe ser una visión general del programa.
  
-EL LISTADO DEL JUEGO+\\  
 +===== El listado del juego ===== 
  
 En los siguientes apartados veremos los diferentes bloques funcionales del juego comentados. El lector podrá identificar fácilmente a qué función del pseudocódigo se corresponden. Como puede verse, la utilización de pseudocódigo permite una programación más limpia, ya que la implementación sólo tiene que ceñirse a lo que hemos diseñado, reduciendo los errores posteriores (se trata tan sólo de implementar la visión general que nos da el pseudocódigo, y esto se puede hacer de forma modular). En los siguientes apartados veremos los diferentes bloques funcionales del juego comentados. El lector podrá identificar fácilmente a qué función del pseudocódigo se corresponden. Como puede verse, la utilización de pseudocódigo permite una programación más limpia, ya que la implementación sólo tiene que ceñirse a lo que hemos diseñado, reduciendo los errores posteriores (se trata tan sólo de implementar la visión general que nos da el pseudocódigo, y esto se puede hacer de forma modular).
Línea 96: Línea 109:
 Para empezar, veamos la relación entre el pseudocódigo y las diferentes líneas BASIC del programa (podéis utilizar el Listado 1 para identificar los diferentes bloques funcionales): Para empezar, veamos la relación entre el pseudocódigo y las diferentes líneas BASIC del programa (podéis utilizar el Listado 1 para identificar los diferentes bloques funcionales):
  
 +<code>
 Inicio ZXMINES Inicio ZXMINES
  - Inicio del programa (líneas 48-90)  - Inicio del programa (líneas 48-90)
Línea 108: Línea 122:
 FUNCIÓN Preparar_Pantalla: (líneas 105-140 y 2300-9022) FUNCIÓN Preparar_Pantalla: (líneas 105-140 y 2300-9022)
 Fin ZXMINES Fin ZXMINES
 +</code>
  
 Como puede verse, cada función o bloque lógico del pseudocódigo se corresponde con un bloque de líneas BASIC, que será que lo veremos más detallado a continuación. Como puede verse, cada función o bloque lógico del pseudocódigo se corresponde con un bloque de líneas BASIC, que será que lo veremos más detallado a continuación.
  
-INICIO DEL PROGRAMA Y PREPARACIÓN DE PANTALLA+\\  
 +===== Inicio del programa y preparación de pantalla =====
  
 Aparte de los comentarios del programa (líneas 9100 a 9147), el programa (lógicamente hablando) empieza con el siguiente código: Aparte de los comentarios del programa (líneas 9100 a 9147), el programa (lógicamente hablando) empieza con el siguiente código:
  
 +<code basic>
 48 INPUT "Spanish/English? (s/e)", L$ ; 48 INPUT "Spanish/English? (s/e)", L$ ;
 49 IF L$<>"s" AND L$<>"S" AND L$<>"e" AND L$<>"E" THEN GO TO 48 49 IF L$<>"s" AND L$<>"S" AND L$<>"e" AND L$<>"E" THEN GO TO 48
Línea 129: Línea 146:
 85 LET DIFICULTAD=0 85 LET DIFICULTAD=0
 90 GO SUB 8000            : REM DEFINIMOS LOS GRAFICOS 90 GO SUB 8000            : REM DEFINIMOS LOS GRAFICOS
 +</code>
  
 Lo primero que hacemos es pues preguntar al usuario el idioma para el juego, y después declarar las variables que utilizaremos en el programa. La variable L$ almacenará el idioma del juego, pudiendo contener la cadena "e" (english) o la cadena "s" (spanish). La usaremos posteriormente a la hora de hacer PRINTs para imprimir mensajes en un idioma u otro. Lo primero que hacemos es pues preguntar al usuario el idioma para el juego, y después declarar las variables que utilizaremos en el programa. La variable L$ almacenará el idioma del juego, pudiendo contener la cadena "e" (english) o la cadena "s" (spanish). La usaremos posteriormente a la hora de hacer PRINTs para imprimir mensajes en un idioma u otro.
Línea 138: Línea 156:
 Así pues, un tablero tapado tendría la siguiente forma: Así pues, un tablero tapado tendría la siguiente forma:
  
 +<code>
 V = V =
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
Línea 151: Línea 170:
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 +</code>
  
 Un tablero con la casilla central destapada tendría el siguiente aspecto: Un tablero con la casilla central destapada tendría el siguiente aspecto:
  
 +<code>
 V = V =
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
Línea 167: Línea 188:
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 +</code>
  
 El array T(12,12) indica lo que contiene cada casilla propiamente dicha. Si contiene un cero, la casilla está vacía. Si contiene un nueve, la casilla tiene una mina. Finalmente, cualquier valor entre 1 y 8 significa el número de minas que hay alrededor de la casilla actual. El array T(12,12) indica lo que contiene cada casilla propiamente dicha. Si contiene un cero, la casilla está vacía. Si contiene un nueve, la casilla tiene una mina. Finalmente, cualquier valor entre 1 y 8 significa el número de minas que hay alrededor de la casilla actual.
Línea 172: Línea 194:
 Así, inicialmente tendremos ambos arrays (V y T) llenos de ceros porque no hay minas dentro, y porque todas las casillas están tapadas. Si queremos introducir una mina en el tablero en la posición (3,5), podemos hacerlo con: Así, inicialmente tendremos ambos arrays (V y T) llenos de ceros porque no hay minas dentro, y porque todas las casillas están tapadas. Si queremos introducir una mina en el tablero en la posición (3,5), podemos hacerlo con:
  
 +<code basic>
 T(3,5) = 9; T(3,5) = 9;
 +</code>
  
 Al igual que en el caso anterior, un array sin minas estará lleno de ceros, pero un array con 4 minas y ya correctamente "rellenado" (como veremos posteriormente), podría tener un aspecto similar al siguiente: Al igual que en el caso anterior, un array sin minas estará lleno de ceros, pero un array con 4 minas y ya correctamente "rellenado" (como veremos posteriormente), podría tener un aspecto similar al siguiente:
  
 +<code>
 T = T =
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
Línea 189: Línea 214:
 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 +</code>
  
 Como puede verse, las minas se sitúan con números 9, y las casillas de alrededor de las minas contienen valores numéricos que indican la cantidad de minas circundantes. Como puede verse, las minas se sitúan con números 9, y las casillas de alrededor de las minas contienen valores numéricos que indican la cantidad de minas circundantes.
Línea 194: Línea 220:
 Finalmente se produce un salto a la rutina de la dirección 8000, que prepara los UDG o gráficos definidos por el usuario: Finalmente se produce un salto a la rutina de la dirección 8000, que prepara los UDG o gráficos definidos por el usuario:
  
 +<code basic>
 8000 REM *** Preparacion de GDUs *** 8000 REM *** Preparacion de GDUs ***
 8005 RESTORE 9012 8005 RESTORE 9012
Línea 199: Línea 226:
 8015 FOR I=0 TO 7: READ FILA : POKE USR "M"+I, FILA : NEXT I 8015 FOR I=0 TO 7: READ FILA : POKE USR "M"+I, FILA : NEXT I
 8020 FOR I=0 TO 7: READ FILA : POKE USR "E"+I, FILA : NEXT I 8020 FOR I=0 TO 7: READ FILA : POKE USR "E"+I, FILA : NEXT I
 +</code>
  
 Como puede verse, se define "M" como el UDG que contiene el dibujo de las minas, "E" como un UDG que contiene el dibujo de una casilla sin destapar o esquina, e "I" como el UDG que contiene el dibujo de una casilla destapada vacía (un pequeño puntito central). Como puede verse, se define "M" como el UDG que contiene el dibujo de las minas, "E" como un UDG que contiene el dibujo de una casilla sin destapar o esquina, e "I" como el UDG que contiene el dibujo de una casilla destapada vacía (un pequeño puntito central).
  
 Los 3 dibujos han sido creados con SevenuP de la misma forma. Por ejemplo, para crear la mina, abrimos SevenuP y creamos un nuevo dibujo de 8x8. Dibujamos la mina (en la siguiente figura la veremos a medio dibujar) y vamos a File , y luego a Output Options y seleccionamos "No attributes" y como Byte Sort Priority dejamos "X char, Char line, Y char, Mask". Los 3 dibujos han sido creados con SevenuP de la misma forma. Por ejemplo, para crear la mina, abrimos SevenuP y creamos un nuevo dibujo de 8x8. Dibujamos la mina (en la siguiente figura la veremos a medio dibujar) y vamos a File , y luego a Output Options y seleccionamos "No attributes" y como Byte Sort Priority dejamos "X char, Char line, Y char, Mask".
-Creando nuestros gráficos en SevenuP + 
-Creando nuestros gráficos en SevenuP+\\  
 +{{ :cursos:basic:zxmines_2.gif }} 
 +;#; 
 +//Creando nuestros gráficos en SevenuP// 
 +;#; 
 +\\  
  
 A continuación exportamos los datos, desde el menu File, opción Export Data. Escribimos un nombre de fichero (por ejemplo, mina.c), y en el desplegable seleccionamos "C source" en lugar de "raw Binary". Al darle a OK habremos creado un fichero mina.c con el gráfico exportado a formato C. El aspecto del fichero será similar (comentarios aparte) al siguiente: A continuación exportamos los datos, desde el menu File, opción Export Data. Escribimos un nombre de fichero (por ejemplo, mina.c), y en el desplegable seleccionamos "C source" en lugar de "raw Binary". Al darle a OK habremos creado un fichero mina.c con el gráfico exportado a formato C. El aspecto del fichero será similar (comentarios aparte) al siguiente:
  
 +<code c>
 unsigned char Sprite[8] = { unsigned char Sprite[8] = {
  16, 186, 116, 254, 124, 186, 16, 0 };  16, 186, 116, 254, 124, 186, 16, 0 };
 +</code>
  
 Estos son los valores que podremos en nuestros DATA en BASIC: Estos son los valores que podremos en nuestros DATA en BASIC:
  
 +<code basic>
 9017 DATA 16, 186, 116, 254, 124, 186, 16, 0 9017 DATA 16, 186, 116, 254, 124, 186, 16, 0
 +</code>
  
 Tras el inicio del programa (líneas 48 a 90) viene la preparación de la pantalla (líneas 2300 a 2450) donde simplemente se imprimen en pantalla los mensajes que veremos en el menú principal. Tras el inicio del programa (líneas 48 a 90) viene la preparación de la pantalla (líneas 2300 a 2450) donde simplemente se imprimen en pantalla los mensajes que veremos en el menú principal.
-Aspecto de la pantalla de presentación 
-Aspecto de la pantalla de presentación 
  
-GENERACIÓN DEL TABLERO+ 
 +\\  
 +{{ :cursos:basic:zxmines_3.gif }} 
 +;#; 
 +//Aspecto de la pantalla de presentación// 
 +;#; 
 +\\  
 + 
 + 
 +\\  
 +===== Generación del tablero =====
  
 La rutina de las líneas 2000 a 2200 es llamada desde la rutina de preparación de la pantalla para rellenar el tablero T con minas aleatorias (números 9), y al mismo tiempo calcular los valores numéricos de cada casilla indicando el número de minas alrededor. La rutina de las líneas 2000 a 2200 es llamada desde la rutina de preparación de la pantalla para rellenar el tablero T con minas aleatorias (números 9), y al mismo tiempo calcular los valores numéricos de cada casilla indicando el número de minas alrededor.
Línea 230: Línea 276:
 En pseudocódigo: En pseudocódigo:
  
 +<code>
  Desde I = 0 a NUMERO_DE_MINAS  Desde I = 0 a NUMERO_DE_MINAS
    Poner minas aleatoriamente con T(RANDOMX,RANDOMY) = 9;    Poner minas aleatoriamente con T(RANDOMX,RANDOMY) = 9;
Línea 240: Línea 287:
    Fin Desde    Fin Desde
  Fin Desde  Fin Desde
 +</code>
  
 El código resultante es: El código resultante es:
  
 +<code basic>
 2000 REM *** GENERACION DE TABLERO *** 2000 REM *** GENERACION DE TABLERO ***
 2001 IF DIFICULTAD=0 THEN RETURN 2001 IF DIFICULTAD=0 THEN RETURN
Línea 269: Línea 318:
 2101 IF L$="e" THEN PRINT AT 20,1 ; "> Preparing board: 0%"; 2101 IF L$="e" THEN PRINT AT 20,1 ; "> Preparing board: 0%";
 2102 IF L$="s" THEN PRINT AT 20,1 ; "> Gener.  tablero: 0%"; 2102 IF L$="s" THEN PRINT AT 20,1 ; "> Gener.  tablero: 0%";
 +</code>
  
 En esa primera parte colocamos las minas dentro del tablero, y a continuación calculamos los valores numéricos de cada celdilla del tablero: En esa primera parte colocamos las minas dentro del tablero, y a continuación calculamos los valores numéricos de cada celdilla del tablero:
  
 +<code basic>
 2109 LET PORCEN=0 : LET CON=0 2109 LET PORCEN=0 : LET CON=0
 2110 FOR N=1 TO MINAS 2110 FOR N=1 TO MINAS
Línea 291: Línea 342:
 2181 PRINT AT 20,1 ; INK COL; PAPER PAP; "                        "; 2181 PRINT AT 20,1 ; INK COL; PAPER PAP; "                        ";
 2200 RETURN 2200 RETURN
 +</code>
  
 Nótese un pequeño truco que se ha utilizado para acelerar la generación y cálculos: en lugar de trabajar sobre arrays temporales, trabajamos sobre zonas de memoria (59000 y 59300) tratándolas como vectores de trabajo temporales. Escribimos en nuestros "arrays" en memoria con POKE, y leemos con PEEK. Posteriormente en la rutina de "Blanqueado de casillas" veremos su utilidad y porqué se ha realizado esto en lugar de arrays temporales. En las posiciones de memoria desde 59000 a 59000+numero_de_minas (ese numero_de_minas varía con la dificultad de juego elegida) se almacenan las posiciones X de las minas aleatorias generadas. Lo mismo ocurre en 59300, donde se almacenan las posiciones Y de las minas aleatorias generadas. Nótese un pequeño truco que se ha utilizado para acelerar la generación y cálculos: en lugar de trabajar sobre arrays temporales, trabajamos sobre zonas de memoria (59000 y 59300) tratándolas como vectores de trabajo temporales. Escribimos en nuestros "arrays" en memoria con POKE, y leemos con PEEK. Posteriormente en la rutina de "Blanqueado de casillas" veremos su utilidad y porqué se ha realizado esto en lugar de arrays temporales. En las posiciones de memoria desde 59000 a 59000+numero_de_minas (ese numero_de_minas varía con la dificultad de juego elegida) se almacenan las posiciones X de las minas aleatorias generadas. Lo mismo ocurre en 59300, donde se almacenan las posiciones Y de las minas aleatorias generadas.
Línea 296: Línea 348:
 Así, la primera parte de la función coloca en 59000 y 59300 valores X e Y para las minas, y finalmente incorporamos las minas al tablero mediante: Así, la primera parte de la función coloca en 59000 y 59300 valores X e Y para las minas, y finalmente incorporamos las minas al tablero mediante:
  
 +<code basic>
 2170 FOR N=1 TO MINAS : LET T( PEEK (59000+N), PEEK (59300+N)) = 9 : 2170 FOR N=1 TO MINAS : LET T( PEEK (59000+N), PEEK (59300+N)) = 9 :
      NEXT N      NEXT N
 +</code>
  
 El resultado de la ejecución de la rutina de arriba es un tablero tapado (V(x,y) = 0 para todo x e y), y que tiene una serie de minas (valores numéricos 9) en T(xminas,yminas), estando el resto de casillas de T(x,y) calculadas con valores numéricos que representan las minas alrededor de la casilla en cuestión, como en el ejemplo que hemos visto antes: El resultado de la ejecución de la rutina de arriba es un tablero tapado (V(x,y) = 0 para todo x e y), y que tiene una serie de minas (valores numéricos 9) en T(xminas,yminas), estando el resto de casillas de T(x,y) calculadas con valores numéricos que representan las minas alrededor de la casilla en cuestión, como en el ejemplo que hemos visto antes:
  
 +<code>
 T = T =
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
Línea 314: Línea 369:
 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0
 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
 +</code>
  
 Una vez tenemos definido el tablero de juego podemos continuar con el bucle principal del programa. Una vez tenemos definido el tablero de juego podemos continuar con el bucle principal del programa.
  
-BUCLE PRINCIPAL DEL JUEGO+\\  
 + 
 +===== Bucle principal del juego ===== 
  
 El bucle principal del juego se desarrolla entre las líneas 200 y 990). El bucle principal del juego se desarrolla entre las líneas 200 y 990).
Línea 323: Línea 382:
 Lo primero que hacemos es dibujar el tablero de juego con 2 bucles FOR para el ancho y para el alto: Lo primero que hacemos es dibujar el tablero de juego con 2 bucles FOR para el ancho y para el alto:
  
-00 REM *** BUCLE PRINCIPAL ***+<code basic> 
 +100 REM *** BUCLE PRINCIPAL ***
 300 LET P$= CHR$(16) + CHR$(0) + CHR$(17) + CHR$(7) : 300 LET P$= CHR$(16) + CHR$(0) + CHR$(17) + CHR$(7) :
     FOR X=1 TO ANCHO : LET P$=P$+"{E}" : NEXT X     FOR X=1 TO ANCHO : LET P$=P$+"{E}" : NEXT X
Línea 329: Línea 389:
 315   PRINT AT IY+Y,IX+1 ; P$; 315   PRINT AT IY+Y,IX+1 ; P$;
 370 NEXT Y 370 NEXT Y
 +</code>
  
 Se ha utilizado una cadena "P$" en la que introducimos toda una fila horizontal de casillas sin destapar (E). Después con un bucle pintamos tantas líneas horizontales de casillas como altura tiene nuestro tablero. Esto es más rápido que un doble bucle, donde tendríamos que recalcular las casillas horizontales cada vez. El símbolo "{E}" representa al UDG "E" (para bas2tap). Se ha utilizado una cadena "P$" en la que introducimos toda una fila horizontal de casillas sin destapar (E). Después con un bucle pintamos tantas líneas horizontales de casillas como altura tiene nuestro tablero. Esto es más rápido que un doble bucle, donde tendríamos que recalcular las casillas horizontales cada vez. El símbolo "{E}" representa al UDG "E" (para bas2tap).
Línea 334: Línea 395:
 A continuación viene el bucle principal en sí mismo. Lo que se hace en este bucle principal es utilizar unas variables CX, CY que indican la posición dentro del array de la casilla "actual" o cursor que estamos manejando (de forma que cuando pulsamos espacio destapamos la casilla actual). Las variables CX y CY (posición de la casilla/cursor) se actualizan cuando pulsamos izquierda, derecha, arriba o abajo. En cada paso del bucle representamos la casilla "actual" o cursor mediante un FLASH de la posición en pantalla de la casilla (IX+CX,IY+CY). Recordad: la posición (CX, CY) es un valor entre 1 y 12 dentro de los arrays V() y T(), mientras que (CX+IX, CY+IY) representa un valor entre 1-32 y 1-24 para dibujar en pantalla el cuadro parpadeante (IX e IY son el inicio en pantalla del tablero). A continuación viene el bucle principal en sí mismo. Lo que se hace en este bucle principal es utilizar unas variables CX, CY que indican la posición dentro del array de la casilla "actual" o cursor que estamos manejando (de forma que cuando pulsamos espacio destapamos la casilla actual). Las variables CX y CY (posición de la casilla/cursor) se actualizan cuando pulsamos izquierda, derecha, arriba o abajo. En cada paso del bucle representamos la casilla "actual" o cursor mediante un FLASH de la posición en pantalla de la casilla (IX+CX,IY+CY). Recordad: la posición (CX, CY) es un valor entre 1 y 12 dentro de los arrays V() y T(), mientras que (CX+IX, CY+IY) representa un valor entre 1-32 y 1-24 para dibujar en pantalla el cuadro parpadeante (IX e IY son el inicio en pantalla del tablero).
  
 +<code basic>
 400 REM Bucle principal de partida 400 REM Bucle principal de partida
 405 IF DIFICULTAD=0 THEN GO TO 415 405 IF DIFICULTAD=0 THEN GO TO 415
Línea 360: Línea 422:
     IF CY<>0 THEN GO SUB 1000     IF CY<>0 THEN GO SUB 1000
 990 GO TO 400 990 GO TO 400
 +</code>
  
 La actualización de la posición actual del cursor se realiza en la línea 410: La actualización de la posición actual del cursor se realiza en la línea 410:
  
 +<code basic>
 410 OVER 1: PRINT AT IY+CY, IX+CX ; FLASH 1; INK 0 ; PAPER 7;" "; : 410 OVER 1: PRINT AT IY+CY, IX+CX ; FLASH 1; INK 0 ; PAPER 7;" "; :
     OVER 0 : GO SUB 991     OVER 0 : GO SUB 991
 +</code>
  
 Al final de esa línea se salta a la rutina 991, que simplemente se encarga con varios PRINT de actualizar los valores numéricos de números de minas y casillas restantes en el juego: Al final de esa línea se salta a la rutina 991, que simplemente se encarga con varios PRINT de actualizar los valores numéricos de números de minas y casillas restantes en el juego:
  
 +<code basic>
 991 REM *** Actualizar contadores minas *** 991 REM *** Actualizar contadores minas ***
 992 IF L$="e" THEN PRINT AT 14, 18; "Mines: "; INK 7 ; MINAS ;  " " 992 IF L$="e" THEN PRINT AT 14, 18; "Mines: "; INK 7 ; MINAS ;  " "
Línea 377: Línea 443:
 997 PRINT AT 15, 18; "            " 997 PRINT AT 15, 18; "            "
 998 RETURN 998 RETURN
 +</code>
  
 Como puede verse, en el bucle principal también se controla la pulsación de las teclas 1 a 4 para cambiar la dificultad y acabar el juego. Nótese también la siguiente línea: Como puede verse, en el bucle principal también se controla la pulsación de las teclas 1 a 4 para cambiar la dificultad y acabar el juego. Nótese también la siguiente línea:
  
 +<code basic>
 407 IF MINAS=QUEDAN THEN GO TO 1600 407 IF MINAS=QUEDAN THEN GO TO 1600
 +</code>
  
 La línea 407 es el control de fin de juego para saber si el jugador ha ganado: si quedan tantas casillas por destapar como minas hemos puesto en el tablero, quiere decir que hemos destapado todas las casillas del tablero excepto las minas, con lo cual el juego se ha acabado y hemos ganado. En ese caso se salta a la línea 1600 que es la rutina de Victoria_Fin_Juego. Las rutinas de Victoria_Fin_Juego y Muerte_Fin_Juego son bastante sencillas (líneas 1300-1330 y 1600-1630): simplemente muestran un mensaje en pantalla, muestran el tablero desplegado y completo (si pulsas una mina y pierdes, tienes derecho a ver el contenido del tablero y la posición de las minas), esperan la pulsación de una tecla, reinicializan las variables del juego y saltan a la línea 400 (Inicio del bucle principal) para comenzar una nueva partida. La muestra y borrado del tablero se realiza mediante las siguientes rutinas: La línea 407 es el control de fin de juego para saber si el jugador ha ganado: si quedan tantas casillas por destapar como minas hemos puesto en el tablero, quiere decir que hemos destapado todas las casillas del tablero excepto las minas, con lo cual el juego se ha acabado y hemos ganado. En ese caso se salta a la línea 1600 que es la rutina de Victoria_Fin_Juego. Las rutinas de Victoria_Fin_Juego y Muerte_Fin_Juego son bastante sencillas (líneas 1300-1330 y 1600-1630): simplemente muestran un mensaje en pantalla, muestran el tablero desplegado y completo (si pulsas una mina y pierdes, tienes derecho a ver el contenido del tablero y la posición de las minas), esperan la pulsación de una tecla, reinicializan las variables del juego y saltan a la línea 400 (Inicio del bucle principal) para comenzar una nueva partida. La muestra y borrado del tablero se realiza mediante las siguientes rutinas:
  
 +<code basic>
 1400 REM *** Mostrar tablero *** 1400 REM *** Mostrar tablero ***
 1420 FOR Y=1 TO ALTO 1420 FOR Y=1 TO ALTO
Línea 397: Línea 467:
 1463 NEXT N 1463 NEXT N
 1470 RETURN 1470 RETURN
 +</code>
  
 El borrado se basa simplemente en pintar espacios sobre el tablero, mientras que la visualización del tablero muestra todos los valores de T(X,Y) (independientemente del valor de V(X,Y), ya que pretendemos mostrar el tablero destapado completo). Puede verse el uso de C$(), que es un array donde hemos almacenado los caracteres para los 10 valores posibles del tablero (0 a 9, desde una casilla sin mina hasta una mina), de tal forma que 0 se corresponde con una casilla vacía, los números del 1 al 8 con los caracteres del 1 al 9, y el 9 con una mina. El borrado se basa simplemente en pintar espacios sobre el tablero, mientras que la visualización del tablero muestra todos los valores de T(X,Y) (independientemente del valor de V(X,Y), ya que pretendemos mostrar el tablero destapado completo). Puede verse el uso de C$(), que es un array donde hemos almacenado los caracteres para los 10 valores posibles del tablero (0 a 9, desde una casilla sin mina hasta una mina), de tal forma que 0 se corresponde con una casilla vacía, los números del 1 al 8 con los caracteres del 1 al 9, y el 9 con una mina.
  
 +<code basic>
 101 DIM C$(10) : FOR N=1 TO 10: READ C$(N) : NEXT N 101 DIM C$(10) : FOR N=1 TO 10: READ C$(N) : NEXT N
 102 DIM H(10) : FOR N=1 TO 10: READ H(N) : NEXT N 102 DIM H(10) : FOR N=1 TO 10: READ H(N) : NEXT N
 103 DATA "{I}","1","2","3","4","5","6","7","8","{M}" 103 DATA "{I}","1","2","3","4","5","6","7","8","{M}"
 104 DATA 0, 1, 3, 2, 2, 2, 2, 2, 2, 0 104 DATA 0, 1, 3, 2, 2, 2, 2, 2, 2, 0
 +</code>
  
 La notación {M} significa "el UDG de M mayúscula", y se utiliza en BAS2TAP para especificar los UDGs (ya que los teclados de los PCs no tienen las teclas de nuestro Spectrum para dibujar esos símbolos). La notación {M} significa "el UDG de M mayúscula", y se utiliza en BAS2TAP para especificar los UDGs (ya que los teclados de los PCs no tienen las teclas de nuestro Spectrum para dibujar esos símbolos).
Línea 409: Línea 482:
 Por último, cuando movemos el "cursor" de una posición a otra tenemos que recuperar en pantalla lo que había bajo la posición que abandonamos. Esta tarea la realiza la siguiente función: Por último, cuando movemos el "cursor" de una posición a otra tenemos que recuperar en pantalla lo que había bajo la posición que abandonamos. Esta tarea la realiza la siguiente función:
  
 +<code basic>
 1500 REM *** Restaurar grafico bajo el cursor *** 1500 REM *** Restaurar grafico bajo el cursor ***
 1510 IF V(CX,CY)=1 THEN PRINT AT CY+IY,CX+IX ; FLASH 0; PAPER 7; 1510 IF V(CX,CY)=1 THEN PRINT AT CY+IY,CX+IX ; FLASH 0; PAPER 7;
Línea 414: Línea 488:
 1520 PRINT AT CY+IY,CX+IX; FLASH 0; INK 0 ; PAPER 7; "{E}"; 1520 PRINT AT CY+IY,CX+IX; FLASH 0; INK 0 ; PAPER 7; "{E}";
 1530 RETURN 1530 RETURN
 +</code>
  
 De nuevo podemos ver cómo se hace uso de CX+IX y CY+IY para saber dónde "escribir" la casilla que hay que recuperar, y de C$() para saber qué carácter imprimir, y H() para saber con qué atributo (tinta) imprimirlo. De nuevo podemos ver cómo se hace uso de CX+IX y CY+IY para saber dónde "escribir" la casilla que hay que recuperar, y de C$() para saber qué carácter imprimir, y H() para saber con qué atributo (tinta) imprimirlo.
Línea 419: Línea 494:
 Gracias a H(), por ejemplo, podemos hacer: Gracias a H(), por ejemplo, podemos hacer:
  
 +<code basic>
 102 DIM H(10) : FOR N=1 TO 10: READ H(N) : NEXT N 102 DIM H(10) : FOR N=1 TO 10: READ H(N) : NEXT N
 104 DATA 0, 1, 3, 2, 2, 2, 2, 2, 2, 0 104 DATA 0, 1, 3, 2, 2, 2, 2, 2, 2, 0
 (...) (...)
 PRINT INK H( valor ) ; "X" ; PRINT INK H( valor ) ; "X" ;
 +</code>
  
 en lugar de: en lugar de:
  
 +<code basic>
 IF valor="0" : PRINT INK 0; "X" IF valor="0" : PRINT INK 0; "X"
 IF valor="1" : PRINT INK 1; "X" IF valor="1" : PRINT INK 1; "X"
Línea 431: Línea 509:
 (...) (...)
 IF valor="9" : PRINT INK 0; "X" IF valor="9" : PRINT INK 0; "X"
 +</code>
  
 Lo cual es mucho más rápido y más óptimo y legible. Lo cual es mucho más rápido y más óptimo y legible.
  
-DESTAPADO DE MINAS+\\  
 +===== Destapado de minas =====
  
 Cuando en el bucle principal pulsamos espacio, se salta a una rutina que destapa la casilla actual: Cuando en el bucle principal pulsamos espacio, se salta a una rutina que destapa la casilla actual:
  
 +<code basic>
 470 IF K$=" " OR K$="0" THEN IF DIFICULTAD<>0 THEN GO SUB 1000 470 IF K$=" " OR K$="0" THEN IF DIFICULTAD<>0 THEN GO SUB 1000
 +</code>
  
 Dicha rutina DestaparMina se desarrolla desde las líneas 1000 a 1099 del programa: Si la casilla ya está destapada, no hay nada que destapar. En caso contrario, la marcamos como ya abierta, y reducimos el número de casillas, y en una tercera línea imprimimos en pantalla el carácter al que corresponde la casilla que acabamos destapar: Dicha rutina DestaparMina se desarrolla desde las líneas 1000 a 1099 del programa: Si la casilla ya está destapada, no hay nada que destapar. En caso contrario, la marcamos como ya abierta, y reducimos el número de casillas, y en una tercera línea imprimimos en pantalla el carácter al que corresponde la casilla que acabamos destapar:
  
 +<code basic>
 1000 REM *** Destapar mina *** 1000 REM *** Destapar mina ***
 1010 IF V(CX,CY)=1 THEN RETURN 1010 IF V(CX,CY)=1 THEN RETURN
Línea 447: Línea 530:
 1030 PRINT AT CY+IY,CX+IX ; PAPER 7; INK H(T(CX,CY)+1) ; 1030 PRINT AT CY+IY,CX+IX ; PAPER 7; INK H(T(CX,CY)+1) ;
      C$(T(CX,CY)+1) ;      C$(T(CX,CY)+1) ;
 +</code>
  
 Si es una mina lo que hemos destapado, hemos perdido. Si, por contra, es una casilla en blanco, tenemos que destapar todas las casillas en blanco alrededor de la casilla actual, algo que se hace en la porción de código a partir de la línea 1060. Para el resto de los casos (números 1 al 8) basta con haber destapado y mostrado la casilla, y podemos volver (saltando a 1098): Si es una mina lo que hemos destapado, hemos perdido. Si, por contra, es una casilla en blanco, tenemos que destapar todas las casillas en blanco alrededor de la casilla actual, algo que se hace en la porción de código a partir de la línea 1060. Para el resto de los casos (números 1 al 8) basta con haber destapado y mostrado la casilla, y podemos volver (saltando a 1098):
  
 +<code basic>
 1040 IF T(CX,CY)=9 THEN GO TO 1300 1040 IF T(CX,CY)=9 THEN GO TO 1300
 1050 IF T(CX,CY)=0 THEN GO TO 1060 1050 IF T(CX,CY)=0 THEN GO TO 1060
 1055 GO TO 1098 1055 GO TO 1098
 +</code>
  
 A partir de aquí empieza el destapado de recuadros en blanco: lo primero es visualizar un mensaje de información de que se va a saltar a un cálculo que en BASIC es algo lento y costoso, para después llamar a la rutina Blanquear_Cuadros_Alrededor (llamada en línea 1075). Tras volver de la rutina se actualizan los contadores de minas, casillas y puntos y se vuelve al bucle principal. A partir de aquí empieza el destapado de recuadros en blanco: lo primero es visualizar un mensaje de información de que se va a saltar a un cálculo que en BASIC es algo lento y costoso, para después llamar a la rutina Blanquear_Cuadros_Alrededor (llamada en línea 1075). Tras volver de la rutina se actualizan los contadores de minas, casillas y puntos y se vuelve al bucle principal.
  
 +<code basic>
 1060 REM Destapar todos los cuadros de alrededor que sean blancos 1060 REM Destapar todos los cuadros de alrededor que sean blancos
 1066 LET PP=1                    : REM Puntero a la pila 1066 LET PP=1                    : REM Puntero a la pila
Línea 470: Línea 557:
 1098 LET PUNTOS=PUNTOS+1 1098 LET PUNTOS=PUNTOS+1
 1099 RETURN 1099 RETURN
 +</code>
  
 La rutina Blanquear_Cuadros_Alrededor se encarga de destapar todos los recuadros en blanco si el recuadro que acabamos de destapar está en blanco. Se usa para evitar que en zonas grandes de recuadros sin minas ni números tengamos que destapar uno a uno todos los recuadros que no contienen nada. Si no se llamara a esta función, al pulsar en un recuadro de un área vacía, simplemente se destaparía ese recuadro y ningún otro más. Lo que hace esta función es destapar todo el área vacía, para agilizar el juego. La rutina Blanquear_Cuadros_Alrededor se encarga de destapar todos los recuadros en blanco si el recuadro que acabamos de destapar está en blanco. Se usa para evitar que en zonas grandes de recuadros sin minas ni números tengamos que destapar uno a uno todos los recuadros que no contienen nada. Si no se llamara a esta función, al pulsar en un recuadro de un área vacía, simplemente se destaparía ese recuadro y ningún otro más. Lo que hace esta función es destapar todo el área vacía, para agilizar el juego.
Línea 481: Línea 569:
 El algoritmo de la rutina de las líneas 3 a 14 lo que hace es lo siguiente: El algoritmo de la rutina de las líneas 3 a 14 lo que hace es lo siguiente:
  
-    * Si la casilla actual es cero, destaparla (V(X,Y) = 1) y pintarla en pantalla. +   * Si la casilla actual es cero, destaparla (V(X,Y) = 1) y pintarla en pantalla. 
-    * Comprobar si las 8 casillas de alrededor son cero. +   * Comprobar si las 8 casillas de alrededor son cero.\\ Si lo son, ejecutar este mismo algoritmo sobre cada una de las 8 casillas.
-      Si lo son, ejecutar este mismo algoritmo sobre cada una de las 8 casillas.+
  
 La implementación es bastante ilegible (al estar optimizada) y es por eso que os animamos a mejorarla y hacerla más rápida utilizando un algoritmo diferente. La implementación es bastante ilegible (al estar optimizada) y es por eso que os animamos a mejorarla y hacerla más rápida utilizando un algoritmo diferente.
  
-CÓMO PASAR NUESTRO PROGRAMA BASIC A UN FICHERO TAP+\\  
 +===== Cómo pasar nuestro programa BASIC a un fichero TAP =====
  
 Bas2tap es una utilidad muy interesante que permite teclear nuestros programas en BASIC en nuestro ordenador personal y después generar un tap tal y como si lo hubieramos tecleado en el editor del Spectrum. Bas2tap es una utilidad muy interesante que permite teclear nuestros programas en BASIC en nuestro ordenador personal y después generar un tap tal y como si lo hubieramos tecleado en el editor del Spectrum.
Línea 493: Línea 581:
 Si grabamos el Listado 1 como zxmines.bas, podemos generar un tap del juego mediante: Si grabamos el Listado 1 como zxmines.bas, podemos generar un tap del juego mediante:
  
 +<code>
  bas2tap -a1 -szxmines zxmines.bas  bas2tap -a1 -szxmines zxmines.bas
 +</code>
  
 La sintaxis (explicada) es: La sintaxis (explicada) es:
  
 +<code>
  -a1 : línea de autoarranque (en qué línea autocomenzará el programa sin  -a1 : línea de autoarranque (en qué línea autocomenzará el programa sin
        necesidad de poner RUN tras el LOAD "").        necesidad de poner RUN tras el LOAD "").
  -szxmines : título del programa BASIC.  -szxmines : título del programa BASIC.
  zxmines.bas : programa a "compilar" o "traducir".  zxmines.bas : programa a "compilar" o "traducir".
 +</code>
  
 Podéis descargar BAS2TAP de la sección de Utilidades de WorldOfSpectrum. Podéis descargar BAS2TAP de la sección de Utilidades de WorldOfSpectrum.
-LINKS 
  
-    * Fichero basic_zxmines.zip conteniendo: zxmines.*, Makefile, README +\\  
-    * Fichero zxmines.tap (el programa ya compilado) +===== Enlaces ===== 
-    * Concurso de BASIC 2003 de Bytemaniacos + 
-    * SevenuP +    {{:cursos:basic:basic_zxmines.zip|Fichero basic_zxmines.zip}} conteniendo: zxmines.*, Makefile, README. 
-    * bas2tap+    * {{:cursos:basic:zxmines.tap|Fichero zxmines.tap}} (el programa ya compilado). 
 +    * [[http://www.bytemaniacos.com/html/basic2003.html|Concurso de BASIC 2003 de Bytemaniacos]]. 
 +    * [[http://www.speccy.org/metalbrain/|Editor gráfico SevenuP]]. 
 +    * [[http://www.worldofspectrum.org/utilities.html#other|Herramienta bas2tap]].
  
  
 \\  \\ 
-;#;+;;#
 **Santiago Romero\\ Septiembre 2005\\ Magazine ZX #12** **Santiago Romero\\ Septiembre 2005\\ Magazine ZX #12**
-;#;+;;#
  
  • cursos/basic/zxmines_ejemplo_comentado.1237576586.txt.gz
  • Última modificación: 20-03-2009 19:16
  • por sromero