/* * 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 Wilson.java * @date Jan 6, 2015 */ package es.ull.mazesolver.maze.algorithm; import java.awt.Point; import java.util.ArrayList; import es.ull.mazesolver.maze.MazeCreationAlgorithm; import es.ull.mazesolver.util.Direction; /** * Implementación del algoritmo Wilson para la generación aleatoria de * laberintos perfectos. */ public class Wilson extends MazeCreationAlgorithm { private int m_remaining; private ArrayList <ArrayList <Boolean>> m_included_cells; /** * Constructor. Crea una nueva instancia de la clase. * * @param rows * Número de filas del laberinto. * @param columns * Número de columnas del laberinto. */ public Wilson (int rows, int columns) { super(rows, columns); // Creamos una matriz de visitados para saber en cada momento cuáles son // las celdas que no se han visitado todavía. m_included_cells = new ArrayList <ArrayList <Boolean>>(rows); for (int y = 0; y < rows; y++) { m_included_cells.add(new ArrayList <Boolean>(columns)); for (int x = 0; x < columns; x++) m_included_cells.get(y).add(false); } m_remaining = columns * rows - 1; int x = (int) (Math.random() * m_columns); int y = (int) (Math.random() * m_rows); m_included_cells.get(y).set(x, true); } /* * (non-Javadoc) * * @see es.ull.mazesolver.maze.MazeCreationAlgorithm#runCreationAlgorithm() */ @Override protected void runCreationAlgorithm () { while (m_remaining > 0) { ArrayList <Short []> path = walk(); for (int i = 0; i < path.size(); i++) { Short [] aux = path.get(i); Point p = new Point(aux[0], aux[1]); Direction dir = Direction.fromValue(aux[2]); m_included_cells.get(p.y).set(p.x, true); openPassage(p.y, p.x, dir); m_remaining -= 1; } } } /** * Método que calcula un camino a seguir. * * @return Lista de puntos con su respectiva dirección que hemos de seguir * para llegar a una zona visitada. */ private ArrayList <Short []> walk () { Point p_start = getRandomStarter(); ArrayList <ArrayList <Direction>> directionsTaken; directionsTaken = new ArrayList <ArrayList <Direction>>(m_rows); for (int y = 0; y < m_rows; y++) { directionsTaken.add(new ArrayList <Direction>(m_columns)); for (int x = 0; x < m_columns; x++) directionsTaken.get(y).add(Direction.NONE); } Point p = p_start; do { Direction dir = getRandomDirection(p.y, p.x); directionsTaken.get(p.y).set(p.x, dir); p = dir.movePoint(p); } while (!m_included_cells.get(p.y).get(p.x)); p = p_start; ArrayList <Short []> path = new ArrayList <Short []>(); do { Direction dir = directionsTaken.get(p.y).get(p.x); Short [] pos = {(short) p.x, (short) p.y, dir.val}; path.add(pos); p = dir.movePoint(p); } while (!m_included_cells.get(p.y).get(p.x)); return path; } /** * Obtiene una dirección aleatoria desde la posición indicada que la conecte * con una posición no explorada dentro del laberinto que se está creando. * * @param y * Posición en el eje Y desde la que se quiere partir. * @param x * Posición en el eje X desde la que se quiere partir. * @return Una direccion aleatoria dentro de las posibles a las que ir * en la casilla dada por las posiciones i y j. */ private Direction getRandomDirection (int y, int x) { ArrayList <Direction> directions = new ArrayList <Direction>(); Point actual = new Point(x, y); // Comprobamos qué posiciones de alrededor son válidas y no se han visitado // Suponemos que la posición proporcionada es válida para empezar for (int i = 1; i < Direction.MAX_DIRECTIONS; i++) { Direction dir = Direction.fromIndex(i); Point next = dir.movePoint(actual); if (next.y >= 0 && next.y < m_rows && next.x >= 0 && next.x < m_columns) directions.add(dir); } if (directions.isEmpty()) return Direction.NONE; else return directions.get((int) (Math.random() * directions.size())); } /** * Obtiene un punto inicial de manera aleatoria desde el cual se puede llamar * al método {@link Wilson#walk}. * * @return Punto inicial aleatorio para metodo walk */ private Point getRandomStarter () { ArrayList <Point> freePoints = new ArrayList <Point>(); for (int y = 0; y < m_rows; y++) for (int x = 0; x < m_columns; x++) if (!m_included_cells.get(y).get(x)) freePoints.add(new Point(x, y)); return freePoints.get((int) (Math.random() * freePoints.size())); } }