/* * 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 Kruskal.java * @date 3 Nov 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 Kruskal para la generación aleatoria de * laberintos. */ public class Kruskal extends MazeCreationAlgorithm { private ArrayList <Integer> disjoint_set; 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 Kruskal (int rows, int columns) { super(rows, columns); walls = new ArrayList <short []>(); // Creamos una matriz de visitados para saber en cada momento cuáles son // las celdas que no se han visitado todavía. disjoint_set = new ArrayList <Integer>(); for (int i = 0; i < m_rows * m_columns; i++) disjoint_set.add(i); addAll(); } /* * (non-Javadoc) * * @see maze.MazeCreationAlgorithm#runCreationAlgorithm() */ @Override public void runCreationAlgorithm () { int nextWall, y, x; 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 pertenece a otro conjunto // entonces, la marcamos del mismo conjunto (y a cada elemento del mismo) // y abrimos el pasillo por ahi. if (value(y, x) != value(y + desp.second, x + desp.first)) { openPassage(y, x, dir); union(value(y, x), value(y + desp.second, x + desp.first)); } walls.remove(nextWall); } } /** * Añade todos los muros a la lista de muros a elegir aleatoriamente. */ private void addAll () { for (int y = 0; y < m_rows; y++) for (int x = 0; x < m_columns; 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)) { short [] aux = {(short) y, (short) x, dir.val}; walls.add(aux); } } } /** * Obtiene el índice que representa la posición (x, y). * * @param y * Posición en el eje Y. * @param x * Posición en el eje X. * * @return La posición del vector dada por el punto (x, y). */ private int pos (int y, int x) { // FIXME Esto está invertido. La fórmula real sería (y * m_columns) + x // No lo cambio porque no sé si hay código que depende de este // comportamiento erróneo. return (x * m_columns) + y; } /** * Une dos conjuntos disjuntos, de la forma siguiente: Todo conjunto que * tenga como valor representativo value_from, lo mueve al conjunto de valor * value_to. * * @param value_from * Valor representativo del conjunto que se va a ser unido al otro * conjunto. * @param value_to * Valor representativo del conjunto al que se va a unir el otro * conjunto. */ private void union (int value_from, int value_to) { for (int k = 0; k < disjoint_set.size(); k++) if (disjoint_set.get(k) == value_from) disjoint_set.set(k, value_to); } /** * Obtiene el valor del conjunto del elemento (x, y). * * @param y * Posición en el eje Y. * @param x * Posición en el eje X. * * @return Valor del conjunto del elemento (x, y). */ private int value (int y, int x) { return disjoint_set.get(pos(y, x)); } }