Está en la página 1de 19

Trabajo Practico

Especial
ALGORITMOS 2
EZEQUIEL DESAEGER JOHNNY BRYAN COTRINA ILLESCAS

Eze_07_yo@hotmail.com - bryan03121@gmail.com
Grupo 46
Menchn, Martn

Ayudante :

PRESENTACIN

El objetivo del trabajo es implementar diversos algoritmos de bsqueda que permitan


resolver los laberintos propuestos por el juego Mummy Maze. El juego est compuesto
de una serie de escenarios a travs de los cuales se debe guiar al explorador hasta la
salida de cada uno de ellos. Cada escenario contiene paredes que constituyen
obstculos para el movimiento, as como enemigos (momias y escorpiones) que
intentaran evitar que el explorador llegue a la salida del laberinto.
Para orientar la bsqueda de la solucin, implementaremos algoritmos de bsqueda
heurstica, ya que su objetivo es reducir el tiempo de proceso al orientar la exploracin
de los estados.
La finalidad del trabajo es buscar interiorizarnos con el funcionamiento de las
bsquedas por fuerza bruta y egosta, as como su implementacin.
Explicado el objetivo del trabajo, procedemos con el cdigo del programa.

2 ALGORITMO DFS
ES un algoritmo empleado para recorrer nodos (en nuestro caso estados de juego)
expandiendo por niveles (en nuestro caso los niveles corresponden a las acciones
posibles del explorador). El algoritmo avanza recursivamente por una rama hasta
que llega a un estado donde pierde el juego y regresa hacia atrs; o si llega a un
estado ganador y se termina.
La principal desventaja que tiene son los largos caminos que prueba por fuerza
bruta hasta que aleatoriamente llega a la salida. Aclaramos que todos los
escenarios dados tienen solucin.
Si bien prueba aleatoriamente, podramos decir que hace un buen uso de memoria
respecto al bfs cuando los escenarios son muy grandes, ya que las soluciones no
son de tanta calidad como en este ltimo pero las encuentra probando menos
caminos.
La forma ms intuitiva de hacer este algoritmo es de forma recursiva, porque tiene
que avanzar por una rama y si pierde retrocede y sigue con el siguiente estado hijo.
Grficamente trabaja as:
Dado un rbol

1
2
4

3
5

Hace una bsqueda por nivel de la siguiente manera:

IMPLEMENTACIN:
bool dfs (GameSimulator & sim, unsigned int & depth, list<Action::Type> & solution)
{
if (sim.isGameOver()) {
if (sim.getGameOverResult() == GameOverRule::WIN) {
cout << "--- Se gano el game\n\n";
cout << "la cantidad total de estados visitado fue: " << total_visitados<< endl;
total_visitados =0;
return true;
} else {
cout << "--- Se perdio el game\n\n";
return false;
}
} else if (solution.size() ==depth) {
cout << "--- PODA: se alcanzo la longitud maxima de la solucion\n\n";
return false;
} else {
const GameState * currentState = sim.getCurrentState();
//if (sim.getVisited(currentState) == 0) {
if (!sim.isInPath()) {
cout << "--- Se agrega el estado nuevo\n\n";
//sim.addVisited(currentState);
cout << "---- Detalle de la configuracion del estado actual:\n";
cout << currentState->toString() << "\n";
ComponentPtr<LogicObject> explorer(sim.getGame()->getObject(EXPLORER));
list<Action::Type> explorerActions;
explorer->getActions(explorerActions);
bool found = false;
list<Action::Type>::iterator action = explorerActions.begin();
while (!found && (action != explorerActions.end())) {
if (explorer->canPerform(*action)) {
cout << "--- Se ejecutara la accion: " << action->toString() << "\n\n";
solution.push_back(*action);
explorer->performAction(*action);
total_visitados++;
sim.update();
cout << "--- Se actualizo el estado\n\n";
found = dfs(sim, depth, solution);
if (!found) {
cout << "--- Se vuelve al estado anterior\n\n";
solution.pop_back();
sim.undoUpdate();
}
}
action++;
}

return found;
} else {
cout << "--- El estado ya fue visitado\n\n";
return false;
}
}
}

3 ALGORITMO BFS
Es otro algoritmo de bsqueda por fuerza bruta, pero este trabaja haciendo una
bsqueda en anchura. O sea agarra un estado de juego y explora todos sus estados
vecinos. Despus para cada uno de ellos se fija todos los hijos y explora todos los
vecinos. Y as hasta que encuentra un estado ganador. Cuando encuentra un estado
perdedor no sigue explorando esa rama.
La principal desventaja de usar bfs es la cantidad de memoria que consume, dado
que al hacer por niveles no explora una rama, sino como mximo 5 ( las 5 acciones
posibles del explorador). Adems mnimamente, la lista de estados por expandir
debe ser almacenada durante la bsqueda de la solucin.
Encuentra la solucin en pocos pasos, a costa de muchos estados visitados.
Grficamente funciona as:
Dado un rbol

1
2
4

3
5

Hace una bsqueda por nivel de la siguiente manera:

IMPLEMENTACIN
bool bfs(GameSimulator & sim, unsigned int & depth, list<Action::Type> & solution)
//El recorrido del espacio de estados se hace por niveles de profundidad
{
int total_visitados = 0;
queue<estados_visitados*> cola_estados;
estados_visitados * estado_actual = new estados_visitados;
estados_visitados * estado_padre;
estado_actual->estado = sim.getCurrentState();
estado_actual->anterior = 0;
cola_estados.push(estado_actual);
estado_padre = cola_estados.front();
while ( !cola_estados.empty() ){
cola_estados.pop();
if (sim.isGameOver()) {
if (sim.getGameOverResult() == GameOverRule::WIN) {
while( estado_padre->anterior != 0 ){
solution.push_front(estado_padre->accion);
estado_padre = estado_padre->anterior;
}
cout << "El numero total de estados visitados es: "<< total_visitados << endl;
return true;
} else {
cout << "Lo mata la momia" << endl;
estado_padre = cola_estados.front();
sim.setState(estado_padre->estado);
}
}
else if (solution.size() == depth) {
cout << "--- PODA: se alcanzo la longitud maxima de la solucion\n\n";
return false;
}
else {
if (sim.getVisited(estado_padre->estado) == 0){
sim.addVisited(estado_padre->estado);
cout << "---- Detalle de la configuracion del estado actual:\n";
cout << estado_padre->estado->toString() << "\n";
ComponentPtr<LogicObject> explorer(sim.getGame()->getObject(EXPLORER));
list<Action::Type> explorerActions;
explorer->getActions(explorerActions);
list<Action::Type>::iterator it = explorerActions.begin();
while (it != explorerActions.end()) {
if (explorer->canPerform(*it)) {
explorer->performAction(*it);
sim.update();
total_visitados++;
estado_actual = new estados_visitados;
cout << "Agrega a la cola: " << it->toString() << endl;
estado_actual->estado = sim.getCurrentState();
estado_actual->accion = *it;

estado_actual->anterior = estado_padre;
cola_estados.push(estado_actual);
sim.undoUpdate();
}
it++;

}
}
estado_padre = cola_estados.front();
sim.setState(estado_padre->estado);
cout << "---- Se ejecutara la accion: " << estado_padre->accion.toString() << endl;

}
}
return false;

ALGORITMO

A*

El algoritmo A* es usualmente utilizado en los problemas para encontrar el mejor


camino. Lo que realiza el algoritmo es construir todos los caminos desde un punto
inicial hasta encontrar alguno que llegue al nodo final o meta.

Bsicamente funciona como un bfs recorriendo en anchura, pero se le da una


especie de consejo o gua para orientar un poco la bsqueda por fuerza bruta,
el cual est dado por el valor de la heurstica que se le suma.
Se puede expresar matemticamente como F(n) = g(n) + h(n), donde h(n) es la
funcin matemtica de la heurstica y g(n) es el coste exacto del camino desde el
estado inicial al nodo actual.
As como el bfs , la cantidad de memoria que utiliza es alta y necesita mnimamente
almacenar la lista de estados por expandir hasta que encuentra una solucin.
Grficamente funciona as:
Dado un rbol

1
2

Hace una bsqueda por nivel de la siguiente manera:

IMPLEMENTACIN:
bool astar(GameSimulator & sim, const Heuristic & h, list<Action::Type> & solution)

//me devuelve el camino desde un punto inicial hasta encontrar alguno que llegue al nodo
final o meta
{
int distancia_primero;
bool agregar;
int total_visitados = 0;
list<estados_astar*> lista_primera;
list<estados_astar*> lista_segunda;
list<estados_astar*>::iterator pos_lista_primera,pos_lista_segunda;
estados_astar * nodo_padre = new estados_astar;
estados_astar * nodo_actual;
const GameState * estado_actual = sim.getCurrentState();
nodo_padre->estado = estado_actual;
nodo_padre->anterior = 0;
nodo_padre->distancia_inicial = 0;
nodo_padre->aprox.H_n = h.eval(sim.getGame());
nodo_padre->aprox.G_n = 0;
nodo_padre->aprox.F_n = nodo_padre->aprox.H_n + nodo_padre->aprox.G_n;
lista_primera.push_front(nodo_padre);
while ( !lista_primera.empty() ){
nodo_padre = lista_primera.front();
insertar_ordenado(lista_segunda,nodo_padre);
lista_primera.pop_front();
sim.setState(nodo_padre->estado);
if (sim.isGameOver()) {
if (sim.getGameOverResult() == GameOverRule::WIN) {
while( nodo_padre->anterior != 0 ){
solution.push_front(nodo_padre->accion);
cout << nodo_padre->aprox.F_n << endl;
nodo_padre = nodo_padre->anterior;
}
cout << "El numero total de estados visitados es: "<< total_visitados << endl;
return true;
}
}
else if (solution.size() ==depth) {
cout << "---- PODA: se alcanzo la longitud maxima de la solucion\n\n";
return false;
}
else {
if ( sim.getVisited(nodo_padre->estado) == 0 ){
sim.addVisited(nodo_padre->estado);
cout << "---- Detalle de la configuracion del estado actual:\n";
cout << nodo_padre->estado->toString() << "\n";
ComponentPtr<LogicObject> explorer(sim.getGame()->getObject(EXPLORER));
list<Action::Type> explorerActions;
explorer->getActions(explorerActions);
list<Action::Type>::iterator it = explorerActions.begin();
while( it != explorerActions.end()){
if (explorer->canPerform(*it)){
explorer->performAction(*it);
sim.update();
total_visitados++;

estado_actual = sim.getCurrentState();
distancia_primero = nodo_padre->distancia_inicial + 1;
agregar = false;
int num = 1;
pos_lista_primera = posicion(lista_primera,estado_actual,num);
cout <<" "<< endl;
if (( num != '0') &&((*pos_lista_primera)->distancia_inicial >
distancia_primero)){
cout << "MEJORA EL estado DE lista_primera" << endl;
cout << "Tamano antes de borrar: "<< lista_primera.size() << endl;
lista_primera.remove(*pos_lista_primera);
cout << "Tamano despues de borrar: " << lista_primera.size() <<
endl;
agregar = true;
}
else{
pos_lista_segunda = posicion(lista_segunda,estado_actual,num);
if (( num != 0 ) && ((*pos_lista_segunda)->distancia_inicial >
distancia_primero)){
cout << "MEJORA EL estado DE lista_segunda" << endl;
cout << "Tamano antes de borrar: "<< lista_segunda.size() << endl;
lista_segunda.remove(*pos_lista_segunda);
cout << "Tamano despues de borrar: " << lista_segunda.size() << endl;
agregar = true;
}
else
agregar = true;
}

>aprox.G_n ;

if ( agregar ){
nodo_actual = new estados_astar;
nodo_actual->estado = estado_actual;
nodo_actual->accion = *it;
cout << "Agrega la accion: " << it->toString() << endl;
nodo_actual->anterior = nodo_padre;
nodo_actual->distancia_inicial = distancia_primero;
nodo_actual->aprox.H_n = h.eval(sim.getGame());
nodo_actual->aprox.G_n = distancia_primero;
nodo_actual->aprox.F_n =h.eval(sim.getGame()) + nodo_actualcout << "Distancia estimada: "<< nodo_actual->aprox.G_n << endl;
insertar_ordenado(lista_primera,nodo_actual);
}
sim.undoUpdate();

}
it++;
}

}
}
return false;
ESTRUCTURAS IMPLEMENTADAS:

Estructura estados_astar: Esta fue utilizada en el A* para facilitar el almacenamiento de


estados generados, para el caso del dato aprox fue necesario crear una estructura
aparte para poder guardar el costo F(n).
typedef struct estados_estrella{
const GameState * estado;
estados_estrella * anterior;
Action::Type accion;
float distancia_inicial;
struct datos aprox;
} estados_astar;

struct datos{
float H_n;
float G_n;
float F_n;
};

Estrucutura estados_visitados: Utilizada en el algoritmo BFS, contiene todos los campos que
nosostros pensamos que son necesarios para que el BFS funcione de forma eficiente
typedef struct vistados{
const GameState * estado;
Action::Type accion;
vistados * anterior;
} estados_visitados;

5 GRAFICAS OBTENIDAS

Tabla con la cantidad total de estados visitados por lo algoritmos en cada escenario.
(ESCENARIO/ALGORIT
MO)
4
6
8
10
13
15

DFS

BFS

247
157
579
422
153
116

102
292
293
991
35
294

HEURISTI
CA 1
69
247
263
459
17
261

HEURISTI
CA 2
68
262
271
542
20
265

HEURISTI
CA 3
65
253
263
462
17
259

HEURISTI
CA 4
74
196
271
558
16
272

Tabla comparativa entre los algortimos de busqueda.


DFS

BFS

HEURISTICA 1

HEURISTICA 2

HEURISTICA 3

HEURISTICA 4

991

579

542 558

422

247

292
262
253
247

459 462

294
272
265
261
259

293
271
271
263
263

196
157

153

102
6968 6574

E S C E N A RI O 4

35
E S C E N A RI O 6

E S C E N ARIO 8

E S C E N A RI O 1 0

116
17 2017 16

E S C E N A RI O 1 3

E S C E N ARIO 1 5

Promedio de estados visitados


400

350

334.5

300
279
238

250

219.8

219.3

231.2

200

150

100

50

0
DFS

BFS

6 HEURSTICAS

HEURISTICA 1

HEURISTICA 2

HEURISTICA 3

HEURISTICA 4

Dado que el objetivo del juego es encontrar un camino seguro para el explorador
hacia la salida, y como los algoritmos de DFS y BFS son de fuerza bruta, entonces lo
ideal sera orientarlos mediante algunas reglas para mejorar los costos y reducir
caminos que haran que nunca llegue.
Para esto se emplean 3 heursticas que sirven como una estimacin del coste del
camino ms econmico de un nodo dado hasta el nodo objetivo.
Las heursticas se usan en algoritmos de bsqueda egosta buscando el nodo con el
valor ms bajo, luego el A* expandir esos nodos para g(n) + h(n), donde h(n) es
la funcin matemtica de la heurstica y g(n) es el coste exacto del camino desde
el estado inicial al nodo actual.
Cuando h(n) es admisible, o sea que nunca sobrestima los costes de encontrar el
objetivo, A* es probablemente un camino ptimo. Aun as, aunque normalmente
encuentran buenas soluciones, a menudo pueden encontrarse instancias concretas
del problema donde la heurstica producir resultados muy malos o se ejecutar
muy lentamente, pero estas instancias concretas pueden ser ignoradas porque no
deberan ocurrir nunca en la prctica por ser de origen terico.
La primera heurstica se basa en las paredes adyacentes al explorador. Establece un
rectngulo alrededor de l, y se fija si hay una pared a su lado. Cada pared aumenta
el valor del camino, por lo tanto elije los que se encuentran sin obstculos. La idea
es que el explorador trate de evitar chocar con una pared para llegar ms rpido a
la salida.
La idea se tom en base a una sugerencia dada por el profesor de tener en cuenta
los objetos que rodean al explorador en un rectngulo centrado en l.
Para la segunda heurstica, se pens en que si la primera se fijaba las paredes que
haba cerca del explorador para intentar evitarlas, tambin servira hacerlo con la
momia, pero de forma opuesta, buscar los caminos que ms la encierran.
Bsicamente es casi el mismo cdigo que la anterior, pero enfocndose en la
momia.
En la tercer heurstica, se busc combinar las 2 primeras para mejorar todava ms
la calidad de la solucin, donde el explorador buscara un camino que evite las
paredes y, a su, trate de bloquear a la momia. Esta la consideramos la heurstica
ms segura. Aunque en escenarios pequeos no se note.
Para la cuarta heurstica (que se nos pidi agregar), la hiptesis que planteamos fue
que haba escenarios con un solo enemigo y otros con mltiples. Nos enfocamos en
este ltimo caso, y pensamos que deba haber alguna heurstica que fuera ms
eficiente dada una mayor cantidad de enemigos. La idea es que evala la distancia
al enemigo ms prximo y las paredes adyacentes. En escenarios de enemigo
nico, no es la ms recomendada.

ANLISIS DE RESULTADOS
ESCENARIO 4:

El algoritmo BFS visito menos de la mitad de estados que el DFS.


Las heursticas mejoraron mucho la eleccin del camino respecto al BFS,
aproximadamente cerca del 30% menos de estados visitados.
ESCENARIO 6:
El algoritmo DFS fue el que menos estados visito, encontr una solucin aleatoria,
rpidamente.
El BFS prcticamente duplica la cantidad de estados que se visitaron respecto al
DFS por lo que fue mucho ms til en este caso
Las heursticas no mejoraron mucho los costos, a excepcin de la cuarta que reduce
la cantidad de estados que se visit en aproximadamente 30%, esto se debe a que
fue pensada para escenarios con mltiples enemigos.
ESCENARIO 8:
Podemos apreciar que realizando la bsqueda con el BFS se reducen
aproximadamente un 50% del costo respecto al DFS.
Las heursticas para este escenario no fueron muy eficientes como se puede
apreciar en el grfico.
ESCENARIO 10:
Este fue el escenario que ms nos llam la atencin, por el altsimo costo que
genero el BFS, este costo sobrepaso en un 100% la cantidad de estados que genero
el algoritmo DFS.
Las heursticas para este caso fueron eficientes.
ESCENARIO 13:
Para este escenario tenemos que tener en cuenta que la recursin con la que
trabaja el DFS no considera que se gana el juego solo con llegar a la salida, sino,
cuando al haber llegado a la salida y terminado el turno, el explorador se encuentre
a salvo, esto quiere decir que antes de ganar el juego simula el turno del enemigo.
Por este motivo no elige un camino directo, lo que hace que tenga que generar y
visitar ms estados y debido a este tiene mucho ms costo que el BFS, el cual
realiza el camino directo.
Las heursticas tambin fueron eficientes reduciendo los costos prcticamente a la
mitad respecto al BFS.
ESCENARIO 15:

Para este ltimo escenario el DFS tuvo un costo mucho menor que el BFS, mientras
que las heursticas no optimizaron demasiado los costos.

COMPORTAMIENTO DEL EXPLORADOR

Segn la primera heurstica, nunca elije quedarse quieto si tiene una pared cerca, y
nunca va a quedar encerrado. No significa que opte por el camino que tiene menos
paredes o que no se mueva hacia una de ellas. Lo que hace es no encerrarse y
quedarse quieto.
El comportamiento justifica la hiptesis de la evaluacin, nunca lo va a matar la
momia por encontrarlo sin escape. Es un camino seguro el que elije siempre.
Segn la segunda heurstica, busca encerrar a la momia para moverse con ms
libertad. Segn se ve, lo logra hacer siempre, por ms que tenga un camino ms
rpido opta por quitarle movilidad al enemigo.
El comportamiento justifica la hiptesis de la evaluacin, siempre la atora en alguna
parte.
Segn la tercera heurstica, y la ms segura de las tres, el explorador trata de
seguir un camino donde nunca se queden 2 turnos seguidos al lado de la misma
pared, y a su vez le quita movilidad a la momia. Lo que lo hace un camino seguro,
pero no as el ms rpido para llegar a la meta.
El comportamiento justifica la hiptesis de la evaluacin, aunque a veces es
innecesario que intente atorarla o que no este 2 veces seguidas contra la misma
pared, porque con una sola de las dos opciones puede alcanzar la salida .sin
embargo es el camino ms seguro de todos.
Nos sorprendi el hecho que el DFS fuera en promedio ms eficiente que el BFS,
pero esto sucede porque en el escenario

CONCLUSIONES

El uso de clases en las heursticas facilitan el nivel de abstraccin al momento de


asociarlas con algoritmo A*, evitando repetir cdigo.
Al momento de probar las heursticas, observamos que no influan mucho en la
decisiones que tomaba el A*, la mejora era ineficiente ya que no haba mucha
diferencia entre los costos respecto al BFS. Por este motivo tratamos de aumentar la
importancia, incrementado su valor y fuimos probando nmeros hasta que llegamos
a los valores que se encuentran asignados a cada una de ellas, y este cambio
redujo hasta en un 50% la cantidad de estados visitados en los mejores casos, como
por ejemplo podemos observar el numero resaltado en la tabla de costos.
Entender el trabajo fue algo complicado para nosotros, porque nos cost adaptarnos
a la plataforma que nos brind la catedra al igual que familiarizarnos con los
mtodos que debamos usar. El hecho de que ya estuviera implementado de gran
ayuda para poder saber cmo invocar al explorador, avanzar la simulacin,
almacenar estados, etc.
La implementacin que ms nos cost fue el A*, para eso tuvimos que crear
estructuras especiales, las cuales van a servir para el almacenamiento de estados.
La parte ms difcil del trabajo fue pensarlo, y entender el concepto de cmo
debera de funcionar las bsquedas, cuando la llegamos a comprender, el resto fue
solo implementar cdigo, el cual no nos llev mucho tiempo.
Nos sorprendi el hecho que el DFS fuera en promedio ms eficiente que el BFS,
pero esto sucede porque en el escenario 10 la cantidad de estados visitados por el
BFS fue muy alta.

Al corregir los errores que tenamos en un principio, realmente nos dimos cuenta
todo lo que habamos hecho mal, como el tomar costo la cantidad de pasos que
realiza el explorador a la salida. Otro grave error cometido fue al momento de
insertar ordenado en el A*, el cual debe ser de menor a mayor de acuerdo al F(n).