/* * This file is part of MazeSolver. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Copyright (c) 2014 MazeSolver * Sergio M. Afonso Fumero <theSkatrak@gmail.com> * Kevin I. Robayna Hernández <kevinirobaynahdez@gmail.com> */ /** * @file Maze.java * @date 21/10/2014 */ package es.ull.mazesolver.maze; import java.awt.Point; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import es.ull.mazesolver.gui.MainWindow; import es.ull.mazesolver.util.Direction; /** * Clase que representa un laberinto. */ public class Maze { private ArrayList <ArrayList <MazeCell>> m_maze; private Point m_exit; /** * Crea un laberinto a partir de un algoritmo de generación de laberintos * ya inicializado. * * @param alg * Algoritmo de creación de laberintos ya inicializado */ public Maze (MazeCreationAlgorithm alg) { if (alg != null) { m_maze = alg.createMaze(); m_exit = alg.getExit(); } else throw new IllegalArgumentException( MainWindow.getTranslations().exception().invalidMazeCreationAlgorithm()); } /** * Crea un laberinto a partir de su versión serializada en un fichero. * * @param fileName * Nombre del fichero del que cargar el laberinto. * @throws IOException * Cuando no se encuentra el fichero, no se puede abrir para su * lectura o no contiene un laberinto válido. */ public Maze (String fileName) throws IOException { loadFile(fileName); } /** * Obtiene la celda situada en una posición concreta del laberinto. * * @param row * Fila. * @param column * Columna. * @return Celda en la posición indicada. */ public MazeCell get (int row, int column) { return m_maze.get(row).get(column); } /** * Establece el contenido de una celda en el laberinto. * * @param row * Fila. * @param column * Columna. * @param cell * Celda que se quiere introducir. */ public void set (int row, int column, MazeCell cell) { m_maze.get(row).set(column, cell); } /** * Obtiene el número de columnas (anchura) del laberinto. * * @return Anchura (en celdas) del laberinto. */ public int getWidth () { return m_maze.get(0).size(); } /** * Obtiene el número de filas (altura) del laberinto. * * @return Altura (en celdas) del laberinto. */ public int getHeight () { return m_maze.size(); } /** * Cambia la salida del laberinto a otro lugar, añadiendo y eliminando las * paredes necesarias. * * @param pos * Índice de la celda en la que se quiere colocar la salida. * Dependiendo de la dirección este valor se referirá a una columna * (si la dirección es arriba o abajo) o a una fila (si la dirección * es izquierda o derecha). * @param dir * Borde del laberinto donde colocar la salida. */ public void setExit (int pos, Direction dir) { // Comprobamos que los parámetros son válidos if (pos < 0 || dir == Direction.NONE) return; if (dir.isVertical() && pos >= getWidth()) return; if (dir.isHorizontal() && pos >= getHeight()) return; // Tapamos la salida anterior antes de modificar su posición if (m_exit.x < 0) m_maze.get(m_exit.y).get(0).setWall(Direction.LEFT); else if (m_exit.x >= getWidth()) m_maze.get(m_exit.y).get(getWidth() - 1).setWall(Direction.RIGHT); else if (m_exit.y < 0) m_maze.get(0).get(m_exit.x).setWall(Direction.UP); else if (m_exit.y >= getHeight()) m_maze.get(getHeight() - 1).get(m_exit.x).setWall(Direction.DOWN); // Modificamos la salida del laberinto y abrimos la pared switch (dir) { case UP: m_exit.move(pos, 0); break; case DOWN: m_exit.move(pos, getHeight() - 1); break; case LEFT: m_exit.move(0, pos); break; case RIGHT: m_exit.move(getWidth() - 1, pos); break; default: break; } m_maze.get(m_exit.y).get(m_exit.x).unsetWall(dir); m_exit.setLocation(dir.movePoint(m_exit)); } /** * Obtiene una copia del lugar donde está la salida del laberinto. * * @return La posición donde se encuentra la salida al laberinto. */ public Point getExit () { return new Point(m_exit); } /** * Determina si el punto se encuentra dentro del laberinto o no. * * @param p * Punto a testear. * @return Si el punto está dentro del laberinto o no. */ public boolean containsPoint (Point p) { return p.x >= 0 && p.y >= 0 && p.x < getWidth() && p.y < getHeight(); } /** * Carga una instancia de laberinto de un fichero que contiene una instancia * de esta clase serializada. * * @param fileName * Nombre del fichero del que cargar el laberinto. * @throws IOException * Cuando no se encuentra el fichero, no se puede abrir para su * lectura o no contiene un laberinto válido. */ @SuppressWarnings ("unchecked") public void loadFile (String fileName) throws IOException { try { FileInputStream fileIn = new FileInputStream(fileName); ObjectInputStream in = new ObjectInputStream(fileIn); m_maze = (ArrayList <ArrayList <MazeCell>>) in.readObject(); m_exit = (Point) in.readObject(); in.close(); fileIn.close(); } catch (ClassNotFoundException c) { throw new IOException(c); } } /** * Guarda la actual instancia de la clase {@link Maze} en un fichero mediante * su serialización. * * @param fileName * Nombre del fichero donde guardar el laberinto. * @throws IOException * Cuando el fichero no se puede abrir o no se tienen permisos de * escritura en el mismo. */ public void saveFile (String fileName) throws IOException { FileOutputStream fileOut = new FileOutputStream(fileName); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(m_maze); out.writeObject(m_exit); out.close(); fileOut.close(); } /** * Calcula el número máximo de aristas que se pueden añadir en un laberinto * del tamaño dado. Cada arista se refiere a un pasillo abierto entre 2 * celdas. Se puede ver como el número de paredes que tiene en su interior un * laberinto perfecto, sin contar el contorno. * * @param rows * Número de filas. * @param columns * Número de columnas. * @return Número de paredes en el interior del laberinto. */ public static int perfectMazeWalls (int rows, int columns) { return (rows * columns) - rows - columns + 1; } /** * Calcula el número de aristas que tiene un laberinto perfecto de las * dimensiones dadas. Las aristas representan pasillos entre 2 celdas. * * @param rows * Númnero de filas. * @param columns * Número de columnas. * @return Número de aristas del laberinto perfecto. */ public static int perfectMazeEdges (int rows, int columns) { return (rows * columns) - 1; } }