ANEXO: Juego de carreras en Turbo Pascal



Los simuladores de carreras son un género clásico, habituales desde el mismo comienzo de los juegos informáticos. En este caso se trata de un pequeño programa en Turbo Pascal con gráficos generados en modo texto. 


Ejemplo de ejecución del programa
(carrera hasta la meta)



El trazado de la carretera y la posición de los enemigos son aleatorios y el movimiento del coche se produce sólo cuando se escoge un movimiento. Puedes permanecer parado, pero entonces el tiempo transcurre en tu contra. El programa calcula las colisiones con los laterales de la carretera o con los coches rivales. Asimismo se cronometra el tiempo invertido en llegar hasta la meta, comparando las marcas de cada partida y guardando el récord.



Ejemplo de ejecución del programa
(carreras con colisiones)


Sencillo pero entretenido.

El código del programa completo en lenguaje Turbo PASCAL es el siguiente:


PROGRAM carreras;

{por TDM para Arqui-2.blogspot.com.es (2009)}

USES DOS, CRT;

{Librerias: CRT para CLRSCR, GOTOXY, KEYPRESSED, etc.
            DOS para GETTIME, computo de tiempo para el record.}

CONST
     margen_hz = 35;
     margen_vt = 15; {Los margenes horizontal y vertical
                      marcan el punto de origen 0,0 en pantalla a
                      partir del cual se generan los distintos elementos:
                      carretera, coche, enemigos, meta y colisiones.}

     l_crrt    = 30; {Longitud en tramos de la carretera.}

TYPE
     VECTOR = ARRAY [1..4] OF SHORTINT;
                     {Se define un tipo de variable ARRAY de cuatro elementos
                     para almacenar los valores de posición de los tramos de
                     carretera visibles en pantalla.}

VAR                                      

   t_inicial, t_final, t_record :LONGINT; {RECORD}

   tramo :VECTOR;                         {CARRETERA}

   pos_coche, mov :SHORTINT;              {COCHE}

   p_enemigo_x, p_enemigo_y :SHORTINT;    {ENEMIGO}
   hay_enemigo :BOOLEAN;

   meta, choque, seguir :BOOLEAN;         {CONCLUSION/NUEVA PARTIDA}
   paso :BYTE;


FUNCTION tiempo_reloj :LONGINT;

         {La función obtiene el tiempo de reloj en un instante en

             horas : minutos : segundos : centesimas

         y convierte el tiempo total en centésimas para operar con
         un único valor.}

VAR
   h, m, s, cs :WORD;
   t :LONGINT;

BEGIN

     GETTIME(h, m, s, cs);
     t:= h*60+m; t:=t*60+s; t:=t*100+cs;
     tiempo_reloj:= t;

END;

FUNCTION tiempo_de_ejecucion (t1_, t2_ :LONGINT):LONGINT;


BEGIN

     IF t2_ >= t1_ THEN tiempo_de_ejecucion:= t2_-t1_
        ELSE tiempo_de_ejecucion:= t2_ - t1_ + 24*60*60*100
END;

PROCEDURE presentacion;


VAR i, j :INTEGER;

BEGIN

     CLRSCR;

     GOTOXY(margen_hz-14, margen_vt-4); WRITELN('BIENVENIDO AL JUEGO DE LOS AUTOS LOCOS');
     GOTOXY(margen_hz-14, margen_vt-2); WRITELN('  Pulse "a" para mover a la izquierda');
     GOTOXY(margen_hz-14, margen_vt-1); WRITELN('  Pulse "d" para mover a la derecha');
     GOTOXY(margen_hz-14, margen_vt  ); WRITELN('  Pulse "enter" para ir adelante');
     GOTOXY(margen_hz-14, margen_vt+2); WRITELN(' (Pulse una tecla para EMPEZAR)');

     i:= 1; j:=1;

     REPEAT
           DELAY(100);
           GOTOXY(margen_hz -i, margen_vt -6); WRITELN('  *  ');
           GOTOXY(margen_hz +i, margen_vt -7); WRITELN('  #  ');
           GOTOXY(margen_hz+18, margen_vt+2);

           i:= i+j;
           IF ABS(i)=8 THEN j:= -j;

     UNTIL keypressed;

END;

PROCEDURE iniciar_variables (VAR paso_ :BYTE; VAR meta_, choque_ :BOOLEAN; VAR tramo_:VECTOR;
                             VAR p_ch, p_enmg_x, p_enmg_y :SHORTINT; VAR hay_enmg_ :BOOLEAN);

VAR i:INTEGER;

BEGIN

     paso_ := 1;         {La partida se inicia en el tramo 1 de carretera.}

     meta_  := FALSE;
     choque_:= FALSE;    {Se inician las variables booleanas de llegada y colisión en FALSE.}


     {CARRETERA:}

     FOR i:= 1 TO 2 DO
               tramo_[i]:= 0;

                         {El primer y segundo tramos se inician con valor 0, el resto se
                          genera aleatoriamente. Los valores serán negativos cuando
                          los tramos se situen a la izquierda del origen y positivos
                          cuando se situen a la derecha.}

     RANDOMIZE;

     FOR i:= 3 TO 4 DO
               tramo_[i]:= tramo_[i-1] + RANDOM(3) - 1;

     {COCHE:}

     p_ch:= RANDOM(3) +1;      {La variable toma valores iniciales entre 1 y 3
                                    |*  |  --> pos_coche = 1
                                    | * |  --> pos_coche = 2
                                    |  *|  --> pos_coche = 3
                                Valores menores que 0 ó superiores a 3 indicarán colisión.}

     {ENEMIGO:}

     hay_enmg_ := TRUE;

     p_enmg_x:= RANDOM(3) +1;
     p_enmg_y:= 4;              {La variable X de posición del enemigo determina,
                                 como en el coche, su ubicación en el ancho de la carretera.
                                 Varía siempre entre 1 y 3.
                                     |#  |  -->p_enemigo_x = 1
                                     | # |  -->p_enemigo_x = 2
                                     |  #|  -->p_enemigo_x = 3
                                 La variable Y de posición determina el tramo de carretera
                                 en que se encuentra, inicialmente, el tramo 4.}

END;

PROCEDURE pintar_carretera (tramo_:VECTOR);

VAR i :INTEGER;

BEGIN

     FOR i:= 1 TO 4 DO
         BEGIN
              GOTOXY(margen_hz + tramo_[i], margen_vt -i +1);
              WRITE('|   |');
         END;

END;


PROCEDURE pintar_coche (tramo_:VECTOR; p_ch :SHORTINT);

BEGIN

     GOTOXY(margen_hz + tramo_[1] + p_ch, margen_vt);
     WRITE('*');

END;


PROCEDURE pintar_enemigo(tramo_:VECTOR; p_enmg_x, p_enmg_y :SHORTINT);

BEGIN

     GOTOXY(margen_hz + p_enmg_x + tramo_[p_enmg_y], margen_vt - p_enmg_y +1);
     WRITE('#');

END;


PROCEDURE pintar_meta (pos_meta_ :BYTE; tramo_:VECTOR);

BEGIN

     GOTOXY(margen_hz + tramo_[pos_meta_] +1, margen_vt - pos_meta_ +1);
     WRITE('---');

END;


PROCEDURE pintar_pantalla (paso_:BYTE; tramo_:VECTOR; p_ch, p_enmg_x, p_enmg_y :SHORTINT; hay_enmg_ :BOOLEAN);

          {El procedimiento limpia la pantalla y dibuja con los valores
          actualizados de las variables cada uno de los elementos en sus
          posiciones.}

BEGIN

     CLRSCR;

     pintar_carretera(tramo_);
     IF paso_ > l_crrt-4 THEN pintar_meta(l_crrt-paso_+1, tramo_);

     pintar_coche(tramo_, p_ch);
     IF hay_enmg_ THEN pintar_enemigo(tramo_, p_enmg_x, p_enmg_y);

END;


PROCEDURE solicitar_movimiento (VAR mov_ :SHORTINT);

          {El procedimiento obtiene las instrucciones del usuario
           necesarias para el movimiento del coche. Aguarda hasta
           una entrada válida de teclado:

             a, A  --> Movimiento a la izquierda --> mov = -1
             d, D  --> Movimiento a la derecha   --> mov = +1
             enter --> Movimiento hacia delante  --> mov =  0 }


CONST
     enter =#13;  {Se introduce el valor ASCII de la tecla "enter".}

VAR mv :CHAR;

BEGIN

     REPEAT

           GOTOXY(margen_hz - 3, margen_vt +1); WRITE('¿Movimiento? ');

           mv:=READKEY;

     UNTIL mv IN[enter, 'a', 'A', 'd', 'D'];


     CASE mv OF

          enter   : mov_ :=  0;
          'a', 'A': mov_ := -1;
          'd', 'D': mov_ :=  1;

     END;

END;


PROCEDURE actualizar_variables (VAR paso_:BYTE; VAR meta_ :BOOLEAN; VAR tramo_:VECTOR;
                                VAR mov_, p_ch, p_enmg_x, p_enmg_y :SHORTINT; VAR hay_enmg_ :BOOLEAN);

          {El procedimiento determina los nuevos valores de cada variable
           en función de la entrada de movimiento dada por el usuario.}

VAR i:INTEGER;

BEGIN

     paso_ := paso_+1;
     IF paso_ = l_crrt THEN meta_ := TRUE;


     {COCHE:}

     p_ch:= p_ch + mov_ + tramo_[1] - tramo_[2];  {Al avanzar, el coche toma su nueva posición en función
                                                   de la posición anterior, del movimiento introducido por
                                                   el usuario y la diferencia de posición entre el tramo de
                                                   partida, [1], y el tramo de llegada, [2].}


     {CARRETERA:}

     FOR i:= 1 TO 3 DO
             tramo_[i]:= tramo_[i+1];  {Al avanzar, cada tramo toma el valor del tramo inmediatamente
                                        posterior. El tramo[4], o nuevo tramo de carretera, se genera
                                        aleatoriamente.}

     tramo_[4]:= tramo_[3] + RANDOM(3) -1;


     {ENEMIGO:}

     IF (p_enmg_y = 1) AND (paso_ <= l_crrt-4) THEN

        BEGIN                    {Se genera un nuevo enemigo cuando el anterior ha llegado al
                                        al primer tramo y aún no ha aparecido la meta en pantalla.}
             p_enmg_x:= RANDOM(3) +1;
             p_enmg_y:= 4;

        END
     ELSE p_enmg_y:= p_enmg_y -1;       
                                        {Si no se genera un nuevo enemigo entonces se mueve el actual
                                         al tramo inmediatamente inferior.}

     IF p_enmg_y <= 0 THEN hay_enmg_ := FALSE;  

       {Si la coordenada Y del enemigo toma un valor inferior a 0, entonces:
                    Ya no se generan nuevos enemigos y el enemigo actual ha salido de pantalla,
                     por tanto no hay enemigos.}


END;


PROCEDURE comprobar_colisiones (choque_ :BOOLEAN; p_ch, p_enmg_x, p_enmg_y :SHORTINT);

BEGIN

     {COLISION CON CARRETERA:}

     IF (p_ch < 1) OR (p_ch > 3) THEN choque:= TRUE;


     {COLISION CON ENEMIGO:}

     IF (p_ch = p_enmg_x) AND (p_enmg_y = 1) THEN choque:= TRUE;


END;

PROCEDURE pintar_colision (tramo_:VECTOR; p_ch :SHORTINT);

BEGIN

     GOTOXY(margen_hz + tramo_[1] + p_ch, margen_vt);
     WRITE('X');

END;


PROCEDURE seguir_jugando (VAR seguir_ :BOOLEAN);

VAR sgr :CHAR;

BEGIN

     REPEAT

           GOTOXY(margen_hz - 3, margen_vt +1);
           WRITE('¿Jugar otra partida? (S/N): ');

           sgr:=READKEY;

     UNTIL sgr IN['s', 'S', 'n', 'N'];

     CASE sgr OF

          's', 'S': seguir_:= TRUE;
          'n', 'N': seguir_:= FALSE;

     END;

END;

PROCEDURE  escribir_record (t_ini_, t_fin_ :LONGINT; VAR t_rec_ :LONGINT);

BEGIN

     GOTOXY(margen_hz -3, margen_vt +2);
     WRITELN('Tiempo empleado: ');

     GOTOXY(margen_hz -3, margen_vt +3);
     WRITE(tiempo_de_ejecucion(t_ini_, t_fin_) DIV 100, ' segundos ');
     WRITE(tiempo_de_ejecucion(t_ini_, t_fin_) MOD 100, ' centesimas');

     IF tiempo_de_ejecucion(t_ini_, t_fin_) < t_rec_ THEN
        BEGIN
             t_rec_:= tiempo_de_ejecucion(t_ini_, t_fin_);

             GOTOXY(margen_hz -3, margen_vt +4);
             WRITELN('¡NUEVO RECORD!')
        END
     ELSE
        BEGIN
             GOTOXY(margen_hz -3, margen_vt +4);
             WRITELN('El record es: ');

             GOTOXY(margen_hz -3, margen_vt +5);
             WRITE(t_rec_ DIV 100, ' segundos ');
             WRITE(t_rec_ MOD 100, ' centesimas');
        END;

END;


BEGIN  {INICIO DEL PROGRAMA PRINCIPAL}

     t_record:= 2147483647; 
                            {Limite maximo del tipo LONGINT, cualquier tiempo logrado en la primera
                             partida mejorará el record.}
     presentacion;

REPEAT {BUCLE DE PARTIDA}

      t_inicial := tiempo_reloj; {Toma de tiempo del momento de inicio de la partida.}

      iniciar_variables(paso, meta, choque, tramo, pos_coche, p_enemigo_x, p_enemigo_y, hay_enemigo);


      REPEAT  {BUCLE DE MOVIMIENTO} {Se repetirá hasta llegada a meta o colisión del coche.}

            pintar_pantalla(paso, tramo, pos_coche, p_enemigo_x, p_enemigo_y, hay_enemigo);

            solicitar_movimiento (mov);

            actualizar_variables (paso, meta, tramo, mov, pos_coche, p_enemigo_x, p_enemigo_y, hay_enemigo);

            comprobar_colisiones (choque, pos_coche, p_enemigo_x, p_enemigo_y);

      UNTIL meta OR choque;


      t_final:= tiempo_reloj; {Toma de tiempo del momento de finalización de la partida.}

      pintar_pantalla(paso, tramo, pos_coche, p_enemigo_x, p_enemigo_y, hay_enemigo);

      IF choque THEN pintar_colision (tramo, pos_coche)
      ELSE escribir_record(t_inicial, t_final, t_record);

      seguir_jugando(seguir);

UNTIL seguir = FALSE;



END.

1 comentario: