JPuzzle

Matriz.java
    1 /*
    2  * This file is part of JPuzzle.
    3  *
    4  * JPuzzle is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation, either version 3 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * JPuzzle is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with JPuzzle.  If not, see <http://www.gnu.org/licenses/>.
   16  *
   17  * JPuzzle Copyright © 2008 HackmanGT
   18  * HackmanGT@hotmail.com
   19  */
   20 
   21 package JPuzzle.Graphics;
   22 
   23 import java.awt.image.BufferedImage;
   24 import java.io.IOException;
   25 import javax.imageio.ImageIO;
   26 
   27 /**
   28  * La matriz se encarga de cargar una imagen almacenada en el jar, y devolver
   29  * diferentes piezas dependiendo de la celda.
   30  * @author HackmanGT
   31  */
   32 public class Matriz {
   33 
   34     private BufferedImage bi = null;
   35 
   36     /**
   37      * Intenta cargar una imagen del archivo de recursos.
   38      * @throws java.io.IOException si ocurrió un error al cargar la imagen
   39      */
   40     public void loadImage() throws IOException {
   41         int r = (int) (Math.random() * 3) + 1;
   42         bi = ImageIO.read(getClass().getResource(
   43                 "/JPuzzle/Graphics/JPuzzle" + r + ".gif"));
   44     }
   45 
   46     /**
   47      * Devuelve una pieza de la imagen en función de los parámetros.
   48      * @param x la posición horizontal en coordenadas del tablero
   49      * @param y la posición vertical en coordenadas del tablero
   50      * @param w el ancho en coordenadas del tablero
   51      * @param h el alto en coordenadas del tablero
   52      * @return una pieza de la imagen original
   53      */
   54     public BufferedImage getTile(int x, int y, int w, int h) {
   55         if (bi != null) {
   56             int cellWidth = bi.getWidth(null) / w;
   57             int cellHeight = bi.getHeight(null) / h;
   58             return bi.getSubimage(
   59                     x * cellWidth,
   60                     y * cellHeight,
   61                     cellWidth,
   62                     cellHeight);
   63         }
   64         return null;
   65     }
   66 }
Celda.java
    1 /*
    2  * This file is part of JPuzzle.
    3  *
    4  * JPuzzle is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation, either version 3 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * JPuzzle is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with JPuzzle.  If not, see <http://www.gnu.org/licenses/>.
   16  *
   17  * JPuzzle Copyright © 2008 HackmanGT
   18  * HackmanGT@hotmail.com
   19  */
   20 
   21 package JPuzzle.Utilities;
   22 
   23 import java.awt.image.BufferedImage;
   24 
   25 /**
   26  * Las celdas son las unidades en que se divide el tablero de juego, contienen
   27  * el número ordinal que representan en el orden natural del tablero y
   28  * posiblemente una parte de la imagen.
   29  * @author HackmanGT
   30  */
   31 public class Celda {
   32 
   33     private int valor;
   34     private BufferedImage imagen = null;
   35 
   36     /**
   37      * Construye una nueva celda con el valor por defecto 0 y una imagen null.
   38      */
   39     public Celda() {
   40         initComponents(0);
   41     }
   42 
   43     /**
   44      * Construye una nueva celda con el número indicado en el parámetro valor y
   45      * una imagen null.
   46      * @param valor el valor inicial de la celda
   47      */
   48     public Celda(int valor) {
   49         initComponents(valor);
   50     }
   51 
   52     /**
   53      * Inicializa el componente.
   54      * @param valor el valor inicial de la celda
   55      */
   56     private void initComponents(int valor) {
   57         this.setValor(valor);
   58     }
   59 
   60     /**
   61      * Devuelve el valor de la celda.
   62      * @return el valor de la celda
   63      */
   64     public int getValor() {
   65         return valor;
   66     }
   67 
   68     /**
   69      * Asigna un valor a la celda.
   70      * @param valor el valor de la celda
   71      */
   72     public void setValor(int valor) {
   73         this.valor = valor;
   74     }
   75 
   76     /**
   77      * Devuelve la imagen de la celda.
   78      * @return la imagen de la celda
   79      */
   80     public BufferedImage getImagen() {
   81         return imagen;
   82     }
   83 
   84     /**
   85      * Asigna una imagen a la celda.
   86      * @param imagen la imagen de la celda
   87      */
   88     public void setImagen(BufferedImage imagen) {
   89         this.imagen = imagen;
   90     }
   91 }
Posicion.java
    1 /*
    2  * This file is part of JPuzzle.
    3  *
    4  * JPuzzle is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation, either version 3 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * JPuzzle is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with JPuzzle.  If not, see <http://www.gnu.org/licenses/>.
   16  *
   17  * JPuzzle Copyright © 2008 HackmanGT
   18  * HackmanGT@hotmail.com
   19  */
   20 
   21 package JPuzzle.Utilities;
   22 
   23 /**
   24  * Las posiciones representan un par de coordenadas, sirven para almacenar,
   25  * comparar y realizar operaciones matemáticas (transformaciones) con
   26  * direcciones y coordenadas. Ver <b>java.awt.Point</b>
   27  * @author HackmanGT
   28  */
   29 public class Posicion {
   30 
   31     /**
   32      * Enumeración de las posibles direcciones que sirven para transformar una
   33      * posición, permite generar una dirección aleatoria. El orden es importante
   34      * para la velocidad en la búsqueda de los movimientos.
   35      */
   36     protected enum Direccion {
   37 
   38         ARRIBA, ABAJO, IZQUIERDA, DERECHA;
   39 
   40         /**
   41          * Genera una dirección aleatoria.
   42          * @return una dirección aleatoria
   43          */
   44         public static Direccion random() {
   45             switch ((int) ((java.lang.Math.random() * 4) + 1)) {
   46                 case 1:
   47                     return ARRIBA;
   48                 case 2:
   49                     return ABAJO;
   50                 case 3:
   51                     return DERECHA;
   52                 case 4:
   53                     return IZQUIERDA;
   54                 default:
   55                     throw new IndexOutOfBoundsException("Fuera del rango aleatorio.");
   56             }
   57         }
   58     }
   59 
   60     private int x;
   61     private int y;
   62 
   63     /**
   64      * Construye una nueva posición con el valor por defecto igual a 0, 0.
   65      */
   66     public Posicion() {
   67         initComponents(0, 0);
   68     }
   69 
   70     /**
   71      * Construye una nueva posición con los valores indicados en los
   72      * parámetros x, y.
   73      * @param x el valor inicial en el eje x
   74      * @param y el valor inicial en el eje y
   75      */
   76     public Posicion(int x, int y) {
   77         initComponents(x, y);
   78     }
   79 
   80     /**
   81      * Inicializa el componente.
   82      * @param x el valor inicial en el eje x
   83      * @param y el valor inicial en el eje y
   84      */
   85     private void initComponents(int x, int y) {
   86         this.setX(x);
   87         this.setY(y);
   88     }
   89 
   90     /**
   91      * Compara con otro objeto para revisar si es una posición, y si es igual
   92      * en los ejes x, y.
   93      * @param obj el objeto a comparar
   94      * @return verdadero si es una posición igual en los ejes x, y; sino falso
   95      */
   96     @Override
   97     public boolean equals(Object obj) {
   98         if (obj == null) {
   99             return false;
  100         }
  101         if (getClass() != obj.getClass()) {
  102             return false;
  103         }
  104         final Posicion other = (Posicion) obj;
  105         if (this.x != other.x) {
  106             return false;
  107         }
  108         if (this.y != other.y) {
  109             return false;
  110         }
  111         return true;
  112     }
  113 
  114     /**
  115      * Genera un valor hash para usar este componente en algoritmos de
  116      * ordenación y comparación.
  117      * @return un valor hash
  118      */
  119     @Override
  120     public int hashCode() {
  121         int hash = 3;
  122         hash = 41 * hash + this.x;
  123         hash = 41 * hash + this.y;
  124         return hash;
  125     }
  126 
  127     /**
  128      * Obtiene una nueva posición de transformación, en función de la dirección
  129      * indicada, con las coordenadas para realizar una tranformación.
  130      * @param d la dirección para obtener la nueva posición de transformación
  131      * @return una nueva posición de transformación con las coordenadas correctas
  132      */
  133     protected static Posicion posicionDireccion(Direccion d) {
  134         switch (d) {
  135             case ARRIBA:
  136                 return new Posicion(0, -1);
  137             case ABAJO:
  138                 return new Posicion(0, 1);
  139             case DERECHA:
  140                 return new Posicion(1, 0);
  141             case IZQUIERDA:
  142                 return new Posicion(-1, 0);
  143             default:
  144                 throw new IndexOutOfBoundsException("Fuera del rango aceptado.");
  145         }
  146     }
  147 
  148     /**
  149      * Realiza una transformación de la posición actual en base a una posición
  150      * indicada, obteniendo como resultado la suma. (x + dx, y + dy). Si la
  151      * posición indicada es negativa regresa la operación inversa (x + (-dx)).
  152      * @param p la posición con la cual vamos a transformar
  153      * @return una nueva posición en función de la operación realizada
  154      */
  155     protected Posicion transformar(Posicion p) {
  156         return new Posicion(this.getX() + p.getX(), this.getY() + p.getY());
  157     }
  158 
  159     /**
  160      * Realiza una transformación relativa de la posición actual en base a una
  161      * dirección indicada, obteniendo como resultado una nueva posición.
  162      * @param d la direccion con la cual vamos a transformar la posición
  163      * @return una nueva posición en función de la dirección indicada
  164      */
  165     public Posicion relativa(Direccion d) {
  166         return transformar(posicionDireccion(d));
  167     }
  168 
  169     /**
  170      * Devuelve la coordenada en x.
  171      * @return la coordenada en x
  172      */
  173     public int getX() {
  174         return x;
  175     }
  176 
  177     /**
  178      * Asigna un valor a la coordenada en x.
  179      * @param x el valor de la coordenada en x
  180      */
  181     public void setX(int x) {
  182         this.x = x;
  183     }
  184 
  185     /**
  186      * Devuelve la coordenada en y.
  187      * @return la coordenada en y
  188      */
  189     public int getY() {
  190         return y;
  191     }
  192 
  193     /**
  194      * Asigna un valor a la coordenada en y.
  195      * @param y el valor de la coordenada en y
  196      */
  197     public void setY(int y) {
  198         this.y = y;
  199     }
  200 }
Tabla.java
    1 /*
    2  * This file is part of JPuzzle.
    3  *
    4  * JPuzzle is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation, either version 3 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * JPuzzle is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with JPuzzle.  If not, see <http://www.gnu.org/licenses/>.
   16  *
   17  * JPuzzle Copyright © 2008 HackmanGT
   18  * HackmanGT@hotmail.com
   19  */
   20 
   21 package JPuzzle.Utilities;
   22 
   23 /**
   24  * La tabla es el modelo abstracto para guardar todas las celdas que componen
   25  * el juego, así como almacenar el ancho y el alto, y obtener sus dimensiones.
   26  * @author HackmanGT
   27  */
   28 public abstract class Tabla {
   29 
   30     /**
   31      * El ancho de la tabla.
   32      */
   33     protected int ancho = 0;
   34     /**
   35      * El alto de la tabla.
   36      */
   37     protected int alto = 0;
   38     /**
   39      * Las celdas que componen la tabla.
   40      */
   41     protected Celda[][] celdas;
   42 
   43     /**
   44      * Valida que la posición indicada se encuentre en el rango válido, dentro
   45      * de los límites de la tabla.
   46      * @param p la posición a validar
   47      * @return verdadero si la posición esta en el rango, sino falso
   48      */
   49     protected boolean esPosicionValida(Posicion p) {
   50         return
   51                 p.getX() >= 0 &&
   52                 p.getX() < ancho &&
   53                 p.getY() >= 0 &&
   54                 p.getY() < alto;
   55     }
   56 
   57     /**
   58      * Obtiene una celda indicada por los ejes x, y; no revisa que sea válida,
   59      * usar con cuidado (preferiblemente llamar esPosicionValida()). Esta
   60      * función se puede llegar a ejecutar más de 280,000 veces al resolver.
   61      * @param x la posición en el eje x
   62      * @param y la posición en el eje y
   63      * @return la celda que representa las coordenadas indicadas
   64      */
   65     public Celda getCelda(int x, int y) {
   66         // if (esPosicionValida(new Posicion(x, y))) {
   67         //    return celdas[x][y];
   68         // }
   69         // return null;
   70         return celdas[x][y];
   71     }
   72 
   73     /**
   74      * Devuelve el tamaño de la tabla, el ancho multiplicado por el alto.
   75      * @return el tamaño de la tabla
   76      */
   77     public int getTamano() {
   78         return ancho * alto;
   79     }
   80 
   81     /**
   82      * Devuelve el ancho de la tabla.
   83      * @return el ancho de la tabla
   84      */
   85     public int getAncho() {
   86         return ancho;
   87     }
   88 
   89     /**
   90      * Asigna un valor al ancho de la tabla.
   91      * @param ancho el ancho de la tabla
   92      */
   93     protected void setAncho(int ancho) {
   94         this.ancho = ancho;
   95     }
   96 
   97     /**
   98      * Devuelve el alto de la tabla.
   99      * @return el alto de la tabla
  100      */
  101     public int getAlto() {
  102         return alto;
  103     }
  104 
  105     /**
  106      * Asigna un valor al alto de la tabla.
  107      * @param alto el alto de la tabla
  108      */
  109     protected void setAlto(int alto) {
  110         this.alto = alto;
  111     }
  112 }
Tablero.java
    1 /*
    2  * This file is part of JPuzzle.
    3  *
    4  * JPuzzle is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation, either version 3 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * JPuzzle is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with JPuzzle.  If not, see <http://www.gnu.org/licenses/>.
   16  *
   17  * JPuzzle Copyright © 2008 HackmanGT
   18  * HackmanGT@hotmail.com
   19  */
   20 
   21 package JPuzzle.Utilities;
   22 
   23 import JPuzzle.Graphics.Matriz;
   24 import java.io.IOException;
   25 import javax.swing.event.ChangeEvent;
   26 import javax.swing.event.ChangeListener;
   27 import javax.swing.event.EventListenerList;
   28 
   29 /**
   30  * El tablero es el controlador para realizar el juego propiamente, se encarga
   31  * de todas las operaciones de creación, modificación, y actualización del
   32  * modelo de datos, haciendo operaciones con todas las celdas en la tabla.
   33  * Maneja la matriz de imágenes que se utilizan en las celdas.
   34  * No es multi-threading!
   35  * @author HackmanGT
   36  */
   37 public class Tablero extends Tabla {
   38 
   39     /**
   40      * El objeto generador de imágenes.
   41      */
   42     protected Matriz matriz = null;
   43     /**
   44      * El evento enviado cuando cambia el modelo de datos.
   45      */
   46     protected ChangeEvent changeEvent = null;
   47     /**
   48      * La lista de vistas que reciben eventos.
   49      */
   50     protected EventListenerList listenerList = new EventListenerList();
   51 
   52     /**
   53      * Construye una nuevo tablero con el tamaño por defecto de 4 x 4.
   54      */
   55     public Tablero() {
   56         initComponents(4, 4);
   57     }
   58 
   59     /**
   60      * Construye un nuevo tablero con los parámetros indicados en ancho y alto.
   61      * @param ancho el valor inicial del ancho de la tabla
   62      * @param alto el valor inicial del alto de la tabla
   63      */
   64     public Tablero(int ancho, int alto) {
   65         initComponents(ancho, alto);
   66     }
   67 
   68     /**
   69      * Inicializa el componente y carga las imágenes.
   70      * @param ancho el valor inicial del ancho de la tabla
   71      * @param alto el valor inicial del alto de la tabla
   72      */
   73     private void initComponents(int ancho, int alto) {
   74         this.setAncho(ancho);
   75         this.setAlto(alto);
   76         this.matriz = new Matriz();
   77         try {
   78             this.matriz.loadImage();
   79         } catch (IOException ex) {
   80             System.err.println(ex.getMessage());
   81         }
   82         crear();
   83     }
   84 
   85     /**
   86      * Intercambia una celda especificada en la posición origen, por otra que
   87      * se indica por la posición destino en la tabla de modelo de datos.
   88      * Y avisa a la vista que cambiaron los datos.
   89      * @param origen la posición de la celda que se debe intercambiar
   90      * @param destino la posición de la celda con la cual debe de intercambiarse
   91      */
   92     protected void intercambiarCelda(Posicion origen, Posicion destino) {
   93         Celda c = celdas[destino.getX()][destino.getY()];
   94         celdas[destino.getX()][destino.getY()] = celdas[origen.getX()][origen.getY()];
   95         celdas[origen.getX()][origen.getY()] = c;
   96         fireStateChanged();
   97     }
   98 
   99     /**
  100      * Crea la tabla de modelo de datos, asignandole un valor consecutivo
  101      * incremental a cada celda, así como una parte de la imagen a mostrar.
  102      * La última celda la elimina, poniendole el valor nulo para indicar vacío.
  103      */
  104     protected void crear() {
  105         int valor = 1;
  106         celdas = new Celda[ancho][alto];
  107         for (int y = 0; y < alto; y++) {
  108             for (int x = 0; x < ancho; x++) {
  109                 celdas[x][y] = new Celda(valor++);
  110                 celdas[x][y].setImagen(matriz.getTile(x, y, ancho, alto));
  111             }
  112         }
  113         celdas[ancho - 1][alto - 1] = null;
  114     }
  115 
  116     /**
  117      * Intenta encontrar la celda vacía en la tabla, indicada por el valor nulo.
  118      * @return la posición de la celda que está vacía, o nulo si no la encuentra
  119      */
  120     protected Posicion encontrarVacio() {
  121         for (int y = 0; y < alto; y++) {
  122             for (int x = 0; x < ancho; x++) {
  123                 if (celdas[x][y] == null) {
  124                     return new Posicion(x, y);
  125                 }
  126             }
  127         }
  128         return null;
  129     }
  130 
  131     /**
  132      * Intenta encontrar una celda en la tabla, indicada por su valor.
  133      * @param valor el número ordinal de la celda en la tabla
  134      * @return la posición de la celda buscada, o nulo si no la encuentra
  135      */
  136     protected Posicion encontrarCelda(int valor) {
  137         for (int y = 0; y < alto; y++) {
  138             for (int x = 0; x < ancho; x++) {
  139                 if (celdas[x][y] != null) {
  140                     if (celdas[x][y].getValor() == valor) {
  141                         return new Posicion(x, y);
  142                     }
  143                 }
  144             }
  145         }
  146         return null;
  147     }
  148 
  149     /**
  150      * Intenta realizar un movimiento dado por la posicion p, validando todos
  151      * los posibles movimimientos cercanos, y si es válido realiza la operación
  152      * de intercambiar por el espacio vacío.
  153      * @param p la posición que se desea mover al espacio vacío
  154      * @return verdadero si se realizo el movimiento, sino falso
  155      */
  156     public boolean intentarMover(Posicion p) {
  157         Posicion n = null;
  158         for (Posicion.Direccion d : Posicion.Direccion.values()) {
  159             n = p.relativa(d);
  160             if (esPosicionValida(n)) {
  161                 if (celdas[n.getX()][n.getY()] == null) {
  162                     intercambiarCelda(n, p);
  163                     return true;
  164                 }
  165             }
  166         }
  167         return false;
  168     }
  169 
  170     /**
  171      * Verifica si todas las celdas están ordenadas naturalmente. Es decir,
  172      * verifica si el jugador ha logrado ordenar las celdas.
  173      * @return verdadero si estan ordenadas, sino falso
  174      */
  175     public boolean verificarGanador() {
  176         int valor = 1;
  177         for (int y = 0; y < alto; y++) {
  178             for (int x = 0; x < ancho; x++) {
  179                 if (celdas[x][y] != null) {
  180                     if (celdas[x][y].getValor() != valor) {
  181                         return false;
  182                     }
  183                 }
  184                 valor++;
  185             }
  186         }
  187         return true;
  188     }
  189 
  190     /**
  191      * Muestra una vista previa del modelo de datos, en la consola de Java.
  192      * Tomando en cuenta los saltos de línea para mantener el formato.
  193      * (Depuración)
  194      */
  195     public void preview() {
  196         for (int y = 0; y < alto; y++) {
  197             for (int x = 0; x < ancho; x++) {
  198                 System.out.format("%2d ",
  199                         celdas[x][y] != null ?
  200                         celdas[x][y].getValor() :
  201                         -1);
  202             }
  203             System.out.println();
  204         }
  205     }
  206 
  207     /**
  208      * Desordena la tabla realizando movimientos aleatorios de cualquiera
  209      * celda que se encuentre en las cuatro direcciónes cercana a la celda
  210      * vacía, el objetivo es simular el (des)ordenamiento natural humano.
  211      * Intenta no regresar a la celda de donde viene.
  212      * @param nivel la cantidad de movimientos aleatorios a realizar
  213      */
  214     public void desordenar(int nivel) {
  215         Posicion p = encontrarVacio();
  216         Posicion n = null;
  217         Posicion m = null;
  218 
  219         int s = 0;
  220         while (s < nivel) {
  221             n = p.relativa(Posicion.Direccion.random());
  222             if (!n.equals(m)) {
  223                 if (esPosicionValida(n)) {
  224                     m = p;
  225                     intercambiarCelda(n, p);
  226                     p = n;
  227                     s++;
  228                 }
  229             }
  230         }
  231     }
  232 
  233     /**
  234      * Resuelve el tablero.
  235      * Ver <b>Class Cerebro</b>
  236      */
  237     public void resolver() {
  238         Cerebro c = new Cerebro(this);
  239         c.Resolver();
  240     }
  241 
  242     /**
  243      * Agrega una vista receptora de eventos a la lista.
  244      * @param l la vista que va a recibir el evento
  245      */
  246     public void addChangeListener(ChangeListener l) {
  247         listenerList.add(ChangeListener.class, l);
  248     }
  249 
  250     /**
  251      * Remueve una vista receptora de eventos de la lista.
  252      * @param l la vista que se va a eliminar
  253      */
  254     public void removeChangeListener(ChangeListener l) {
  255         listenerList.remove(ChangeListener.class, l);
  256     }
  257 
  258     /**
  259      * Avisa a todas las vistas que hubo un cambio en la tabla modelo de datos.
  260      */
  261     protected void fireStateChanged() {
  262         Object[] listeners = listenerList.getListenerList();
  263         for (int i = listeners.length - 2; i >= 0; i -= 2) {
  264             if (listeners[i] == ChangeListener.class) {
  265                 if (changeEvent == null) {
  266                     changeEvent = new ChangeEvent(this);
  267                 }
  268                 ((ChangeListener) listeners[i + 1]).stateChanged(changeEvent);
  269             }
  270         }
  271     }
  272 }
Mesa.java
    1 /*
    2  * This file is part of JPuzzle.
    3  *
    4  * JPuzzle is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation, either version 3 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * JPuzzle is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with JPuzzle.  If not, see <http://www.gnu.org/licenses/>.
   16  *
   17  * JPuzzle Copyright © 2008 HackmanGT
   18  * HackmanGT@hotmail.com
   19  */
   20 
   21 package JPuzzle.Graphics;
   22 
   23 import JPuzzle.Event.MesaActionEvent;
   24 import JPuzzle.Event.MesaActionListener;
   25 import java.awt.Color;
   26 import java.awt.Font;
   27 import java.awt.Graphics;
   28 
   29 /**
   30  * La mesa es la vista del tablero, se encarga de mostrar los datos en el
   31  * viewport, aceptar los eventos del mouse y reaccionar consecuentemente.
   32  * @author HackmanGT
   33  */
   34 public class Mesa extends javax.swing.JPanel {
   35 
   36     private JPuzzle.Utilities.Tablero tablero;
   37     private boolean showNumbers = true;
   38     private boolean showImages = true;
   39     private boolean showGrid = true;
   40     private boolean animated = false;
   41     private boolean running = false;
   42     private int speed = 1000;
   43     private int count = 0;
   44 
   45     /**
   46      * Construye una nueva mesa descendiente de JPanel.
   47      */
   48     public Mesa() {
   49         initComponents();
   50     }
   51 
   52     /**
   53      * Inicializa el componente.
   54      */
   55     private void initComponents() {
   56         crearTablero(3, 3);
   57         addMouseListener(new java.awt.event.MouseAdapter() {
   58             @Override
   59             public void mouseClicked(java.awt.event.MouseEvent evt) {
   60                 mouseClickedHandler(evt);
   61             }
   62         });
   63     }
   64 
   65     /**
   66      * Crea un nuevo tablero con el tamaño indicado en los parámetros.
   67      * @param ancho el nuevo ancho del tablero
   68      * @param alto el nuevo alto del tablero
   69      */
   70     private void crearTablero(int ancho, int alto) {
   71         tablero = new JPuzzle.Utilities.Tablero(ancho, alto);
   72         tablero.addChangeListener(new javax.swing.event.ChangeListener() {
   73             public void stateChanged(javax.swing.event.ChangeEvent evt) {
   74                 stateChangedHandler(evt);
   75             }
   76         });
   77     }
   78 
   79     /**
   80      * Cuando sucede el evento <b>intercambiarCelda()</b> en la clase tablero
   81      * se genera una llamada a esta función por medio del listener.
   82      * @param e el evento recibido
   83      */
   84     private void stateChangedHandler(javax.swing.event.ChangeEvent evt) {
   85         repaint();
   86         count++;
   87         try {
   88             // Solamente desordenar y resolver pueden poner el valor true a
   89             // running; vamos a suponer que siempre que running sea verdadero
   90             // está corriendo en otro thread, y es ese el que duerme (sleep).
   91             if (running) {
   92                 if (animated) {
   93                     firePropertyChange("cuenta", -1, count);
   94                     Thread.sleep(speed);
   95                 }
   96             } else {
   97                 firePropertyChange("cuenta", -1, count);
   98             }
   99         } catch (InterruptedException ex) {
  100             // Vamos a intentar interrumpir el proceso saliendo lo más pronto
  101             // posible del thread que ha sido interrumpido.
  102             animated = false;
  103             System.err.println(ex.getMessage());
  104         }
  105     }
  106 
  107     /**
  108      * Convierte las coordenadas absolutas del componente en coordenadas
  109      * relativas al tablero, intenta hacer el movimiento y verifica si
  110      * hay un ganador. Avisa enviando el evento propertyChange "winner".
  111      * @param evt el evento recibido
  112      */
  113     private void mouseClickedHandler(java.awt.event.MouseEvent evt) {
  114         if (!running) {
  115             int x = evt.getX() / (this.getWidth() / tablero.getAncho());
  116             int y = evt.getY() / (this.getHeight() / tablero.getAlto());
  117             if (tablero.intentarMover(new JPuzzle.Utilities.Posicion(x, y))) {
  118                 if (tablero.verificarGanador()) {
  119                     firePropertyChange("winner", false, true);
  120                     count = 0;
  121                 }
  122             }
  123         }
  124     }
  125 
  126     private static int rectX;
  127     private static int rectY;
  128     private static int rectW;
  129     private static int rectH;
  130     private static Font font;
  131 
  132     {
  133         try {
  134             font = new Font("Monospaced", Font.BOLD, 12);
  135         } catch (Exception e) {
  136             font = this.getFont();
  137         }
  138     }
  139 
  140     /**
  141      * Pinta el tablero, recorre la tabla dibujando la imágen, la rejilla y
  142      * el número correspondiente a cada celda. Calcula las coordenadas
  143      * correspondientes.
  144      * El acceso a la tabla lo realiza a traves de la función getCelda() en
  145      * la clase tablero; se espera que no exista ningún problema con la
  146      * sincronización (aunque no está sincronizado -- overhead), por eso se
  147      * almacena una referencia al tablero, por si este cambia.
  148      * Le falta optimización.
  149      * Debería de corren en el Swing Event Dispatching Thread.
  150      * @param g Graphics from Java System
  151      */
  152     @Override
  153     public void paint(Graphics g) {
  154         super.paint(g);
  155 
  156         JPuzzle.Utilities.Celda c = null;
  157         JPuzzle.Utilities.Tablero t = tablero;
  158         java.awt.image.BufferedImage b = null;
  159 
  160         int alto = t.getAlto();
  161         int ancho = t.getAncho();
  162 
  163         rectW = (this.getWidth() / ancho);
  164         rectH = (this.getHeight() / alto);
  165 
  166         g.setFont(font);
  167         g.setColor(isShowImages() ? Color.WHITE : Color.BLACK);
  168 
  169         for (int y = 0; y < alto; y++) {
  170             for (int x = 0; x < ancho; x++) {
  171 
  172                 rectX = x * rectW;
  173                 rectY = y * rectH;
  174 
  175                 c = t.getCelda(x, y);
  176                 if (c != null) {
  177                     b = c.getImagen();
  178                     if (showImages && b != null) {
  179                         g.drawImage(
  180                                 b,
  181                                 rectX, rectY,
  182                                 rectW, rectH,
  183                                 Color.BLACK,
  184                                 null);
  185                     }
  186                     if (showGrid) {
  187                         g.draw3DRect(
  188                                 rectX, rectY,
  189                                 rectW, rectH,
  190                                 true);
  191                     }
  192                     if (showNumbers) {
  193                         g.drawString(
  194                                 String.valueOf(c.getValor()),
  195                                 (rectX) + (rectW / 2),
  196                                 (rectY) + (rectH / 2));
  197                     }
  198                 } else {
  199                     if (!showImages) {
  200                         g.fill3DRect(
  201                                 rectX +  8, rectY +  8,
  202                                 rectW - 16, rectH - 16,
  203                                 true);
  204                     }
  205                 }
  206             }
  207         }
  208     }
  209 
  210     /**
  211      * Desordena la tabla, pero lo ejecuta en un thread separado,
  212      * para no afectar la velocidad de los eventos. Lo sincroniza
  213      * con el tablero.
  214      */
  215     public void desordenar() {
  216         new Thread() {
  217             @Override
  218             public void run() {
  219                 synchronized (tablero) {
  220                     running = true;
  221                     fireAction(running, "Desordenar.");
  222                     tablero.desordenar(1000);
  223                     running = false;
  224                     fireAction(running, "");
  225                     firePropertyChange("cuenta", -1, count);
  226                     count = 0;
  227                 }
  228             }
  229         }.start();
  230     }
  231 
  232     /**
  233      * Resuelve la tabla, pero lo ejecuta en un thread separado,
  234      * para no afectar la velocidad de los eventos. Lo sincroniza
  235      * con el tablero.
  236      */
  237     public void resolver() {
  238         new Thread() {
  239             @Override
  240             public void run() {
  241                 synchronized (tablero) {
  242                     running = true;
  243                     fireAction(running, "Resolver.");
  244                     tablero.resolver();
  245                     running = false;
  246                     fireAction(running, "");
  247                     firePropertyChange("cuenta", -1, count);
  248                     count = 0;
  249                 }
  250             }
  251         }.start();
  252     }
  253 
  254     /**
  255      * Cambia el tamaño del tablero a unas nuevas dimensiones.
  256      * Se sincroniza con el tablero anterior.
  257      * @param ancho el nuevo ancho
  258      * @param alto el nuevo alto
  259      */
  260     public void cambiarTamaño(int ancho, int alto) {
  261         running = false;
  262         synchronized (tablero) {
  263             crearTablero(ancho, alto);
  264             speed = (11 - ancho) * 125; // El tamaño máximo puede ser 10 x 10
  265             count = 0;
  266             firePropertyChange("cuenta", -1, count);
  267         }
  268         repaint();
  269     }
  270 
  271     /**
  272      * Agrega una vista receptora de eventos a la lista.
  273      * @param l la vista que va a recibir el evento
  274      */
  275     public void addMesaActionListener(MesaActionListener l) {
  276         listenerList.add(MesaActionListener.class, l);
  277     }
  278 
  279     /**
  280      * Remueve una vista receptora de eventos de la lista.
  281      * @param l la vista que se va a eliminar
  282      */
  283     public void removeMesaActionListener(MesaActionListener l) {
  284         listenerList.remove(MesaActionListener.class, l);
  285     }
  286 
  287     /**
  288      * Avisa a todas las vistas que hubo un cambio en el estado de "ejecutandose".
  289      */
  290     protected void fireAction(boolean running, String message) {
  291         Object[] listeners = listenerList.getListenerList();
  292         for (int i = listeners.length - 2; i >= 0; i -= 2) {
  293             if (listeners[i] == MesaActionListener.class) {
  294                 MesaActionEvent mesaActionEvent = new MesaActionEvent(this, running, message);
  295                 ((MesaActionListener) listeners[i + 1]).actionEvent(mesaActionEvent);
  296             }
  297         }
  298     }
  299 
  300     /**
  301      * Devuelve el estado de mostrando números.
  302      * @return el estado de mostrando números
  303      */
  304     public boolean isShowNumbers() {
  305         return showNumbers;
  306     }
  307 
  308     /**
  309      * Asigna el estado de mostrando números.
  310      * @param showNumbers el estado de mostrando números
  311      */
  312     public void setShowNumbers(boolean showNumbers) {
  313         this.showNumbers = showNumbers;
  314         repaint();
  315     }
  316 
  317     /**
  318      * Devuelve el estado de mostrando imágenes.
  319      * @return el estado de mostrando imágenes
  320      */
  321     public boolean isShowImages() {
  322         return showImages;
  323     }
  324 
  325     /**
  326      * Asigna el estado de mostrando imágenes.
  327      * @param showImages el estado de mostrando imágenes
  328      */
  329     public void setShowImages(boolean showImages) {
  330         this.showImages = showImages;
  331         repaint();
  332     }
  333 
  334     /**
  335      * Devuelve el estado de mostrando rejilla.
  336      * @return el estado de mostrando rejilla
  337      */
  338     public boolean isShowGrid() {
  339         return showGrid;
  340     }
  341 
  342     /**
  343      * Asigna el estado de mostrando rejilla.
  344      * @param showGrid el estado de mostrando rejilla
  345      */
  346     public void setShowGrid(boolean showGrid) {
  347         this.showGrid = showGrid;
  348         repaint();
  349     }
  350 
  351     /**
  352      * Devuelve el estado de está animado.
  353      * @return el estado de está animado
  354      */
  355     public boolean isAnimated() {
  356         return animated;
  357     }
  358 
  359     /**
  360      * Asigna el estado de está animado.
  361      * @param animated el estado de está animado
  362      */
  363     public void setAnimated(boolean animated) {
  364         this.animated = animated;
  365         repaint();
  366     }
  367 
  368     /**
  369      * Devuelve el estado de "ejecutandose".
  370      * @return el estado de "ejecutandose"
  371      */
  372     public boolean isRunning() {
  373         return running;
  374     }
  375 }
Panel.java
    1 /*
    2  * This file is part of JPuzzle.
    3  *
    4  * JPuzzle is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation, either version 3 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * JPuzzle is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with JPuzzle.  If not, see <http://www.gnu.org/licenses/>.
   16  *
   17  * JPuzzle Copyright © 2008 HackmanGT
   18  * HackmanGT@hotmail.com
   19  */
   20 
   21 package JPuzzle.Graphics;
   22 
   23 /**
   24  * El panel de control con los diferentes botones de opciones.
   25  * Incluye la mesa controlada por el tablero.
   26  * @author HackmanGT
   27  */
   28 public class Panel extends javax.swing.JPanel {
   29 
   30     /**
   31      * Construye un nuevo panel de opciones.
   32      * Asigna los eventos de status y winner.
   33      */
   34     public Panel() {
   35         initComponents();
   36 
   37         jMesa1.addMesaActionListener(new JPuzzle.Event.MesaActionListener() {
   38             public void actionEvent(JPuzzle.Event.MesaActionEvent evt) {
   39                 cambiarStatus(evt);
   40             }
   41         });
   42 
   43         jMesa1.addPropertyChangeListener("cuenta", new java.beans.PropertyChangeListener() {
   44             public void propertyChange(java.beans.PropertyChangeEvent evt) {
   45                 cambiarCuenta(evt);
   46             }
   47         });
   48 
   49         jMesa1.addPropertyChangeListener("winner", new java.beans.PropertyChangeListener() {
   50             public void propertyChange(java.beans.PropertyChangeEvent evt) {
   51                 mensajeGanador();
   52             }
   53         });
   54     }
   55 
   56     @SuppressWarnings("unchecked")
   57     // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
   58     private void initComponents() {
   59         java.awt.GridBagConstraints gridBagConstraints;
   60 
   61         jMesa1 = new JPuzzle.Graphics.Mesa();
   62         jPanel1 = new javax.swing.JPanel();
   63         jButton1 = new javax.swing.JButton();
   64         jButton2 = new javax.swing.JButton();
   65         jCheckBox1 = new javax.swing.JCheckBox();
   66         jCheckBox2 = new javax.swing.JCheckBox();
   67         jCheckBox3 = new javax.swing.JCheckBox();
   68         jCheckBox4 = new javax.swing.JCheckBox();
   69         jLabel1 = new javax.swing.JLabel();
   70         jComboBox1 = new javax.swing.JComboBox();
   71         jLabel2 = new javax.swing.JLabel();
   72         jTextField1 = new javax.swing.JTextField();
   73         jLabel3 = new javax.swing.JLabel();
   74 
   75         setLayout(new java.awt.BorderLayout());
   76         add(jMesa1, java.awt.BorderLayout.CENTER);
   77 
   78         jPanel1.setLayout(new java.awt.GridBagLayout());
   79 
   80         jButton1.setText("Desordenar");
   81         jButton1.setToolTipText("Desordenar el tablero.");
   82         jButton1.addActionListener(new java.awt.event.ActionListener() {
   83             public void actionPerformed(java.awt.event.ActionEvent evt) {
   84                 jButton1ActionPerformed(evt);
   85             }
   86         });
   87         gridBagConstraints = new java.awt.GridBagConstraints();
   88         gridBagConstraints.gridx = 0;
   89         gridBagConstraints.gridy = 0;
   90         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
   91         jPanel1.add(jButton1, gridBagConstraints);
   92 
   93         jButton2.setText("Resolver");
   94         jButton2.setToolTipText("Resolver el tablero.");
   95         jButton2.addActionListener(new java.awt.event.ActionListener() {
   96             public void actionPerformed(java.awt.event.ActionEvent evt) {
   97                 jButton2ActionPerformed(evt);
   98             }
   99         });
  100         gridBagConstraints = new java.awt.GridBagConstraints();
  101         gridBagConstraints.gridx = 0;
  102         gridBagConstraints.gridy = 1;
  103         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
  104         jPanel1.add(jButton2, gridBagConstraints);
  105 
  106         jCheckBox1.setSelected(true);
  107         jCheckBox1.setText("Imágenes");
  108         jCheckBox1.setToolTipText("Mostrar las imágenes.");
  109         jCheckBox1.addActionListener(new java.awt.event.ActionListener() {
  110             public void actionPerformed(java.awt.event.ActionEvent evt) {
  111                 jCheckBox1ActionPerformed(evt);
  112             }
  113         });
  114         gridBagConstraints = new java.awt.GridBagConstraints();
  115         gridBagConstraints.gridx = 0;
  116         gridBagConstraints.gridy = 2;
  117         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
  118         jPanel1.add(jCheckBox1, gridBagConstraints);
  119 
  120         jCheckBox2.setSelected(true);
  121         jCheckBox2.setText("Números");
  122         jCheckBox2.setToolTipText("Mostrar los números.");
  123         jCheckBox2.addActionListener(new java.awt.event.ActionListener() {
  124             public void actionPerformed(java.awt.event.ActionEvent evt) {
  125                 jCheckBox2ActionPerformed(evt);
  126             }
  127         });
  128         gridBagConstraints = new java.awt.GridBagConstraints();
  129         gridBagConstraints.gridx = 0;
  130         gridBagConstraints.gridy = 3;
  131         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
  132         jPanel1.add(jCheckBox2, gridBagConstraints);
  133 
  134         jCheckBox3.setSelected(true);
  135         jCheckBox3.setText("Rejilla");
  136         jCheckBox3.setToolTipText("Mostrar la rejilla.");
  137         jCheckBox3.addActionListener(new java.awt.event.ActionListener() {
  138             public void actionPerformed(java.awt.event.ActionEvent evt) {
  139                 jCheckBox3ActionPerformed(evt);
  140             }
  141         });
  142         gridBagConstraints = new java.awt.GridBagConstraints();
  143         gridBagConstraints.gridx = 0;
  144         gridBagConstraints.gridy = 4;
  145         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
  146         jPanel1.add(jCheckBox3, gridBagConstraints);
  147 
  148         jCheckBox4.setText("Animar");
  149         jCheckBox4.setToolTipText("Animar los movimientos al desordenar o resolver.");
  150         jCheckBox4.addActionListener(new java.awt.event.ActionListener() {
  151             public void actionPerformed(java.awt.event.ActionEvent evt) {
  152                 jCheckBox4ActionPerformed(evt);
  153             }
  154         });
  155         gridBagConstraints = new java.awt.GridBagConstraints();
  156         gridBagConstraints.gridx = 0;
  157         gridBagConstraints.gridy = 5;
  158         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
  159         jPanel1.add(jCheckBox4, gridBagConstraints);
  160 
  161         jLabel1.setText("Tamaño:");
  162         jLabel1.setBorder(javax.swing.BorderFactory.createEmptyBorder(4, 4, 4, 4));
  163         gridBagConstraints = new java.awt.GridBagConstraints();
  164         gridBagConstraints.gridx = 0;
  165         gridBagConstraints.gridy = 6;
  166         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
  167         jPanel1.add(jLabel1, gridBagConstraints);
  168 
  169         jComboBox1.setModel(new javax.swing.DefaultComboBoxModel(new String[] { 
"3 x 3", "4 x 4", "5 x 5", "6 x 6", "7 x 7", "8 x 8", "9 x 9", "10 x 10" })); 170 jComboBox1.setToolTipText("Cambiar el tamaño del tablero."); 171 jComboBox1.addActionListener(new java.awt.event.ActionListener() { 172 public void actionPerformed(java.awt.event.ActionEvent evt) { 173 jComboBox1ActionPerformed(evt); 174 } 175 }); 176 gridBagConstraints = new java.awt.GridBagConstraints(); 177 gridBagConstraints.gridx = 0; 178 gridBagConstraints.gridy = 7; 179 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 180 jPanel1.add(jComboBox1, gridBagConstraints); 181 182 jLabel2.setText("Cuenta:"); 183 jLabel2.setBorder(javax.swing.BorderFactory.createEmptyBorder(4, 4, 4, 4)); 184 gridBagConstraints = new java.awt.GridBagConstraints(); 185 gridBagConstraints.gridx = 0; 186 gridBagConstraints.gridy = 8; 187 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 188 jPanel1.add(jLabel2, gridBagConstraints); 189 190 jTextField1.setEditable(false); 191 jTextField1.setHorizontalAlignment(javax.swing.JTextField.TRAILING); 192 jTextField1.setText("0"); 193 jTextField1.setToolTipText("Cuenta de los movimientos."); 194 gridBagConstraints = new java.awt.GridBagConstraints(); 195 gridBagConstraints.gridx = 0; 196 gridBagConstraints.gridy = 9; 197 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 198 jPanel1.add(jTextField1, gridBagConstraints); 199 200 jLabel3.setForeground(new java.awt.Color(255, 0, 0)); 201 jLabel3.setBorder(javax.swing.BorderFactory.createEmptyBorder(4, 4, 4, 4)); 202 gridBagConstraints = new java.awt.GridBagConstraints(); 203 gridBagConstraints.gridx = 0; 204 gridBagConstraints.gridy = 10; 205 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; 206 gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; 207 gridBagConstraints.weighty = 1.0; 208 jPanel1.add(jLabel3, gridBagConstraints); 209 210 add(jPanel1, java.awt.BorderLayout.LINE_END); 211 }// </editor-fold>//GEN-END:initComponents 212 213 private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed 214 if (!jMesa1.isRunning()) { 215 jMesa1.desordenar(); 216 } else { 217 javax.swing.JOptionPane.showMessageDialog(this, "Espere... o cambie la opción animar."); 218 } 219 }//GEN-LAST:event_jButton1ActionPerformed 220 221 private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed 222 if (!jMesa1.isRunning()) { 223 jMesa1.resolver(); 224 } else { 225 javax.swing.JOptionPane.showMessageDialog(this, "Espere... o cambie la opción animar."); 226 } 227 }//GEN-LAST:event_jButton2ActionPerformed 228 229 private void jCheckBox1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox1ActionPerformed 230 jMesa1.setShowImages(jCheckBox1.isSelected()); 231 }//GEN-LAST:event_jCheckBox1ActionPerformed 232 233 private void jCheckBox2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox2ActionPerformed 234 jMesa1.setShowNumbers(jCheckBox2.isSelected()); 235 }//GEN-LAST:event_jCheckBox2ActionPerformed 236 237 private void jCheckBox3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox3ActionPerformed 238 jMesa1.setShowGrid(jCheckBox3.isSelected()); 239 }//GEN-LAST:event_jCheckBox3ActionPerformed 240 241 private void jCheckBox4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBox4ActionPerformed 242 jMesa1.setAnimated(jCheckBox4.isSelected()); 243 }//GEN-LAST:event_jCheckBox4ActionPerformed 244 245 private void jComboBox1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jComboBox1ActionPerformed 246 if (!jMesa1.isRunning()) { 247 if (evt.getSource() instanceof javax.swing.JComboBox) { 248 jMesa1.cambiarTamaño( 249 ((javax.swing.JComboBox)evt.getSource()).getSelectedIndex() + 3, 250 ((javax.swing.JComboBox)evt.getSource()).getSelectedIndex() + 3); 251 } 252 } else { 253 javax.swing.JOptionPane.showMessageDialog(this, "Espere... o cambie la opción animar."); 254 } 255 }//GEN-LAST:event_jComboBox1ActionPerformed 256 257 // Variables declaration - do not modify//GEN-BEGIN:variables 258 private javax.swing.JButton jButton1; 259 private javax.swing.JButton jButton2; 260 private javax.swing.JCheckBox jCheckBox1; 261 private javax.swing.JCheckBox jCheckBox2; 262 private javax.swing.JCheckBox jCheckBox3; 263 private javax.swing.JCheckBox jCheckBox4; 264 private javax.swing.JComboBox jComboBox1; 265 private javax.swing.JLabel jLabel1; 266 private javax.swing.JLabel jLabel2; 267 private javax.swing.JLabel jLabel3; 268 private JPuzzle.Graphics.Mesa jMesa1; 269 private javax.swing.JPanel jPanel1; 270 private javax.swing.JTextField jTextField1; 271 // End of variables declaration//GEN-END:variables 272 273 /** 274 * Acción a ejecutar cuando cambie el estado de la cuenta de movimientos. 275 * @param evt el evento 276 */ 277 private void cambiarCuenta(java.beans.PropertyChangeEvent evt) { 278 jTextField1.setText(evt.getNewValue().toString()); 279 } 280 281 /** 282 * Acción a ejecutar cuando cambie el estado de Desordenando o Resolviendo. 283 * @param evt el evento 284 */ 285 private void cambiarStatus(JPuzzle.Event.MesaActionEvent evt) { 286 jComboBox1.setEnabled(!evt.isRunning()); 287 jLabel3.setText(evt.getMessage()); 288 } 289 290 /** 291 * Un mensajito. 292 */ 293 private void mensajeGanador() { 294 javax.swing.JOptionPane.showMessageDialog(this, "¡Resuelto!"); 295 } 296 }
Cerebro.java
    1 /*
    2  * This file is part of JPuzzle.
    3  *
    4  * JPuzzle is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation, either version 3 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * JPuzzle is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with JPuzzle.  If not, see <http://www.gnu.org/licenses/>.
   16  *
   17  * JPuzzle Copyright © 2008 HackmanGT
   18  * HackmanGT@hotmail.com
   19  */
   20 
   21 package JPuzzle.Utilities;
   22 
   23 /**
   24  * La clase Cerebro se encarga de resolver el tablero.
   25  * <b>Realmente esta clase se llama Pinky</b>.
   26  * No está optimizado para velocidad, se llaman demasiadas veces a la
   27  * funcion tablero.encontrarVacio(), así como tablero.encontrarCelda(),
   28  * cuando en algunos casos se podrían almacenar y pasar como parámetros.
   29  * <b>No es un cerebro muy inteligente, algunas veces hace movimientos que
   30  * obviamente no son necesarios; pero hacen mas sencillo el código.</b>
   31  * @author HackmanGT
   32  */
   33 public class Cerebro {
   34 
   35     private Tablero tablero;
   36 
   37     /**
   38      * Construye un nuevo cerebro para resolver el tablero.
   39      * @param tablero el tablero a resolver
   40      */
   41     public Cerebro(Tablero tablero) {
   42         this.tablero = tablero;
   43     }
   44 
   45     /**
   46      * Resuelve el tablero, pasa una a una todas las celdas
   47      * intentando resolver cada una independientemente.
   48      */
   49     public void Resolver() {
   50         int tamano = tablero.getTamano() - 1;
   51         for (int pos = 1; pos < tamano; pos++) {
   52             if (!encontrado(pos)) {
   53                 ResolverCelda(pos);
   54             }
   55         }
   56         if (!encontrado(tamano)) {
   57             intentarUltimoCambio(tamano);
   58         }
   59     }
   60 
   61     /**
   62      * Intenta resolver la posición de una celda indicada.
   63      * Si la celda está en la última columna, mueve el espacio vacío a la
   64      * primera columna de la fila inferior, mueve la celda superior hacia
   65      * abajo y todas las celdas de la fila superior un espacio a la izquierda,
   66      * al terminar vuelve a hacer la misma operación pero invertida.
   67      * @param pos la celda a resolver
   68      */
   69     private void ResolverCelda(int pos) {
   70         if (pos % tablero.getAncho() == 0) { // Cuando esté en la última columna
   71             int poy = (pos - 1) / tablero.getAncho();
   72             Posicion p = new Posicion(0, poy + 1);
   73             while (!encontradoVacioPosicion(p)) {
   74                 while (encontrarMejorMovimiento(p, pos)) {
   75                 }
   76                 intentarUltimoCambio(pos);
   77             }
   78             MoverForzado(p, Posicion.Direccion.ARRIBA);
   79             for (int i = 0; i < tablero.getAncho() - 1; i++) {
   80                 MoverForzado(p, Posicion.Direccion.DERECHA);
   81             }
   82         }
   83 
   84         while (!encontrado(pos)) { // Resulve la celda indicada
   85             Posicion p = encontrarMejorPosicion(pos);
   86             while (encontrarMejorMovimiento(p, pos)) {
   87             }
   88             intentarUltimoCambio(pos);
   89         }
   90 
   91         if (pos % tablero.getAncho() == 0) { // Cuando esté en la última columna
   92             int poy = (pos - 1) / tablero.getAncho();
   93             Posicion p = new Posicion(tablero.getAncho() - 2, poy);
   94             while (!encontradoVacioPosicion(p)) {
   95                 while (encontrarMejorMovimiento(p, pos)) {
   96                 }
   97             }
   98             for (int i = tablero.getAncho() - 2; i >= 0; i--) {
   99                 MoverForzado(p, Posicion.Direccion.IZQUIERDA);
  100             }
  101             MoverForzado(p, Posicion.Direccion.ABAJO);
  102         }
  103     }
  104 
  105     /**
  106      * Valida que la posición indicada esté en el rango válido,
  107      * dentro de los límites de la tabla; y que la celda no esté fija,
  108      * que no sea un número que ya hallamos colocado.
  109      * @param p la posición a comprobar
  110      * @param pos el número de la celda que estamos trabajando
  111      * @return verdadero si es una posición válida, sino falso
  112      */
  113     private boolean esPosicionValida(Posicion p, int pos) {
  114         if (tablero.esPosicionValida(p)) {
  115             Celda c = tablero.getCelda(p.getX(), p.getY());
  116             if (c == null) {
  117                 return true;
  118             } else {
  119                 return c.getValor() > pos;
  120             }
  121         }
  122         return false;
  123     }
  124 
  125     /**
  126      * Verifica si la celda que estamos trabajando se encuentra posicionada
  127      * en el lugar final que le corresponde en la tabla.
  128      * @param pos la celda que estamos trabajando
  129      * @return verdadero si esta en su lugar correspondiente, sino falso
  130      */
  131     private boolean encontrado(int pos) {
  132         int poy = (pos - 1) / tablero.getAncho();
  133         Posicion q = tablero.encontrarCelda(pos);
  134         Posicion r = new Posicion((pos - 1) - (poy * tablero.getAncho()), poy);
  135         return q.equals(r);
  136     }
  137 
  138     /**
  139      * Verifica que la posición indicada sea igual a la posición del espacio
  140      * vacío en la tabla.
  141      * @param p la posición a verificar
  142      * @return verdadero si son iguales, sino falso
  143      */
  144     private boolean encontradoVacioPosicion(Posicion p) {
  145         Posicion q = tablero.encontrarVacio();
  146         return p.equals(q);
  147     }
  148 
  149     /**
  150      * Intenta encontrar la mejor posición para mover la celda vacía,
  151      * y poder cambiar la celda que estamos trabajando.
  152      * <br>
  153      * Busca la celda que estamos trabajando, así como calcula la posición
  154      * que debería tener en el tablero, compara las posiciónes arriba y abajo,
  155      * para ver cual es mejor, después compara izquierda y derecha, por último
  156      * compara las mejores posiciones entre las cuatro revisadas; para saber
  157      * cual está mas cerca para resolver el tablero.
  158      *
  159      * @param pos el número de la celda que estamos trabajando
  160      * @return la mejor posición encontrada, sino hay ninguna entonces nulo (realmente siempre hay una)
  161      */
  162     private Posicion encontrarMejorPosicion(int pos) {
  163         int poy = (pos - 1) / tablero.getAncho();
  164 
  165         Posicion q = tablero.encontrarCelda(pos);
  166         Posicion r = new Posicion((pos - 1) - (poy * tablero.getAncho()), poy);
  167 
  168         Posicion a = compararPosicion(r,
  169                 q.relativa(Posicion.Direccion.ARRIBA),
  170                 q.relativa(Posicion.Direccion.ABAJO),
  171                 pos);
  172         Posicion b = compararPosicion(r,
  173                 q.relativa(Posicion.Direccion.IZQUIERDA),
  174                 q.relativa(Posicion.Direccion.DERECHA),
  175                 pos);
  176 
  177         if (a != null && b != null) {
  178             return compararPosicion(r, a, b, pos);
  179         } else {
  180             if (a != null) {
  181                 return a;
  182             } else {
  183                 if (b != null) {
  184                     return b;
  185                 }
  186             }
  187         }
  188 
  189         return null;
  190     }
  191 
  192     /**
  193      * Compara cual de las dos posiciones dadas <b>b</b> o <b>c</b> está
  194      * mas cercana a la posición <b>a</b>.
  195      * @param a la posición que es el objetivo
  196      * @param b la primera posición a comparar
  197      * @param c la segunda posicion a comparar
  198      * @param pos el número de la celda que estamos trabajando
  199      * @return la posición que está más cercana al objetivo, sino entonces nulo (siempre hay una)
  200      */
  201     private Posicion compararPosicion(Posicion a, Posicion b, Posicion c, int pos) {
  202         if (esPosicionValida(b, pos) && esPosicionValida(c, pos)) {
  203             if (Math.abs(a.getY() - b.getY()) < Math.abs(a.getY() - c.getY()) ||
  204                     Math.abs(a.getX() - b.getX()) < Math.abs(a.getX() - c.getX())) {
  205                 return b;
  206             } else {
  207                 return c;
  208             }
  209         } else {
  210             if (esPosicionValida(b, pos)) {
  211                 return b;
  212             } else {
  213                 if (esPosicionValida(c, pos)) {
  214                     return c;
  215                 }
  216             }
  217         }
  218         return null;
  219     }
  220 
  221     /**
  222      * Este es el procedimiento que hace que se resuelva el tablero.
  223      * <br>
  224      * Búsca cual es el mejor movimiento en todas las direcciones
  225      * en busca de la posición p, para mover el espacio vacío en
  226      * esa dirección. Esta función se podría llamar "hacer uno o varios
  227      * movimientos en la dirección mas adecuada hacia la solución".
  228      *
  229      * @param p la posición a donde queremos llegar
  230      * @param pos el número de celda que estamos trabajando
  231      * @return verdadero si logró moverse en esa dirección, falso si necesita recalcular la mejor posición otra vez
  232      */
  233     private boolean encontrarMejorMovimiento(Posicion p, int pos) {
  234         Posicion q = tablero.encontrarVacio();
  235         Posicion t = null;
  236 
  237         if (q.equals(p)) {
  238             return false;
  239         }
  240 
  241         // Es muy importante que primero intente moverse en Y y después en X,
  242         // así como intente moverse ARRIBA antes de ABAJO e IZQUIERDA antes
  243         // de DERECHA.
  244 
  245         // Verifica arriba y abajo cual está mas cerca e intentar mover
  246 
  247         t = new Posicion(0, 0);
  248         if (cercanoY(q, p, t)) {
  249             if (intentarCambiar(q, t, pos)) {
  250                 return true;
  251             }
  252         }
  253 
  254         // Verifica derecha e izquierda cual está mas cerca e intentar mover
  255 
  256         t = new Posicion(0, 0);
  257         if (cercanoX(q, p, t)) {
  258             if (intentarCambiar(q, t, pos)) {
  259                 return true;
  260             }
  261         }
  262 
  263         Posicion r = tablero.encontrarCelda(pos);
  264 
  265         if (r.equals(p)) {
  266             return false;
  267         }
  268 
  269         // Si no pudo moverse antes, es por que está bloqueda la celda en el
  270         // lado izquierdo e intenta moverse en una forma de L hacia abajo y
  271         // la izquierda.
  272 
  273         if (q.getX() - 1 == r.getX()) {
  274             if (Mover(q, Posicion.Direccion.ABAJO, pos)) {
  275                 if (Mover(q, Posicion.Direccion.IZQUIERDA, pos)) {
  276                     return true;
  277                 }
  278             } else {
  279 
  280                 // Si no pudo moverse antes, por que no pudo moverse hacia abajo
  281                 // es por que estamos en la última linea, pero sigue bloqueda la
  282                 // celda en el lado izquierdo, intenta un movimiento basado en un
  283                 // truco, un movimiento específico que no modifica casí ninguna
  284                 // celda importante o la regresa a su posición lo antes posible.
  285 
  286                 if (q.getX() >= 2 && q.getX() <= tablero.getAncho() - 2) {
  287                     MoverForzado(q, Posicion.Direccion.IZQUIERDA);
  288                     MoverForzado(q, Posicion.Direccion.IZQUIERDA);
  289                     TrucoUltimasLineas(q);
  290                     return false;
  291                 }
  292                 if (q.getX() >= 2 && q.getX() <= tablero.getAncho() - 1) {
  293                     MoverForzado(q, Posicion.Direccion.IZQUIERDA);
  294                     MoverForzado(q, Posicion.Direccion.IZQUIERDA);
  295                     MoverForzado(q, Posicion.Direccion.IZQUIERDA);
  296                     TrucoUltimasLineas(q);
  297                     return false;
  298                 }
  299             }
  300         }
  301 
  302         // Si no pudo moverse antes, es por que está bloqueda la celda en el
  303         // lado derecho e intenta moverse en una forma de L hacia abajo y
  304         // la derecha.
  305 
  306         if (q.getX() + 1 == r.getX()) {
  307             if (Mover(q, Posicion.Direccion.ABAJO, pos)) {
  308                 if (Mover(q, Posicion.Direccion.DERECHA, pos)) {
  309                     return true;
  310                 }
  311             } else {
  312 
  313                 // Si no pudo moverse antes, por que no pudo moverse hacia abajo
  314                 // es por que estamos en la última linea, pero sigue bloqueda la
  315                 // celda en el lado derecho, intenta un movimiento basado en un
  316                 // truco, un movimiento específico que no modifica casí ninguna
  317                 // celda importante o la regresa a su posición lo antes posible.
  318                 // El segundo truco creo que es evaluado solo para 3 x 3.
  319 
  320                 if (q.getX() <= tablero.getAncho() - 4) {
  321                     TrucoUltimasLineas(q);
  322                     return false;
  323                 }
  324                 if (q.getX() + 1 == r.getX()) {
  325                     MoverForzado(q, Posicion.Direccion.ARRIBA);
  326                     MoverForzado(q, Posicion.Direccion.DERECHA);
  327                     MoverForzado(q, Posicion.Direccion.ABAJO);
  328                     MoverForzado(q, Posicion.Direccion.DERECHA);
  329                     MoverForzado(q, Posicion.Direccion.ARRIBA);
  330                     MoverForzado(q, Posicion.Direccion.IZQUIERDA);
  331                     MoverForzado(q, Posicion.Direccion.IZQUIERDA);
  332                     MoverForzado(q, Posicion.Direccion.ABAJO);
  333                     return false;
  334                 }
  335             }
  336         }
  337 
  338         // Si llegó hasta aquí y no pudo moverse antes, es por que está bloqueda
  339         // la celda en el la parte superior e intenta moverse en una forma de L
  340         // hacia la derecha o izquierda y hacia arriba.
  341 
  342         // Es muy importante que en este caso sea primero a la DERECHA y luego
  343         // a la IZQUIERDA. Es decir, para saltarse una celda llena, la rodea
  344         // haciendo una U, primero con una L en los dos casos anteriores, y
  345         // despues una L invertida con estas líneas de código.
  346 
  347         if (q.getY() - 1 == r.getY()) {
  348             if (Mover(q, Posicion.Direccion.DERECHA, pos)) {
  349                 if (Mover(q, Posicion.Direccion.ARRIBA, pos)) {
  350                     return true;
  351                 }
  352             } else {
  353                 if (Mover(q, Posicion.Direccion.IZQUIERDA, pos)) {
  354                     if (Mover(q, Posicion.Direccion.ARRIBA, pos)) {
  355                         return true;
  356                     }
  357                 }
  358             }
  359         }
  360 
  361         return false;
  362     }
  363 
  364     /**
  365      * Comprueba cual posición es mas grande o más pequeña en Y, para modificar
  366      * el transformador en <b>c</b>; para saber si debemos movernos hacia
  367      * arriba o hacia abajo.
  368      * @param a la posición a donde deseamos avanzar
  369      * @param b la posición donde estamos
  370      * @param c la posición que funciona como transformador
  371      * @return falso si las dos son iguales, sino verdadero
  372      */
  373     private boolean cercanoY(Posicion a, Posicion b, Posicion c) {
  374         if (a.getY() > b.getY()) {
  375             c.setY(-1);
  376             return true;
  377         } else {
  378             if (a.getY() < b.getY()) {
  379                 c.setY(1);
  380                 return true;
  381             }
  382 
  383         }
  384         return false;
  385     }
  386 
  387     /**
  388      * Comprueba cual posición es mas grande o más pequeña en X, para modificar
  389      * el transformador en <b>c</b>; para saber si debemos movernos a la
  390      * derecha o a la izquierda.
  391      * @param a la posición a donde deseamos avanzar
  392      * @param b la posición donde estamos
  393      * @param c la posición que funciona como transformador
  394      * @return falso si las dos son iguales, sino verdadero
  395      */
  396     private boolean cercanoX(Posicion a, Posicion b, Posicion c) {
  397         if (a.getX() > b.getX()) {
  398             c.setX(-1);
  399             return true;
  400         } else {
  401             if (a.getX() < b.getX()) {
  402                 c.setX(1);
  403                 return true;
  404             }
  405 
  406         }
  407         return false;
  408     }
  409 
  410     /**
  411      * Mueve la posición indicada en los parámetros en la dirección indicada.
  412      * Nota: la función se podría optimizar extrayendo los if's del case y
  413      * realizando el case dentro de un solo if pero no resulta tan clara.
  414      * @param p la posición a mover
  415      * @param d la dirección a mover
  416      * @param pos el número de celda que estamos trabajando
  417      * @return verdadero si lo pudo mover, sino falso
  418      */
  419     private boolean Mover(Posicion p, Posicion.Direccion d, int pos) {
  420         switch (d) {
  421             case ARRIBA:
  422                 if (intentarCambiar(p, Posicion.posicionDireccion(d), pos)) {
  423                     p.setY(p.getY() - 1);
  424                     return true;
  425                 }
  426                 break;
  427             case ABAJO:
  428                 if (intentarCambiar(p, Posicion.posicionDireccion(d), pos)) {
  429                     p.setY(p.getY() + 1);
  430                     return true;
  431                 }
  432                 break;
  433             case DERECHA:
  434                 if (intentarCambiar(p, Posicion.posicionDireccion(d), pos)) {
  435                     p.setX(p.getX() + 1);
  436                     return true;
  437                 }
  438                 break;
  439             case IZQUIERDA:
  440                 if (intentarCambiar(p, Posicion.posicionDireccion(d), pos)) {
  441                     p.setX(p.getX() - 1);
  442                     return true;
  443                 }
  444                 break;
  445         }
  446         return false;
  447     }
  448 
  449     /**
  450      * Mueve la posición indicada en los parámetros en la dirección indicada
  451      * a la fuerza; eso significa que no verifica si se desplaza una celda
  452      * ya fija, que está en su posición (supuestamente) final, existe por
  453      * que se necesitan mover celdas y regresarlas manualmente.
  454      * Ver <b>TrucoUltimasLineas()</b>
  455      * @param p la posición a mover
  456      * @param d la dirección a mover
  457      * @return verdadero si lo pudo mover, sino falso
  458      */
  459     private boolean MoverForzado(Posicion p, Posicion.Direccion d) {
  460         switch (d) {
  461             case ARRIBA:
  462                 if (intentarCambiarForzado(p, new Posicion(0, -1))) {
  463                     p.setY(p.getY() - 1);
  464                     return true;
  465                 }
  466                 break;
  467             case ABAJO:
  468                 if (intentarCambiarForzado(p, new Posicion(0, 1))) {
  469                     p.setY(p.getY() + 1);
  470                     return true;
  471                 }
  472                 break;
  473             case DERECHA:
  474                 if (intentarCambiarForzado(p, new Posicion(1, 0))) {
  475                     p.setX(p.getX() + 1);
  476                     return true;
  477                 }
  478                 break;
  479             case IZQUIERDA:
  480                 if (intentarCambiarForzado(p, new Posicion(-1, 0))) {
  481                     p.setX(p.getX() - 1);
  482                     return true;
  483                 }
  484                 break;
  485         }
  486         return false;
  487     }
  488 
  489     /**
  490      * El truco consiste en realizar los pasos indicados en el orden correcto
  491      * para mover la celda dos posiciónes adelante sin modificar ninguna celda
  492      * en la línea superior ni inferior (realmente son las últimas lineas, no
  493      * hay línea inferior).
  494      * @param p la posición donde comienza el truco
  495      */
  496     private void TrucoUltimasLineas(Posicion p) {
  497         MoverForzado(p, Posicion.Direccion.ARRIBA);
  498         MoverForzado(p, Posicion.Direccion.DERECHA);
  499         MoverForzado(p, Posicion.Direccion.ABAJO);
  500         MoverForzado(p, Posicion.Direccion.DERECHA);
  501         MoverForzado(p, Posicion.Direccion.DERECHA);
  502         MoverForzado(p, Posicion.Direccion.ARRIBA);
  503         MoverForzado(p, Posicion.Direccion.IZQUIERDA);
  504         MoverForzado(p, Posicion.Direccion.IZQUIERDA);
  505         MoverForzado(p, Posicion.Direccion.IZQUIERDA);
  506         MoverForzado(p, Posicion.Direccion.ABAJO);
  507         MoverForzado(p, Posicion.Direccion.DERECHA);
  508         MoverForzado(p, Posicion.Direccion.DERECHA);
  509         MoverForzado(p, Posicion.Direccion.ARRIBA);
  510         MoverForzado(p, Posicion.Direccion.DERECHA);
  511         MoverForzado(p, Posicion.Direccion.ABAJO);
  512     }
  513 
  514     /**
  515      * Intenta cambiar la celda dada en la posición <b>a</b> transformada con
  516      * la posición (transformador) <b>b</b>; pos sirve para pasarlo a la validación.
  517      * @param a la posición de la celda que queremos probar a cambiar
  518      * @param b el transformador de la posición que queremos probar a cambiar
  519      * @param pos el número de la celda que estamos trabajando
  520      * @return verdadero si lo pudo cambiar, sino falso
  521      */
  522     private boolean intentarCambiar(Posicion a, Posicion b, int pos) {
  523         Posicion c = a.transformar(b);
  524         if (esPosicionValida(c, pos)) {
  525             tablero.intercambiarCelda(a, c);
  526             return true;
  527         }
  528 
  529         return false;
  530     }
  531 
  532     /**
  533      * Intenta cambiar la celda dada en la posición <b>a</b> transformada con
  534      * la posición (transformador) <b>b</b>. Forzado significa que no importa
  535      * que la celda ya este en su lugar, por que en algún momento la vamos a
  536      * regresar manualmente.
  537      * Ver <b>TrucoUltimasLineas()</b>
  538      * @param a la posición de la celda que queremos probar a cambiar
  539      * @param b el transformador de la posición que queremos probar a cambiar
  540      * @return verdadero si lo pudo cambiar, sino falso
  541      */
  542     private boolean intentarCambiarForzado(Posicion a, Posicion b) {
  543         Posicion c = a.transformar(b);
  544         if (tablero.esPosicionValida(c)) {
  545             tablero.intercambiarCelda(a, c);
  546             return true;
  547         }
  548 
  549         return false;
  550     }
  551 
  552     /**
  553      * Intenta hacer el último cambio; es decir, cambiar la celda que estamos
  554      * trabajando por la posición vacía, que ya debe de estar cercana, a una
  555      * celda de distancia.
  556      * @param pos la celda que estamos trabajando
  557      * @return verdadero si la pudo cambiar, sino falso
  558      */
  559     private boolean intentarUltimoCambio(int pos) {
  560         Posicion p = tablero.encontrarVacio();
  561         Posicion q = tablero.encontrarCelda(pos);
  562 
  563         if ((Math.abs(p.getX() - q.getX()) == 0 && Math.abs(p.getY() - q.getY()) == 1) ||
  564                 (Math.abs(p.getX() - q.getX()) == 1 && Math.abs(p.getY() - q.getY()) == 0)) {
  565             tablero.intercambiarCelda(p, q);
  566             return true;
  567         }
  568 
  569         return false;
  570     }
  571 }
Puzzle.java
    1 /*
    2  * This file is part of JPuzzle.
    3  *
    4  * JPuzzle is free software: you can redistribute it and/or modify
    5  * it under the terms of the GNU General Public License as published by
    6  * the Free Software Foundation, either version 3 of the License, or
    7  * (at your option) any later version.
    8  *
    9  * JPuzzle is distributed in the hope that it will be useful,
   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12  * GNU General Public License for more details.
   13  *
   14  * You should have received a copy of the GNU General Public License
   15  * along with JPuzzle.  If not, see <http://www.gnu.org/licenses/>.
   16  *
   17  * JPuzzle Copyright © 2008 HackmanGT
   18  * HackmanGT@hotmail.com
   19  */
   20 
   21 package JPuzzle;
   22 
   23 import JPuzzle.Graphics.Panel;
   24 import java.lang.reflect.InvocationTargetException;
   25 
   26 /**
   27  * La aplicación principal del juego Puzzle.
   28  * Funciona tanto como un Applet como una aplicación Desktop.
   29  * @author HackmanGT
   30  */
   31 public class Puzzle extends javax.swing.JApplet {
   32 
   33     private static Panel panel;
   34 
   35     /**
   36      * Aplicación principal.
   37      * Inicializa el Applet.
   38      */
   39     @Override
   40     public void init() {
   41         super.init();
   42         try {
   43             javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
   44                 public void run() {
   45                     initComponents();
   46                 }
   47             });
   48         } catch (InterruptedException ex) {
   49             System.err.println(ex.getMessage());
   50         } catch (InvocationTargetException ex) {
   51             ex.printStackTrace();
   52         }
   53     }
   54 
   55     /**
   56      * Inicializa el componente.
   57      */
   58     private void initComponents() {
   59         setBackground(java.awt.Color.WHITE);
   60         setLayout(new java.awt.BorderLayout());
   61         panel = new Panel();
   62         add(panel);
   63     }
   64 
   65     /**
   66      * Aplicación principal.
   67      * Inicializa el Frame.
   68      * @param args los argumentos de la línea de comandos
   69      */
   70     public static void main(String[] args) {
   71         javax.swing.SwingUtilities.invokeLater(new Runnable() {
   72             public void run() {
   73                 createAndShowGUI();
   74             }
   75         });
   76     }
   77 
   78     /**
   79      * Inicializa el componente.
   80      */
   81     private static void createAndShowGUI() {
   82         javax.swing.JFrame frame = new javax.swing.JFrame();
   83         frame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
   84         frame.setTitle(".: JPuzzle :.");
   85         frame.setResizable(true);
   86         frame.pack();
   87         frame.setSize(640, 480);
   88         frame.setLocationRelativeTo(null);
   89         frame.add(new Panel());
   90         frame.setVisible(true);
   91     }
   92 }

HTML generated by highlight 2.4.5, http://www.andre-simon.de/


JPuzzle Version 1 Copyright © 2008 HackmanGT
HackmanGT@hotmail.com