Está en la página 1de 15

Trabajo Final de Cátedra

Algoritmo de backtracking
recursivo y no recursivo
para la resolución de un
laberinto y su aplicación en
SDL
Autor
Diaz Ariel Ivan
arielivandiaz@ieee.org

Cátedra
Informática II
Segundo Año Ingeniería Electrónica
Profesor: Friedrich Guillermo
Universidad Tecnológica Nacional Facultad Regional Bahía Blanca
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

Algoritmo de backtracking recursivo y no recursivo para


la resolución de un laberinto y su aplicación en SDL

1 Resumen
En este proyecto se planteó como problema la resolución de un laberinto mediante un
programa en el cual se apliquen los conocimientos obtenidos en la catedra de
Informática II. Para cumplir este objetivo se tuvo en cuenta hacer énfasis en temas
desarrollados en clase, como el uso de árboles, dispositivos periféricos, programación
orientada a objetes e interfaz gráfica.

Para la resolución del laberinto se desarrolló una función en base al algoritmo de


backtracking, agregándole la construcción de un árbol para mapear y almacenar la
solución del laberinto. Dada la eficacia y simpleza de este algoritmo recursivo, se
optó por desarrollar una función alternativa de backtracking usando iteración en lugar
de recursividad. Se probaron ambas funciones para medir su rendimiento, y se
estudiaron los beneficios de usar una o la otra.

Con el fin de darle un contexto a este problema, se procedió a darle una interfaz
gráfica para poder visualizar y percibir la resolución del laberinto. Se escogió para
este fin, usar las bibliotecas SDL, y para aprovechar estas, se desarrolló un juego entre
la computadora ejecutando los algoritmos estudiados y el o los usuarios usando un
joystick para moverse dentro del laberinto.

Palabas clave: Backtracking, SDL, Recursividad, Laberinto.

2 Introducción
El algoritmo de backtracking es utilizado para resolver problemas en los que la
solución consta de una serie de decisiones adecuadas hasta un objetivo. De esta forma
genera todas las secuenciadas de forma sistemática y organizada hasta encontrar la
correcta. En una primer medida se utilizó un algoritmo de backtracking para generar
un laberinto aleatorio, posteriormente se desarrolló en un algoritmo de backtracking
recursivo para el problema del laberinto planteado y luego se buscó desarrollar el
mismo algoritmo pero de forma no recursiva.

Se modificó el contexto del problema del algoritmo para generar un árbol, que mapea
el laberinto, y con este la solución queda presente en el árbol almacenado y puede ser
usada posteriormente. Además de esto, el mapeo permite procesar el árbol para
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

obtener un único y mejor camino, así como también reproducirlo de forma gráfica
para poder visualizar mejor la solución.

Con los algoritmos funcionando, se usó SDL como interfaz gráfica para una
aplicación de estos, se agregó el atractivo de que el usuario pueda competir contra la
PC para ver quién puede resolver el laberinto más rápido. Si bien la resolución del
laberinto, por cualquiera de los 2 algoritmos, es casi instantánea, gracias a que está
almacenado, se puede ir leyendo y dibujando a una velocidad coherente para competir
con el usuario. Este último mueve su puntero, que representa a un caminante dentro
del laberinto, por medio de un joystick. Usando las posibilidades que nos brinda SDL,
se agregaron atractivos visuales, texto y cronómetros que miden el tiempo de
resolución.

3 Creación del laberinto

Para crear el laberinto se implementó un algoritmo de backtracking recursivo [1], el


cual parte de un arreglo con forma de cuadricula, en donde las celdas individuales
representan parte del camino y la cuadricula representa las paredes. El algoritmo
empieza en un extremo y va saltando de una celda a la vez, abriendo y cerrando
paredes de forma aleatoria, generando un sinuoso camino, cada vez que se llega a un
punto en que no se puede seguir abriendo camino, ya sea porque se está en un
extremo, o porque se alteraría el camino ya dibujado, se vuelve hacia atrás hasta una
nueva celda que permita abrir nuevos caminos. Este algoritmo es muy rápido, pero su
interpretación es demasiado compleja y no es tan fácil de visualizar el proceso de
construcción.

El algoritmo resulta muy efectivo y el resultado es bastante agradable dado que el


camino es muy sinuoso. Entre sus desventajas esta que usa N2 de memoria, solo un
camino llega hasta el final del laberinto. En laberintos amplios empieza a aumentar la
cantidad y el tamaño de zonas muertas (zonas sin recorrer).

4 Representación del laberinto


En el caso estudiado de un laberinto cuadrado es más que evidente que una
representación matricial es acertada. Pero cuando queremos recorrer el laberinto esta
representación no es de utilidad, en base a esto surge la necesidad de ver otra
representación para el análisis del problema. Si observamos con atención la
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

geometría del camino del laberinto y lo sacamos de su estructura podemos ver que
este es análogo a un árbol.

Figura 1 – Representación como árbol de un laberinto.

El algoritmo de backtracking no especifica una forma de representar o almacenar el


camino que se está recorriendo dado que la utilidad de este es encontrar la salida
solamente. Para este caso, se consideró que es importante tener una representación
almacenada del resultado de la función que resuelve el laberinto, dado que esto nos
permitirá recorrer una y otra vez el laberinto sin volver a ejecutar la función. Para ello
se utilizó un árbol multicamino, el cual permite 3 grados de libertad, que van a
permitir movernos en 3 direcciones a partir de la posición en la que estamos parados.
La estructura del nodo del árbol está compuesta por las coordenadas X e Y las cuales
representa el nodo en el laberinto, un puntero al nodo padre el cual permitirá recorrer
el camino de forma inversa, y 3 punteros a los posibles nodos hijos.

En el programa el tipo nodo se define como:


Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

5 Implementación del Algoritmo

5.1 Backtracking recursivo

Este algoritmo es usado en muchos ámbitos de programación, por ejemplo, para el


cálculo de expresiones reguladores o para tareas de reconocimiento de texto y sintaxis
en lenguajes regulares y también como soporte para muchos algoritmos de
inteligencia artificial. El backtracking está relacionada con la búsqueda combinatoria,
donde el orden de los elementos no importa, donde se generan secuencias completas
hasta llegar al objetivo deseado. Podemos decir que forma más exacta que engloba
dos tipos de problemas:

1. Problemas de Decisión: Búsqueda de las soluciones que satisfacen ciertas


restricciones.
2. Problemas de Optimización: Búsqueda de la mejor solución en base a una
función objetivo.

A continuación se cita la definición de este algoritmo:

“Encontrar una solución intentándolo con una de varias opciones. Si la elección es


incorrecta, el computo retrocede o vuelve a comenzad desde el punto de decisión
anterior y lo intenta con otra opción.
Nota: Conceptualmente, un algoritmo de backtracking realiza una búsqueda en
profundidad de un árbol de soluciones posibles (parcial). Cada elección es
un nodo en el árbol.” [2]

Esta definición deja bien en claro que la aplicación de este algoritmo para la
resolución de un problema como el laberinto es más que correcta. En esta
implementación tenemos que recorrer un laberinto, que como ya vimos antes puede
ser análogamente representado como un árbol, en el cual debemos explorar los
caminos y volver hacia atrás cuando nos encontramos sin salida, hasta llegar a un
punto en que podamos tomar un camino alternativo aun no explorado y recorrer este;
este proceso se repite sucesivamente hasta llegar al punto deseado.

Pseudocodigo algoritmo de backtracking:

Backtracking (A[], k){


Si Es_Solucion(A[], k)
Entonces Procesar_Solucion(A[], k)
Sino
Por cada Encontrar_Sucesores(A[], k) hacer
Backtracking(A[], k+1)
Si terminar=true
Entonces return
}
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

Dónde:

k=En el seudocódigo, es el número de elemento del arreglo, en el caso del laberinto


caso va a representar la ubicación en el árbol.
Es_Solucion() retorna verdadero o falso si el argumento es una solución posible, en
nuestro caso solo será verdadero cuando se llegue al fin del laberinto.
Procesar_Solucion(): depende del problema, en este caso sería terminar la búsqueda,
ya que se encontró la solución.
Encontrar_Sucesores(): es una función que dado un candidato, genera todos los
candidatos que son extensiones de este, es decir los caminos que puedo tomar a partir
de esta celda.
terminar: es una variable global booleana inicialmente es falsa, que establece la
condición de terminar la búsqueda dado determinados casos (ej.: Número máximo de
iteraciones).

Algoritmo implementado en el programa:

En los casos planteados se toma como posición inicial las coordenadas (1,1) siendo
esta la que representa al inicio del laberinto, por ende se toma como final del laberinto
a la esquina opuesta, al llegar a esta la función se deja de llamarse recursivamente y
retorna el ultimo nodo.
La función cargar_posibles_caminos es vital para el funcionamiento del programa,
esta se encarga de verificar en qué dirección se puede seguir caminando en base a la
posición actual y al paso anterior que realizamos. Cada vez que una celda vecina esta
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

libre y se puede avanzar hacia ella, esta función asigna memoria y crea un nuevo nodo
en función de los datos actuales y lo almacena en una de las ramas del nodo actual.
Una vez cargados los caminos posibles, cada rama del nodo actual representa un
camino que seguir, entonces se evalúan una a una mediante la misma función de
backtracking.

El hecho que la función retorne el último elemento del árbol, el cual representa el fin
del laberinto hace posible que a partir de este se recorra el árbol de forma inversa y se
pueda construir un nuevo árbol de forma muy simple eliminando los demás nodos que
no pertenecen a la solución.

Figura 2 – Diagrama de método para simplificar el árbol.

5.2 Backtracking iterativo

El principio de funcionamiento del algoritmo de backtracking desarrollado de forma


no recursiva es análogo al backtracking original, pero resulta mucho más complicado
de interpretar y un agregado de más código dentro del programa. El problema
principal resulta en que una vez que llegamos a un punto sin salida no se puede
simplemente retornar la función determinadas veces hasta llegar a un nuevo nodo que
nos permita optar por otro camino alternativo. Sino que una vez que llegamos a esta
situación se debe recorrer el camino hasta llegar a dicho nodo y reconocer que este
nodo y su camino alternativo no fueron anteriormente recorridos. Para esto último
vamos “dibujando” en el arreglo que representa el laberinto (anteriormente
inicializado con 0=PATH/CAMINO o 1=PARED) el camino asignándole a cada
celda recorrida un nuevo estado (2=POINT). El arreglo original que representa el
laberinto va a ser alterado por este nuevo estado POINT que es un nuevo valor en las
celdas del arreglo, pero esto se soluciona con un simple recorrido del arreglo que
reemplace este valor por el original (PATH). Es decir que debemos recurrir a no solo
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

usar el arreglo de laberinto como parámetro para orientarnos y escribir el camino, sino
que también lo debemos usar y modificar para controlar de no volver demasiado hacia
atrás y empezar a recorrer camino ya recorrido.
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

6 Recursividad versus iteración


La razón fundamental es que existen numerosos problemas complejos sin fácil
solución iterativa, y con una naturaleza recursiva, que en consecuencia, son más
fáciles de implementar con algoritmos de este tipo. Tal es el caso del algoritmo de
backtracking de demás alternativas a la resolución de un laberinto que usan
recursividad. Pero también existen casos que el planteo de una solución recursiva
representa un mecanismo muy eficiente de programación que facilitan el diseño de las
mismas funciones.

Sin embargo, en condiciones críticas de tiempo y de memoria, es decir, cuando el


consumo de tiempo y memoria sean decisivos o concluyentes para la resolución del
problema, la solución a elegir debe ser, normalmente la iterativa.

Ventajas de la Recursión:

-Soluciones simples, claras


-Soluciones elegantes.
-Soluciones a problemas complejos.

Desventajas de la Recursión:

-Sobrecarga asociada con las llamadas a funciones, una simple ejecución


puede requerir un gran número de llamadas a funciones. (Ej.: !n conlleva a n
llamadas recursivas, ).
-Mayor uso de memoria

7 Análisis de optimización
Si bien a simple vista los algoritmos implementados pueden tener un grado de
dificultad, a la hora de su ejecución, esta es realmente muy rápida y efectiva. El
backtracking siempre garantiza llegar a la solución, aunque este conlleve recorrer al
100% el laberinto. A la hora de optimizar el algoritmo se busca llegar a la solución
recorriendo lo menos posible, pero esto realmente resulta complicado dada la
naturaleza aleatoria de cada laberinto. Si bien podríamos tomar decisiones en base a la
ubicación actual y la ubicación del objetivo, pero estas podrían tener consecuencias
peores, dado que aunque estemos cerca del objetivo, esto no significa que el camino
que nos quede por recorrer sea corto.
En conclusión no hay posibilidades de tomar decisiones para optimizar el recorrido,
dada la aleatoriedad del problema.
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

8 Aplicación en SDL

8.1 Introducción

Las siglas SDL significan Simple DirectMedia Layer, están desarrolladas en C y


proporcionan funciones básicas para realizar operaciones multimedia en espacios de
dos dimensiones y para ir más allá de estas dimensiones también ofrece una buena
vinculación con OpenGL. También nos ofrece la carga y gestión de sonido, imágenes,
tipografías, archivos y conexiones de red.

SDL fue desarrollada inicialmente por Sam Lantinga, un desarrollador


de videojuegos para la plataforma GNU/Linux, y pese a estar programado en C,
tiene adaptaciones a otros lenguajes de programación
como C++, Ada, C#, BASIC, Erlang, Lua, Java,Python, etc. Una de sus grandes
virtudes es el tratarse de una biblioteca multiplataforma, siendo compatible
oficialmente con los sistemas Microsoft Windows, GNU/Linux,Mac OS y QNX,
además de otras arquitecturas y sistemas como Sega Dreamcast, GP32, GP2X. La
biblioteca se distribuye bajo la licencia LGPL, que es la que ha provocado el gran
avance y evolución de SDL.

En este caso SDL hace uso de los periféricos


(joysticks) simplificando la tarea al
encargarse la misma librería de hacer las
complicadas llamadas instrucciones de
DirectInput[4] (Librería de DirectX),

En cuanto a la creación de la interfaz gráfica,


esta se crea de una forma muy simple con
unas simples llamadas a funciones y es Figura 3 – Diagrama de SDL[3]
independiente del sistema operativo que
usemos.

8.2 Descripción del programa

Al ejecutar el programa, el usuario introduce por teclado el tamaño del laberinto que
desea, en base a esto, se crea una ventana adecuada al tamaño del laberinto solicitado.
Al inicializar los componentes de SDL se lee la cantidad de joystick disponibles en el
ordenador y se toma que este número será igual al número de jugadores. Entonces el
programa crea varios laberintos y situaciones en base a esto y se implementa de la
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

siguiente forma:

Laberinto 1 Laberinto 2 Laberinto 3


Ningún Backtracking NO
Backtracking Recursivo No se muestra
Joystick Recursivo
Backtracking NO
1 Joystick Backtracking Recursivo Jugador 1
Recursivo
2 Joystick Backtracking Recursivo Jugador 1 Jugador 2

Los punteros que representan el caminante del laberinto, son dibujados y son
manipulados por un algoritmo que recorre los arboles creados por los algoritmos de
resolución o por el usuario por medio del joystick. Estos punteros van dejando una
traza de puntos, que indica el camino por el cual ya circularon y además hay un
cronometro por cada laberinto que se detiene cuando se llega a la meta.

8.3 Clases utilizadas

Clase Dot

Es la clase que representa el puntero (el caminante del laberinto), en esta se define
que este va a ser un circulo por medio de la estructura Circle donde se contiene la
maginitud del radio y las coordenadas X e y. Los distintos Dots se diferencian porque
a la hora de su construcción se define a que jugador van a pertenecer, para poder
diferenciarlos.

La funcion handle_input() lee el estado del joystick y en base a esto modifica los
valores de velocidad (xVel , yVel) que luego van a ser leidos por la función move; la
cual va a reubicar teniendo como referencia que ha esta se le pasa como parametro un
vector que representa el laberinto, y en base a esto va a checkear si existen colisiones
entre el puntero y la pared para prohibir que este se suba ensima de la pared o la
atravieze. De forma analoga la función handle_PC, mueve el puntero de forma
automatica al pasarle las coordenadas X e Y a las que queremos que se mueva.
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

Clase Timer

El reloj es un elemento necesario en SDL, en caso el objeto creado se encarga de


regular los fotogramas por segundos, para producir una visualizacion correcta sin
utilizar demas los recursos. Como funcion del programa, se uso este objeto para
controlar los cronometros que miden los tiempos en los que cada laberinto fue
resuelto.
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

Clase Particle

Este es un agregado visual al programa, este objeto es usado por la clase Dot para
dejar una estela de particulas mientras el puntero se mueve. Estas particulas son
ubicadas de forma aleatoria y su vida dura un numero determinado de fotogramas.
Las particulas son archivos de imágenes que se cargaron previamente y se van
mostrando gracias a la librería SDL_image.
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

8.4 Capturas de pantalla

Figura 4 – Captura de pantalla laberinto con n=20

Figura 5 – Captura de pantalla laberinto con n=10

9 Conclusión
En este trabajo se desarrollaron y aplicaron dos funciones de backtracking, que
resultan equivalentes pero de diferente naturaleza. Dado que los laberintos son
relativamente sencillos y pequeños para el ordenador, no se pudo obtener una
comparación de rendimiento real entre estos algoritmos. La utilización del tipo de
árboles que se usó para almacenar el laberinto, resulto ser análoga a la de los arboles
binarios vistos en clase, a pesar de que su geometría es más complicada.

Si bien a la hora de desarrollar la aplicación en SDL se utilizó solo un pequeño


porcentaje del total de esta librería, resulto gratificante la experiencia de aprender
desde cero y resolver los problemas en una nueva interfaz. Resulta una interfaz muy
sencilla para realizar aplicaciones rápidas e interactivas.
Diaz Ariel Ivan
Trabajo Final Informática II
UTN FRBB 2013

10 Referencias
[1] Wikibooks Open books for an open world – “Aros/Developer/Games
Programming/Basics”
<http://en.wikibooks.org/wiki/Aros/Developer/Games_Programming/Basics>

[2] Paul E. Black, "backtracking", in Dictionary of Algorithms and Data


Structures [online], Paul E. Black, ed., U.S. National Institute of Standards and
Technology.
<http://www.nist.gov/dads/HTML/backtrack.html>

[3] Wikipedia the free Encyclopedia, – “Simple MediaDirect Layer”.


<http://en.wikipedia.org/wiki/Simple_DirectMedia_Layer>

[4] Microsoft Develop Center - Desktop, - “Microsoft.DirectX.DirectInput”.


<http://msdn.microsoft.com/en-us/library/windows/desktop/ms805653.aspx>

[5] C. Casas, M. Pérez, J. García, “El esquema algorítmico del backtracking” –


Lógica y computabilidad.
<http://www.uls.edu.sv/index.php?option=com_phocadownload&view=category&download=103:el-esq>

[7] Mg. Oscar Adolfo Vallejos - “Tema VII. Recursividad” FaCENA – UNNER.
<http://exa.unne.edu.ar/informatica/programacion1/public_html/archivos/recursividad_1.pdf>

[8] Lazy Foo' Productions “Beginning Game Programming”


<http://lazyfoo.net/SDL_tutorials/index.php>

[9] Jesús Bermúdez de Andrés, “Diseño de algoritmos Algoritmos de Vuelta Atrás” -


Universidad del País Vasco/Euskal Herriko Unibertsitatea (UPV/EHU)
<http://cvb.ehu.es/open_course_ware/castellano/tecnicas/diseno_alg/contenidos/algoritmos-de-vuelta-
atras.pdf>

[10] Alberto García Serrano, - “Programación de Videojuegos con SDL”


<http://www.albertogarcia.info/libros/sdl/%5Bebook%5DProgramacion%20de%20videojuegos%20con%2
0SDL.pdf>

[11] Javier Martínez Baen, -“Introducción a SDL” Universidad de Granada – Dpto.


Ciencias de la computación e Inteligencia Artificial.

También podría gustarte