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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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