Download docx - 8 Puzzle

Transcript
Page 1: 8 Puzzle

JUEGO 8-PUZZLE

Contenido Estudio de la resolución de diferentes configuraciones del 8-puzzle con diferentes heurísticas...................2

8-Puzzle de 6 movimientos........................................................................................................................2

8-Puzzle de 12 movimientos......................................................................................................................3

8-Puzzle de 18 movimientos......................................................................................................................4

Conclusiones de las pruebas de 8-puzzle.......................................................................................................5

ALGORITMO DE PUZZLE EN JAVA...................................................................................................................6

CODIGO DEL 8-PUZZLE EN LISP....................................................................................................................12

Page 2: 8 Puzzle

JUEGO DEL 8-PUZZLE

Estudio de la resolución de diferentes configuraciones del 8-puzzle con diferentes heurísticas

En la implementación del 8-Puzzle hemos creado 4 archivos diferentes en cada uno de los cuales se ha utilizado una heurística diferente:

¾ H1 ó Suma total de fichas descolocadas: heurística minorante fácil de calcular pero con resultados que distan mucho de ser óptimos.

¾ H2 ó Suma de distancias de Manhatan: heurística minorante bastante efectiva. En problemas cortos es la que mejores resultados ha obtenido.

¾ H3 ó Suma de secuencias: heurística no minorante, que en nuestras pruebas no ha demostrado ser excesivamente eficaz.

¾ H=H2+2*H3: heurística no minorante que ha demostrado ser la más eficaz de la cuatro utilizadas, puesto que aunque en tableros de 8-puzzle con una configuración más simple (con menor número de movimientos) se ha visto superada por la H2, en tableros con una complejidad elevada (18 movimientos) ha sido capaz de resolver los problemas un numero mucho más pequeño de nodos.

Para llegar a estas conclusiones nos hemos basado en la resolución de 3 tableros diferentes de 8-puzzle con una complejidad de 6, 12 y 18 movimientos.

En todas ella se pretende llegar a la misma matriz solución:

8-Puzzle de 6 movimientos

La primera matriz utilizada ha sido la siguiente (el 0 representa la casilla vacía):

Page 3: 8 Puzzle

Para resolver esta matriz de forma óptima se deben realizar 6 movimientos, los cuales son descritos a continuación:

Para llegar a este resultado se han tenido que efectuar los 6 movimientos que acabamos de describir y que enumeramos a continuación:

Izquierda, Abajo, Izquierda, Abajo, Derecha, Arriba

Todas las heurísticas utilizadas llegan a una solución optima, pero la diferencia principal radica en le cantidad de recursos que necesitan para resolver el problema.

Vamos a mostrar las principales diferencias obtenidas en la resolución del problema con las diferentes heurísticas:

8-Puzzle de 12 movimientos

Para la prueba de 12 movimientos hemos utilizado la siguiente matriz:

Page 4: 8 Puzzle

Para este ejercicio y para el de 18 movimientos, no vamos a escribir las configuraciones de las matrices en la resolución de este problema pues resultaría muy tedioso, pero si que daremos una descripción optima de los pasos a seguir.

Una solución óptima encontrada para la resolución de esta matriz de forma óptima es:

Arriba, Arriba, Derecha, Abajo, Izquierda, Abajo, Derecha, Arriba, Derecha, Arriba, Izquierda, Abajo.

Si hacemos una comparación entre las diferentes heurísticas podremos comprobar que:

8-Puzzle de 18 movimientos

Para la prueba de 18 movimientos hemos utilizado la siguiente matriz:

Una solución óptima encontrada para la resolución de esta matriz de forma óptima es:

Arriba, Izquierda, Abajo, Derecha, Arriba, Derecha, Abajo, Abajo, Izquierda,

Arriba, Izquierda, Arriba, Derecha, Abajo, Derecha, Arriba, Izquierda, Abajo.

Si hacemos una comparación entre las diferentes heurísticas podremos comprobar que:

Page 5: 8 Puzzle

Conclusiones de las pruebas de 8-puzzle

En problemas de complejidad pequeña (número reducido de movimientos para resolver el problema) la heurística más eficaz es la de Manhattan, ya que es capaz de resolver el problema con menor consumo de memoria (utiliza menos nodos).

A medida que la que la complejidad del problema va aumentando se va comprobando como la utilización de nodos por parte de las diferentes heurísticas va aumentando en grandes cantidades. Así podemos ver que la utilización de nodos resolviendo un problema de 18 movimientos por medio de la heurística de Mahattan asciende a 5421, mientras que con la utilización de una heurística H2+2H3 el numero de nodos se reduce a 4709. Con las otras 2 heurísticas utilizadas el número de nodos utilizados es infinitamente mayor a estas 2 últimas, tanto que es capaz de desbordar la memoria del ordenador impidiéndose así llegar a solución alguna.

De esta forma queda demostrado que la utilización de la heurística H2+2H3 es la más efectiva, no solo por la menor utilización de nodos, sino porque a la larga es la única capaz de resolver problemas de este tipo con una complejidad muy elevada utilizando al hacerlo la menor cantidad de recursos posibles.

Page 6: 8 Puzzle

ALGORITMO DE PUZZLE EN JAVA/*** * Juego del puzzle en el que hay varias piezas y un hueco. */

import java.util.LinkedList;import java.awt.Point;

/** * Juego del puzzle. Mantiene el tablero y permite mover las piezas si el * movimiento es correcto. * Las piezas van de 1 en adelante. El hueco es el 0. */public class Puzzle { public Puzzle (int numeroFilas, int numeroColumnas) { int i, j; int contador = 1; /* Se comprueban los parámetros de entrada */ if (numeroFilas > 0) this.numeroFilas = numeroFilas; if (numeroColumnas > 0) this.numeroColumnas = numeroColumnas; /* Se crea el array que representa el tablero */ tablero = new int [numeroFilas][numeroColumnas]; /* Se ponen las piezas en la posición inicial */ for (i=0; i<numeroFilas; i++) for (j=0; j<numeroColumnas; j++) { tablero [i][j] = contador; contador++; } /* Se situa el hueco en la posición inicial */ tablero [numeroFilas -1][numeroColumnas -1] = HUECO; this.filaHueco = numeroFilas -1; this.columnaHueco = numeroColumnas -1; } /** * Mueve la pieza en fila,columna al hueco. Devuelve true si se ha podido * efectuar el movimiento. false si esa pieza no se puede mover porque no * tiene el hueco al lado o porque se da una fila,columna fuera del * tablero. */ public boolean mueve (int fila, int columna) { /* Se comprueba que las coordenadas caen dentro del tablero */ if (!compruebaCoordenadas(fila, columna)) return false; /* Si no es la primera fila, se mira a ver si el hueco está encima * de la pieza */ if (fila != 0) if (tablero[fila-1][columna] == HUECO) { /* Se hace el movimiento de la pieza a la fila superior */ mueve (fila, columna, fila-1, columna); return true; }

Page 7: 8 Puzzle

/* Si la fila no es la úlitma, se mira a ver si el hueco está en la * fila de abajo de la pieza */ if (fila != (numeroFilas -1)) if (tablero[fila+1][columna] == HUECO) { /* Se hace el movimiento hacia abajo */ mueve (fila, columna, fila+1, columna); return true; } /* Si la columna no es la de la izquierda, se mira a ver si el hueco * está a la izquierda de la pieza */ if (columna != 0) if (tablero[fila][columna-1] == HUECO) { /* Se hace el movimiento hacia la izquierda */ mueve (fila, columna, fila, columna-1); return true; } /* Si la columna no es la de la derecha del todo, se mira a ver si el * hueco está a la derecha de la pieza */ if (columna != (numeroColumnas-1)) if (tablero[fila][columna+1] == HUECO) { /* Se hace el movimiento hacia la derecha */ mueve (fila, columna, fila, columna+1); return true; } /* Si se llega hasta aquí, es que la pieza no tiene el hueco alrdedor y * por tanto no se puede mover */ return false; } /** * Devuelve la pieza que está en la fila, columna indicadas. -1 si * fila,columna está fuera del tablero. */ public int damePieza (int fila, int columna) { /* Si fila,columna no está dentro del tablero, se devuelve -1 */ if (!compruebaCoordenadas (fila, columna)) return -1; /* Se devuelve la pieza en fila,columna */ return tablero[fila][columna]; } /** * Añade el observador que se le pasa a la lista de observadores. */ public void anhadeObservador (ObservadorMovimiento nuevoObservador) { if (nuevoObservador != null) observadores.add (nuevoObservador); } /** * Se elimina el observador que se le pasa de la lista de observadores */ public void quitaObservador (ObservadorMovimiento unObservador) { if (unObservador != null) { observadores.remove (unObservador); }

Page 8: 8 Puzzle

} /** * Devuelve el número de filas del tablero */ public int dameFilas() { return numeroFilas; } /** * Devuelve el número de columnas del tablero */ public int dameColumnas() { return numeroColumnas; }

/** * Devuelve el numero de piezas que se pueden mover. */ public int dameNumeroPosiblesMovimientos() { int numeroPosiblesMovimientos = 4; /* Si el hueco está en la primera fila o en la última fila, hay * un movimiento menos. No se puede mover la pieza desde fuera * del tablero. */ if ((this.filaHueco == 0) || (this.filaHueco == numeroFilas-1)) numeroPosiblesMovimientos--; /* Idem si es primera o última columna */ if ((this.columnaHueco == 0) || (this.columnaHueco == numeroColumnas-1)) numeroPosiblesMovimientos--; return numeroPosiblesMovimientos; } /** * Devuelve un array con las posiciones de las piezas que se pueden mover. */ public Casilla[] damePosiblesMovimientos () { /* Array auxiliar para poner las posiciones de las piezas que se pueden * mover y devolverlo */ Casilla [] aux = new Casilla[dameNumeroPosiblesMovimientos()]; int i=0; /* Si el huecho no esta en la primera fila, se puede bajar la pieza que * está encima del hueco. */ if (this.filaHueco != 0) { aux[i] = new Casilla (this.filaHueco-1, this.columnaHueco); i++; } /* Si el hueco no está en la última fila, se puede subir la pieza que * está debajo del hueco. */ if (this.filaHueco != (numeroFilas -1)) { aux[i] = new Casilla (this.filaHueco+1, this.columnaHueco); i++; } /* si el hueco no está en la primera columna, se puede mover la pieza * que está a la izquierda del hueco. */

Page 9: 8 Puzzle

if (this.columnaHueco != 0) { aux[i] = new Casilla (this.filaHueco, this.columnaHueco-1); i++; } /* Si el hueco no está en la última columna, se puede mover la pieza * que está a la derecha del hueco */ if (this.columnaHueco != (numeroColumnas -1)) { aux[i] = new Casilla (this.filaHueco, this.columnaHueco+1); i++; } return aux; } /** * Devuelve true si el puzzle está ordenado. false en caso contrario. * * Se inicializa un contador con el valor de pieza 1, que debe estar en * la posición 1,1. Se incrementa el contador y se pasa a la siguiente * casilla. Comparando el contador con el contenido de la casilla, se sabe * si el puzzle está ordenado. */ public boolean estaOrdenado() { /* Para recorrer el tablero */ int fila, columna; /* Para ver la pieza que toca en cada casilla */ int contador = 1; /* Bucle doble para cada fila y columna, recorriendo así todo el * tablero */ for (fila=0; fila < numeroFilas; fila++) for (columna=0; columna < numeroColumnas; columna++) { /* Tratamiento especial para el hueco en la última posición * del tablero. Si hemos llegado hasta la última posición del * tablero, todas las demás piezas están en su sitio. La última * posición estará ocupada por el hueco (el 0) y no por la pieza * número filas*columnas, que no existe. */ if (contador == numeroFilas*numeroColumnas) return true; /* Si la pieza no es la que debe, se devuelve false */ if (tablero[fila][columna] != contador) return false; /* Se pone en contador el valor de la siguiente pieza del * del puzzle */ contador++; } /* Si se llega hasta aquí, el puzzle está ordenado */ return true; } /** * Mueve una pieza al hueco. Presupone que los parámetros que se le pasan * son correctos y no los verifica. * Notifica del movimiento a los observadores de movimiento de piezas. * Verifica si el puzzle ya está ordenado para avisar a los observadores. */ private void mueve (int fila, int columna, int filaHueco, int columnaHueco) { /* Se realiza el movimiento */

Page 10: 8 Puzzle

tablero [filaHueco][columnaHueco] = tablero[fila][columna]; tablero [fila][columna] = HUECO; /* Se actuliazan las variables que mantienen la posición actual del * hueco */ this.filaHueco=fila; this.columnaHueco=columna; /* Notifica del movimiento a los suscriptores de movimientos de pieza */ notificaMovimiento (fila, columna, filaHueco, columnaHueco); /* Verifica si está ordenado para notificar a los suscriptores de * tablero ordenado */ if (estaOrdenado()) notificaOrdenado(); } /** * Notifica a los suscriptores el movimiento de una pieza. */ private void notificaMovimiento ( int filaVieja, int columnaVieja, int filaNueva, int columnaNueva) { int i; /* Variable auxiliar para hacer el cast más cómodo */ ObservadorMovimiento aux; /* Bucle para cada observador */ for (i=0; i<observadores.size(); i++) { aux =(ObservadorMovimiento)observadores.get(i); /* Se notifica el movimiento */ aux.tomaMovimiento ( filaVieja, columnaVieja, filaNueva, columnaNueva); } } /** * Notifica a los observadores que el puzzle está ordenado */ private void notificaOrdenado() { int i; /* Variable auxiliar para hacer más cómodo el cast */ ObservadorMovimiento aux; /* Bucle para cada observador */ for (i=0; i<observadores.size(); i++) { aux =(ObservadorMovimiento)observadores.get(i); /* Se notifica que está ordenado */ aux.ordenado(); } } /** * Devuelve true si la fila,columna cae dentro del tablero. false en * caso contario */ private boolean compruebaCoordenadas (int fila, int columna) { // Si la fila, columna no es del tablero, se devuelve false y no se // hace nada. if ( (fila < 0) || (fila >= numeroFilas) || (columna < 0) || (columna >=numeroColumnas))

Page 11: 8 Puzzle

return false; return true; } /** Número de filas del puzzle. Por defecto 3 */ private int numeroFilas = 3; /** Número de columnas del puzzle. Por defecto 3 */ private int numeroColumnas = 3; /** Array bidimensional que representa el tablero. Las piezas serán enteros * 1, 2, 3, etc. El Hueco es el 0 */ private int [][] tablero; /* Fila en la que está el hueco */ private int filaHueco; /* Columna en la que está el hueco */ private int columnaHueco; /** Entero que representa el hueco en el puzzle */ static public final int HUECO=0; /** Lista de observadores */ private LinkedList observadores = new LinkedList();

}

Page 12: 8 Puzzle

CODIGO DEL 8-PUZZLE EN LISP

(defparameter *goal-state*   (make-array '(3 3)               :initial-contents '((1 2 3)(4 5 6)(7 8 space))))

(defun copy-board(board)   (let ((new-board (make-array '(3 3))))     (loop for i from 0 to 2         do (loop for j from 0 to 2                  do (setf (aref new-board i j)                           (aref board i j) )))     new-board))

(defun print-board(board)   (format t "~%-------")   (loop for i from 0 to 2         do (format t "~%|")         (loop for j from 0 to 2               do (format t "~A|" (if (eq (aref board i j) 'space)                                    " "                                    (aref board i j))))         (format t "~%-------")))

(defun find-square(x board) "return a list with the x y coordinates of the piece x in board"   (loop for i from 0 to 2       thereis (loop for j from 0 to 2                   thereis                     (when (eq (aref board i j) x)                       (list i j)))))

(defun move-up(state)   (let* ((at-space (find-square 'space state))          (i (first at-space))          (j (second at-space))          (new-state (copy-board state)))     (when (> i 0)       (setf (aref new-state i j) (aref new-state (- i 1) j))       (setf (aref new-state (- i 1) j) 'space)       new-state)))

Page 13: 8 Puzzle

(defun move-down(state)   (let* ((at-space (find-square 'space state))          (i (first at-space))          (j (second at-space))          (new-state (copy-board state)))     (when (< i 2)       (setf (aref new-state i j) (aref new-state (+ i 1) j))       (setf (aref new-state (+ i 1) j) 'space)       new-state)))

(defun move-left(state)   (let* ((at-space (find-square 'space state))          (i (first at-space))          (j (second at-space))          (new-state (copy-board state)))     (when (> j 0)       (setf (aref new-state i j) (aref new-state  i (- j 1)))       (setf (aref new-state  i (- j 1)) 'space)       new-state)))

(defun move-right(state)   (let* ((at-space (find-square 'space state))          (i (first at-space))          (j (second at-space))          (new-state (copy-board state)))     (when (< j 2)       (setf (aref new-state i j) (aref new-state  i (+ j 1)))       (setf (aref new-state  i (+ j 1)) 'space)       new-state)))    

(defun random-move(state)   "randomly pick one of 4 operators. If it returns nil, choose again"   (let ((r (random 4)))     (or (cond ((= r 0)(move-left state))         ((= r 1) (move-right state))         ((= r 2) (move-up state))         ((= r 3) (move-down state)))       (random-move state))))

Page 14: 8 Puzzle

(defun random-moves (n state)   "make N random moves"   (loop for i from 1 to n         do (setq state (random-move state)))   state)

(defparameter *start-state*   (random-moves 20 *goal-state*))

 (defun solution-state?(state)   "A state description is the solution if it matches the goal state"   (equalp state *goal-state*))

 (defparameter *eight-puzzle-operators*   '(move-up move-down move-left move-right))

 (defun estimated-distance-from-goal (board) "Compute Manhattan distance for each tile (except space)"   (loop for i from 1 to 8         summing (manhattan-distance (find-square i board)                                     (find-square i *goal-state*))))

(defun manhattan-distance (p1 p2) "given two lists of x-y coords, sum the difference between x's and y's"   (+ (abs (- (first p1) (first p2)))      (abs (- (second p1) (second p2)))))

(defun cost-of-applying-operator (state operator)   1)