Mostrando entradas con la etiqueta Matemática - programación. Mostrar todas las entradas
Mostrando entradas con la etiqueta Matemática - programación. Mostrar todas las entradas

Las Torres de Hanoi / [programa PASCAL]




La leyenda de las Torres de Hanoi es imprecisa y su origen parece incierto, pero algunos tienen por seguro que Dios, durante la creación del mundo, colocó tres varillas de diamante con 64 discos de oro dispuestos en la primera de ellas en un templo de una antigua ciudad de la India. Confió la obligación de mover los 64 discos sagrados a los monjes del templo siguiendo unas normas muy concretas y les advirtió que cuando el último disco hubiera sido movido a su posición final el mundo se acabaría.
Esta es al menos la historia que difundió el matemático Édouard Lucas, bajo el pseudónimo N. Claus de Siam (anagrama de Lucas d'Amiens) junto con el juego de las Torres de Hanoi que él mismo inventó en 1883. Su intención era puramente propagandística, una artimaña publicitaria de las que abundaban a finales del siglo XIX, pero tuvo tanto éxito que perduró.
Según la leyenda los discos únicamente podrían moverse de uno en uno. Además, nunca podría colocarse un disco sobre otro más pequeño que él y sólo podrían depositarse los discos sagrados ensartados en las varillas de diamante, jamás en otro lugar.
Con estas sencillas reglas Lucas compuso un juego matemático de gran atractivo que se convirtió con el tiempo de uno de los problemas favoritos de la matemática recreativa por su versatilidad para explicar conceptos de campos muy diversos, desde la computación a la combinatoria, pasando por otros muchos.


El juego original propuesto por Édouard Lucas constaba de 8 discos de madera y pretendía ser originario de Oriente. Se supone que había sido traído desde los territorios de la Indochina colonial francesa y fue bautizado con el nombre de “La Torre de Hanoi”, en singular. Sin embargo, con el paso del tiempo, pasó a llamarse las Torres de Hanoi, en plural, algo que sólo se puede atribuir a un efecto colateral de su popularidad: mientras se juega no es una torre sino varias las que se forman, por lo que es comprensible que la gente usara el plural. El nombre en singular, atribuido por Lucas, acabaría rindiéndose ante el clamor popular.



El programa:

El juego matemático de las Torres de Hanoi puede resolverse mediante diversos algoritmos. En el siguiente programa se calcula con un algoritmo recursivo la solución y a la vez se representan gráficamente los movimientos realizados con los discos. El número de movimientos mínimo que se deben realizar para resolver el problema con este algoritmo es de 2n – 1, siendo n el número de discos.


Ejemplo de ejecución del programa

La posición de cada disco en cada una de las 3 varillas se almacena en una matriz que se actualiza con cada movimiento. El número máximo de discos es 9 por una cuestión práctica, dado que el tiempo de resolución se incrementa exponencialmente. Además el programa permite regular la velocidad a la que se visualiza la resolución del problema.

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


PROGRAM torres_hanoi;
{por TDM para arqui-2.blogspot.com.es (2009)}


USES CRT;


CONST ndiscosmax  = 10;
      nvarillas = 3;

      esperabase = 1440;

      cx = 10; cy = 20;

TYPE tmatriz = ARRAY [1..ndiscosmax, 1..nvarillas] OF INTEGER;

VAR

   matriz :tmatriz;

   ndiscos, velocidad, h :INTEGER;



FUNCTION valor_obligado (limiteinf, limitesup :INTEGER; texto :STRING):INTEGER;


VAR valor :INTEGER;


BEGIN

     REPEAT

           WRITE(texto);
           READLN(valor)


     UNTIL (valor > limiteinf) AND (valor < limitesup);

     valor_obligado := valor

END;



PROCEDURE introducir_datos (VAR ndiscos, velocidad :INTEGER);


BEGIN

     CLRSCR;
     TEXTCOLOR(7);

     ndiscos := valor_obligado (0, ndiscosmax, 'Introduzca el numero de discos (de 1 a 9): ');

     velocidad := valor_obligado (0, 6, 'Velocidad de ejecucion del programa (de 1 a 5): ');

END;


PROCEDURE cargarmatriz;

VAR i  :INTEGER;

BEGIN

           FOR i := 1 TO ndiscos DO

           matriz[i,1] := ndiscos-i+1;
END;



PROCEDURE intercambio (r, i, s, j :INTEGER);


BEGIN

     matriz[s,j] := matriz[r,i]+ matriz[s,j];
     matriz[r,i] := -(matriz[r,i] - matriz[s,j]);
     matriz[s,j] := matriz[s,j] - matriz[r,i]

END;


PROCEDURE dibmatriz(matrizp :tmatriz);

VAR i,j,k :INTEGER;

BEGIN

     CLRSCR;

     FOR i:=ndiscos DOWNTO 1 DO BEGIN

         FOR j:=1 TO nvarillas DO BEGIN

             IF matrizp[i,j] = 0 THEN BEGIN

                GOTOXY(cx + j*ndiscos*2, cy-i);
                TEXTCOLOR(7);
                WRITE('│')

                END

             ELSE BEGIN

                GOTOXY(cx + j*ndiscos*2 - matrizp[i,j] +1, cy-i);

                FOR k:= 1 TO matrizp[i,j]*2-1 DO BEGIN
                    TEXTCOLOR(matrizp[i,j]+1);
                    WRITE('▄')
                    END;
             END;

         END;


     END;

     GOTOXY(cx + ndiscos, cy);

     FOR k := 1 TO ndiscos*2*nvarillas +1 DO BEGIN

         TEXTCOLOR(7);
         WRITE('═')
         END;

     WRITELN;

END;




PROCEDURE mover (r,s :INTEGER);

VAR i, j :INTEGER;


BEGIN

     i:=0;

     WHILE matriz[i+1,r] <> 0 DO  {Busca el primer elemento no 0}
           i:= i+1;

     j:=1;

     WHILE matriz[j,s] <> 0 DO  {Busca el primer elemento 0}
           j:= j+1;

     intercambio(i,r,j,s);

     dibmatriz(matriz);


     DELAY(TRUNC(esperabase/velocidad))

END;




PROCEDURE hanoi (discos, a, b :INTEGER);


BEGIN

     IF discos=1 THEN mover(a,b)

     ELSE BEGIN
        hanoi(discos-1, a, 6-a-b); mover(a,b); hanoi(discos-1, 6-a-b, b)
     END;


END;




BEGIN

     CLRSCR;

     introducir_datos (ndiscos, velocidad);

     cargarmatriz;

     dibmatriz(matriz);
     DELAY(1000);


     hanoi (ndiscos,1,3);

     READKEY

END.



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.