/*
* 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 Prim.java
* @date 26 Oct 2014
*/
package es.ull.mazesolver.maze.algorithm;
import java.util.ArrayList;
import es.ull.mazesolver.maze.MazeCreationAlgorithm;
import es.ull.mazesolver.util.Direction;
import es.ull.mazesolver.util.Pair;
/**
* Implementación del algoritmo de Prim para la generación aleatoria de
* laberintos perfectos.
*/
public class Prim extends MazeCreationAlgorithm {
private ArrayList <ArrayList <Boolean>> m_included_cells;
private ArrayList <short []> walls;
/**
* 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 Prim (int rows, int columns) {
super(rows, columns);
walls = new ArrayList <short []>();
m_included_cells = new ArrayList <ArrayList <Boolean>>(rows);
// Creamos una matriz de visitados para saber en cada momento cuáles son
// las celdas que no se han visitado todavía.
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);
}
}
/*
* (non-Javadoc)
*
* @see maze.MazeCreationAlgorithm#runCreationAlgorithm()
*/
@Override
public void runCreationAlgorithm () {
short y = 0;
short x = 0;
// Empezar el laberinto con todo lleno de paredes y selecionar una celda.
m_included_cells.get(y).set(x, true);
addCell(y, x);
// Mientras haya celdas sin visitar, seguir visitando.
int nextWall = 0;
while (!walls.isEmpty()) {
// Seleccionamos una celda y una direccion de dentro de las posibles que
// no hemos escogido aun.
nextWall = (int) Math.round(0 + (Math.random() * (walls.size() - 1)));
y = walls.get(nextWall)[0];
x = walls.get(nextWall)[1];
Direction dir = Direction.fromValue(walls.get(nextWall)[2]);
Pair <Integer, Integer> desp = dir.decompose();
// Si la celda vecina a la posicion i,j +dir sigue estando disponible
// la elegimos y agregamos las celdas vecinas a esta al conjunto, si no
// eliminamos dicha posicion con dicha direccion para que no vuelva
// a salir de forma aleatoria
if (!m_included_cells.get(y + desp.second).get(x + desp.first)) {
openPassage(y, x, dir);
m_included_cells.get(y + desp.second).set(x + desp.first, true);
addCell(y + desp.second, x + desp.first);
}
walls.remove(nextWall);
}
}
/**
* Añade las celdas adyacentes a la (x, y) a la lista de paredes.
*
* @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.
*/
private void addCell (final int y, final int x) {
for (short k = 1; k < Direction.MAX_DIRECTIONS; k++) {
Direction dir = Direction.fromIndex(k);
Pair <Integer, Integer> desp = dir.decompose();
if ((y + desp.second >= 0) && (x + desp.first >= 0) && (y + desp.second < m_rows)
&& (x + desp.first < m_columns)
&& !m_included_cells.get(y + desp.second).get(x + desp.first)) {
short [] aux = {(short) y, (short) x, dir.val};
walls.add(aux);
}
}
}
}