You are on page 1of 13

Comprobador de Laberintos.

Se presenta a continuacin un programa que se encarga de comprobar una serie de requisitos, entre los cuales est el de hallar un camino libre, habiendo realizado las siguientes comprobaciones: Hay una casilla de comienzo del personaje, y slo una.(1) Hay una casilla de meta, y slo una.(2) Todo el permetro del laberinto son paredes (para que el personaje no pueda escapar por los bordes).(3) La posicin de comienzo del personaje no est en un borde del laberinto.(4) Existe un camino libre que permite llegar desde la posicin del personaje a la meta. (Este ltimo paso, nicamente se realizara si el laberinto cumple todos y cada uno de los anteriores requisitos).(5)

El laberinto a comprobar estar elaborado en una archivo .txt, que adems contar en su primera lnea con dos nmeros, los cuales aportan las dimensiones de dicho laberinto, el cual estar constituido por almohadillas (#, cdigo ASCII 35, el lector debe familiarizarse con este ltimo dato, ya que es el utilizado en el cdigo), dichos caracteres conforman las paredes y bordes del laberinto. Por otra parte, est el asterisco (*, cdigo ASCII 42) que representa la casilla de inicio, y el arroba (@, cdigo ASCII 64, el lector debe familiarizarse con este ltimo dato, ya que es el utilizado en el cdigo) que representa la casilla de meta. A continuacin se muestra un ejemplo del contenido de dicho fichero y la distribucin de las dimensiones y el laberinto incluidos en el:

A efectos prcticos, se ha optado por utilizar la directiva del preprocesador de C que sirve para definir constantes, #define, de esta forma se facilita el uso del asterisco dentro del programa, as como tambin true y false: #define MARCA '*' #define TRUE 1 #define FALSE 0 Cabe decir que para mejorar la esttica con el fin de facilitar en gran medida la visibilidad de las distintas partes del laberinto, se ha hecho uso de la funcin: SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),n); //Donde n es el cdigo del color utilizado.

El programa consta principalmente de dos funciones (adems del main) claramente diferenciadas (la funcin comprobar expuesta a continuacin es llamada dentro de la funcin abrirlab): -void abrirlab(); -void comprobar(FILE *,char [],int *, int *); Funcin void abrirlab() : Esta funcin es el pilar del programa, ya que en ella se realiza la mayor parte de las comprobaciones as como tambin del desarrollo llevado a cabo para encontrar un camino libre. La primera dificultad que encontramos es el manejo del contenido del fichero, por lo que se procede a rellenar un comnmente llamado, array de caracteres bidimensional, con los 3 caracteres que conforman el laberinto. Para ello se requiere las dimensiones de dicho array que se encuentran en la primera lnea del fichero, dos nmeros separados por un espacio, el primero de ellos indica la dimensin vertical del array, y el segundo de ellos indica la dimensin horizontal. Dichos nmeros son ledos por la funcin fscanf, que adems asigna a las variables dimy y dimx sus correspondientes valores: fscanf(filePtr,"%d %d",&dimx,&dimy); Posteriormente se procede a rellenar el array bidimensional al que llamaremos lab[dimy][dimx], para ello hacemos uso de un elemento auxiliar , una cadena de caracteres frase[dimx], la cual , una vez asignada, es pasada a la correspondiente fila del array, mediante el siguiente bucle descrito a continuacin: while(fgetc (filePtr)!= '\n') { }//Posicionamos el puntero en el principio de la segunda lnea del fichero.txt

i=1; do { for(j=1;j<=dimx;j++) { fscanf(filePtr,"%c",&frase[j]); if(frase[j]==42 || frase[j]==64) { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7); printf("%c",frase[j]); } else { SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),6); printf("%c",frase[j]); } fila del laberinto. } printf("\n"); i++; while(fgetc (filePtr)!= '\n') { }//Posicionamos el puntero en el principio de la siguiente lnea del fichero.txt }while(i<=(dimy-1)); for(j=1;j<=dimx;j++) { fscanf(filePtr,"%c",&frase[j]); printf("%c",frase[j]); lab[i][j]=frase[j];//Paso de la ltima cadena de caracteres a la ltima fila del laberinto. } lab[i][j]=frase[j];//Paso de la cadena del fichero a la

A continuacin, se cierra el fichero dado que a partir de este momento trabajaremos con el array bidimensional rellenado (lab[dimy][dimx]). Por lo que se procede a realizar la comprobacin del requisito (3). Para ello recorremos las paredes de lab, que coinciden con la primera y ltima fila as como la primera y ltima columna, mediante los siguientes bucles descritos a continuacin, en el caso de que alguna pared contenga un carcter distinto de almohadilla, ser entonces cuando el contador incremente. Anlogamente, ocurre en la comprobacin del requisito (4), en el caso de que alguna pared contenga al menos un asterisco, ser entonces cuando el contador incremente. Por tanto, para que cada unas de las condiciones se cumpla ( 3 y 4), sus correspondientes contadores tienen que valer cero, de lo contrario supondr que alguna de las paredes no se encuentra correctamente cerrada, y/o que la posicin de inicio se encuentra en un borde del laberinto. for(j=1;j<=dimx;j++) { i=1; if(lab[i][j]!=35) { pared1++; //Comprueba Pared superior horizontal } if(lab[i][j]==42){ posicion inicial pos1++;//Comprueba Pared superior horizontal} i=dimy; horizontal if(lab[i][j]!=35) pared2++; //Comprueba Pared inferior if(lab[i][j]==42){ posicin inicial } } for(i=1;i<=dimy;i++) { j=1; if(lab[i][j]!=35) { pared3++; //Comprueba Pared izquierda vertical pos2++; //Comprueba Pared inferior horizontal-

} if(lab[i][j]==42){ posicion inicial l } j=dimx; if(lab[i][j]!=35) pared4++; //Comprueba Pared derecha vertical if(lab[i][j]==42){ posicin inicial pos4++; //Comprueba Pared derecha verticalpos3++; //Comprueba Pared izquierda vertica-

} }

Dicho razonamiento est plasmado en las siguientes lneas de cdigo:

if(pared1==0 && pared2==0 && pared3==0 && pared4==0) { printf("\n\nEl recinto SI esta cerrado.\n"); ok1=1; } if(pos1==0 && pos2==0 && pos3==0 && pos4==0) { printf("\nLa posicion de inicio NO esta en un borde.\n"); ok2=1; } La asignacin de ok1 y ok2 el valor de 1, tiene como finalidad, proporcionar un resultado binario, 1 si se cumple la condicin correspondiente, y 0 en el caso contrario. Posteriormente, se capturan los valores de las posiciones de inicio y meta dentro del laberinto mediante este bucle: for(i=1;i<=dimy;i++) { for(j=1;j<=dimx;j++)

{ if(lab[i][j]==42) //Asignacin a x1 e y1 los valores de la posicin de la casilla de inicio. { x1=i; y1=j; } if(lab[i][j]==64) //Asignacin a x2 e y2 los valores de la posicin de la casilla de meta. { x2=i; y2=j; } } } El siguiente paso a realizar es llamar a la funcin comprobar, la cual se explicar con ms posteriormente. Dicha funcin nos devuelve dos valores ok3 y ok4, que pueden tener el valor de 1 , afirmando que la condicin correspondiente se cumple, y 0 en caso contrario. Es entonces cuando se realizar la siguiente comprobacin: if(((ok1==1 && ok2==1)&& ok3==1)&& ok4==1) La cual se asegura de que todas y cada una de las 4 condiciones verificadas anteriormente se cumplan(es decir, que todas las variables ok n tengan el valor 1) Por tanto, si y solo si se cumple dicha sentencia, se proceder a resolver el laberinto, es decir, encontrar un camino libre que nos lleve desde la posicin de inicio hasta la meta. En caso de no cumplirse dicha sentencia, entonces la condicin (5) no se cumplira al no cumplirse las anteriores, y por ende, no se procede a resolver el laberinto. Suponiendo que se cumplan las 4 condiciones anteriores, se procede a solucionar el laberinto haciendo uso de la recursividad. La solucin se basa en una tarea que se realizar recursivamente para intentar solucionar el laberinto a partir de una posicin particular(desde la posicin de inicio). Desde cualquier lugar dentro del laberinto se intentar resolverlo primero intentando hacia la derecha, luego hacia abajo, luego hacia la izquierda y finalmente arriba. Para hacer estos intentos se emplear un bucle do-while. El caso base de la recursividad (condicin de termino) es que las coordenadas correspondan con la salida del laberinto. En caso contrario, se procede a marcar la casilla y a intentar en las distintas direcciones (en sentido horario

empezando por la derecha), asegurndose primero que en esa direccin haya una casilla valida (no almohadilla, no visitada, es decir no marcada). En caso en que ninguna direccin nos lleva a la salida, se infecta la casilla actual(la casilla ser marcada con un punto, de esta forma no se volver a pasar por este camino) pues se va a retroceder, es aqu cuando empieza el denominado back tracking o vuelta atrs. Las marcas sirven tambin para sealar la ruta que conforma la solucin, una vez alcanzada. El procedimiento parte de la posicin de entrada. Para ello nos posicionamos asignando a j e i los valores de y1 y x1 correspondientes a la casilla de inicio: i=x1; j=y1;

Descripcin del algoritmo: 1. Inicialmente listo vale FALSO (0) listo=FALSE; 2. Si la posicin es el fin del laberinto, listo es TRUE (1) if(i==x2 && j==y2) listo=TRUE;

3. Se marca la casilla if(i!=x2 && j!=y2) lab[i][j]=MARCA;

4. Si no se ha solucionado (listo vale FALSO) y la posicin de la derecha es vlida if(listo==FALSE &&(lab[i][j+1]==32||lab[i][j+1]==64)) 4.1. Nos desplazamos hacia la casilla de la derecha (j=j+1;).Se comprueba si la posicin es el fin del laberinto (de ser as listo es TRUE), de lo contrario se marca la casilla (lab[i][j]=MARCA;). 5. Si no se ha solucionado (listo vale FALSO) y la posicin de la abajo es vlida if(listo==FALSE &&(lab[i+1][j]==32||lab[i+1][j]==64)) 5.1. Nos desplazamos hacia la casilla de abajo (i=i+1;). Se comprueba si la posicin es el fin del laberinto (de ser as listo es TRUE), de lo contrario se marca la casilla (lab[i][j]=MARCA;). 6. Si no se ha solucionado (listo vale FALSO) y la posicin de la izquierda es vlida if(listo==FALSE &&(lab[i][j-1]==32||lab[i][j-1]==64))

6.1. Nos desplazamos hacia la casilla de la izquierda (j=j-1;). Se comprueba si la posicin es el fin del laberinto (de ser as listo es TRUE), de lo contrario se marca la casilla. (lab[i][j]=MARCA;). 7. Si no se ha solucionado (listo vale FALSO) y la posicin de arriba es vlida if(listo==FALSE &&(lab[i-1][j]==32||lab[i-1][j]==64)) 7.1. Nos desplazamos hacia la casilla de arriba (i=i-1;). Se comprueba si la posicin es el fin del laberinto (de ser as listo es TRUE), de lo contrario se marca la casilla (lab[i][j]=MARCA;). Repeticin de estos pasos a menos que la sentencia siguiente se cumpla: 8. Si no se ha solucionado (listo vale FALSO) y ninguna posicin es vlida,(se ha quedado encerrado) if((((listo==FALSE &&(lab[i][j+1]!=32 && lab[i][j+1]!=64))&&(lab[i+1][j]! =32 && lab[i+1][j]!=64))&&(lab[i][j-1]!=32 && lab[i][j-1]!=64))&&(lab[i-1] [j]!=32 && lab[i-1][j]!=64)) , entonces se procede a realizar la vuelta atrs. 8.1. Si el ltimo movimiento realizado (antes de iniciar al back tracking) a sido hacia la derecha (if k==1), se infecta la casilla actual (se coloca un punto en dicha posicin). Entonces se comprueba si la posicin anterior(es decir la posicin de la casilla de la izquierda) ha sido visitada,(MARCA) y se comprueba tambin si el resto de casillas contiguas son distintas de la meta y/o de un espacio. De cumplirse dicho requisito, nos desplazamos hacia la izquierda, e infectamos dicha posicin. Posteriormente se procede a intentar en las distintas direcciones abajo y arriba, siguiendo el procedimiento anterior, siempre y cuando no haya alguna casilla valida contigua a nuestra posicin actual: lab[i][j]=46;//Infectamos la casilla(se coloca un punto en dicha casilla) if(((lab[i][j-1]==MARCA &&(lab[i][j+1]!=32 && lab[i] [j+1]!=64))&&(lab[i+1][j]!=32 && lab[i+1][j]!=64))&&(lab[i-1][j]!=32 && lab[i-1][j]!=64))//Comprueba si ya ha visitado la casilla(MARCA) { j=j-1;//Mov izquierda lab[i][j]=46;//Infectamos la casilla } if(((lab[i+1][j]==MARCA &&(lab[i][j+1]!=32 && lab[i] [j+1]!=64))&&(lab[i-1][j]!=32 && lab[i-1][j]!=64))&&(lab[i][j-1]!=32 && lab[i][j-1]!=64))//Comprueba si ya ha visitado la casilla(MARCA) {

i=i+1;//Nos desplazamos hacia abajo lab[i][j]=46;//Infectamos la casilla } if(((lab[i-1][j]==MARCA &&(lab[i][j+1]!=32 && lab[i] [j+1]!=64))&&(lab[i+1][j]!=32 && lab[i+1][j]!=64))&&(lab[i][j-1]!=32 && lab[i][j-1]!=64))//Comprueba si ya ha visitado la casilla(MARCA) { i=i-1;//Nos desplazamos hacia arriba lab[i][j]=46;//Infectamos la casilla } if(lab[i][j+1]==32 || lab[i+1][j]==32 || lab[i][j-1]==32 || lab[i-1][j]==32)//Si esta contiguo a algun espacio MARCA la casilla(de esta forma se une el camino correctamente). { lab[i][j]=MARCA; } if(((lab[i][j+1]!=32 && lab[i+1][j]!=32)&& lab[i][j-1]! =32)&& lab[i-1][j]!=32)//Si se ha vuelto a quedar encerrado entre casillas infectadas y/o almohadillas o asteriscos entonces realizar el mov hacia derecha. { if(lab[i][j+1]==MARCA) { derecha j=j+1;//Nos desplazamos hacia la lab[i][j]=46;//Infectamos la casilla } } 8.2. Si el ltimo movimiento realizado (antes de iniciar al back tracking) a sido hacia abajo (if k==2), se infecta la casilla actual (se coloca un punto en dicha posicin). Entonces se comprueba si la posicin anterior(es decir la posicin de la casilla de arriba) ha sido visitada,(MARCA) y se comprueba tambin si el resto de casillas contiguas son distintas de la meta y/o de un espacio. De cumplirse dicho requisito, nos desplazamos hacia arriba, e infectamos dicha posicin. Posteriormente se procede a intentar en las distintas direcciones izquierda, derecha y abajo, siguiendo el procedimiento anterior, siempre y cuando no haya alguna casilla valida contigua a nuestra posicin actual:

lab[i][j]=46;//Infectamos la casilla if(((lab[i-1][j]==MARCA &&(lab[i][j+1]!=32 && lab[i] [j+1]!=64))&&(lab[i+1][j]!=32 && lab[i+1][j]!=64))&&(lab[i][j-1]!=32 && lab[i][j-1]!=64))//Comprueba si ya ha visitado la casilla(MARCA) { i=i-1; lab[i][j]=46;//Infectamos la casilla } if(((lab[i][j-1]==MARCA &&(lab[i][j+1]!=32 && lab[i] [j+1]!=64))&&(lab[i+1][j]!=32 && lab[i+1][j]!=64))&&(lab[i-1][j]!=32 && lab[i-1][j]!=64))//Comprueba si ya ha visitado la casilla(MARCA) { j=j-1;//Mov izquierda lab[i][j]=46;//Infectamos la casilla } if(((lab[i][j+1]==MARCA &&(lab[i+1][j]!=32 && lab[i+1][j]!=64))&&(lab[i-1][j]!=32 && lab[i-1][j]!=64))&& (lab[i][j-1]!=32 && lab[i][j-1]!=64))//Comprueba si ya ha visitado la casilla(MARCA) { j=j+1; //Nos desplazamos hacia la derecha lab[i][j]=46;//Infectamos la casilla } || lab[i-1][j]==32) if(lab[i][j+1]==32 || lab[i+1][j]==32 || lab[i][j-1]==32 { lab[i][j]=MARCA; } if(((lab[i][j+1]!=32 && lab[i+1][j]!=32)&& lab[i][j-1]! =32)&& lab[i-1][j]!=32)//Si se ha vuelto a quedar encerrado entre casillas infectadas y/o almohadillas o asteriscos entonces realizar el mov hacia abajo. { if(lab[i+1][j]==MARCA) { i=i+1;//Nos desplazamos hacia abajo lab[i][j]=46;//Infectamos la casilla }

8.3. Si el ltimo movimiento realizado (antes de iniciar al back tracking) a sido hacia la izquierda (if k==3), se infecta la casilla actual (se coloca un punto en dicha posicin). Entonces se comprueba si la posicin anterior(es decir la posicin de la casilla de la derecha) ha sido visitada,(MARCA) y se comprueba tambin si el resto de casillas contiguas son distintas de la meta y/o de un espacio. De cumplirse dicho requisito, nos desplazamos hacia la derecha, e infectamos dicha posicin. Posteriormente se procede a intentar en las distintas direcciones derecha, abajo y arriba, siguiendo el procedimiento anterior, siempre y cuando no haya alguna casilla valida contigua a nuestra posicin actual:

lab[i][j]=46;//Infectamos la casilla if(((lab[i][j+1]==MARCA &&(lab[i+1][j]!=32 && lab[i+1][j]!=64))&&(lab[i-1][j]!=32 && lab[i-1][j]!=64))&& (lab[i][j-1]!=32 && lab[i][j-1]!=64))//Comprueba si ya ha visitado la casilla(MARCA) { j=j+1; //Nos desplazamos hacia la derecha lab[i][j]=46;//Infectamos la casilla } if(((lab[i+1][j]==MARCA &&(lab[i][j+1]!=32 && lab[i] [j+1]!=64))&&(lab[i-1][j]!=32 && lab[i-1][j]!=64))&&(lab[i][j-1]!=32 && lab[i][j-1]!=64))//Comprueba si ya ha visitado la casilla(MARCA) { i=i+1;//Nos desplazamos hacia abajo lab[i][j]=46; } if(((lab[i-1][j]==MARCA &&(lab[i][j+1]!=32 && lab[i] [j+1]!=64))&&(lab[i+1][j]!=32 && lab[i+1][j]!=64))&&(lab[i][j-1]!=32 && lab[i][j-1]!=64))//Comprueba si ya ha visitado la casilla(MARCA) { i=i-1;//Nos desplazamos hacia arriba lab[i][j]=46;//Infectamos la casilla } if(lab[i][j+1]==32 || lab[i+1][j]==32 || lab[i][j-1]==32 || lab[i-1][j]==32)//Si esta contiguo a algun espacio MARCA la casilla(de esta forma se une el camino correctamente).

{ lab[i][j]=MARCA; } if(((lab[i][j+1]!=32 && lab[i+1][j]!=32)&& lab[i][j-1]! =32)&& lab[i-1][j]!=32)//Si se ha vuelto a quedar encerrado entre casillas infectadas y/o almohadillas o asteriscos entonces realizar el mov hacia izquierda. { la casilla(MARCA) if(lab[i][j-1]==MARCA)//Comprueba si ya ha visitado { j=j-1; lab[i][j]=46;//Infectamos la casilla } }

8.4. Si el ltimo movimiento realizado (antes de iniciar al back tracking) a sido hacia arriba (if k==4), se infecta la casilla actual (se coloca un punto en dicha posicin). Entonces se comprueba si la posicin anterior(es decir la posicin de la casilla de abajo) ha sido visitada,(MARCA) y se comprueba tambin si el resto de casillas contiguas son distintas de la meta y/o de un espacio. De cumplirse dicho requisito, nos desplazamos hacia abajo, e infectamos dicha posicin. Posteriormente se procede a intentar en las distintas direcciones abajo, derecha e izquierda, siguiendo el procedimiento anterior, siempre y cuando no haya alguna casilla valida contigua a nuestra posicin actual:

lab[i][j]=46;//Infectamos la casilla if(((lab[i+1][j]==MARCA &&(lab[i][j+1]!=32 && lab[i] [j+1]!=64))&&(lab[i-1][j]!=32 && lab[i-1][j]!=64))&&(lab[i][j-1]!=32 && lab[i][j-1]!=64))//Comprueba si ya ha visitado la casilla(MARCA) { i=i+1;//Nos desplazamos hacia abajo lab[i][j]=46;//Infectamos la casilla } if(((lab[i][j+1]==MARCA &&(lab[i+1][j]!=32 && lab[i+1][j]!=64))&&(lab[i-1][j]!=32 && lab[i-1][j]!=64))&& (lab[i][j-1]!=32 && lab[i][j-1]!=64))//Comprueba si ya ha visitado la casilla(MARCA)

{ j=j+1; //Nos desplazamos hacia la derecha lab[i][j]=46;//Infectamos la casilla } if(((lab[i][j-1]==MARCA &&(lab[i][j+1]!=32 && lab[i] [j+1]!=64))&&(lab[i+1][j]!=32 && lab[i+1][j]!=64))&&(lab[i-1][j]!=32 && lab[i-1][j]!=64))//Comprueba si ya ha visitado la casilla(MARCA) { j=j-1;//Mov izquierda lab[i][j]=46;//Infectamos la casilla } if(lab[i][j+1]==32 || lab[i+1][j]==32 || lab[i][j-1]==32 || lab[i-1][j]==32)//Si esta contiguo a algun espacio MARCA la casilla(de esta forma se une el camino correctamente). { lab[i][j]=MARCA; } if(((lab[i][j+1]!=32 && lab[i+1][j]!=32)&& lab[i][j-1]! =32)&& lab[i-1][j]!=32)//Si se ha vuelto a quedar encerrado entre casillas infectadas y/o almohadillas o asteriscos entonces realizar el mov hacia arriba. { if(lab[i-1][j]==MARCA) { i=i-1;//Nos desplazamos hacia arriba lab[i][j]=46;//Infectamos la casilla } }