Как реализовать лабиринт, используя непересекающиеся множества?

Вот класс DisjointSet, который я использую:

public class DisjointSet{
    public DisjointSet(int size){
        s = new int[size];
        for(int i = 0; i < size; ++i){
            s[i] = -1;
        }
    }

    public void union(int el1, int el2){
        int root1 = find(el1);
        int root2 = find(el2);
        if(root1 == root2){
            return;
        }

        if(s[root2] < s[root1]){
            s[root1] = root2;
        }
        else{
            if(s[root1] == s[root2]){
                --s[root1];
            }
            s[root2] = root1;
        }
    }

    public int find(int x){
        if(s[x] < 0){
            return x;
        }
        else{
            s[x] = find(s[x]);
            return s[x];
        }
    }

    private int[] s;
}

И вот мой класс лабиринта:

public class Maze
{
    public Vector<Wall> maze;
    public Vector<Room> graph;
    public Vector<Integer> path;

    public int LASTROOM;
    public int height;
    public int width;
    public Random generator;
    public DisjointSet ds;

    public int dsSize;

    public Maze(int w, int h, int seed)
    {
        width = w;
        height = h;
        dsSize = width * height;

        LASTROOM = dsSize-1;

        // Maze initialization with all walls
        maze = new Vector<Wall>();
        for(int i = 0; i < height; ++i)
        {
            for(int j = 0; j < width; ++j)
            {
                if(i > 0)
                    maze.add(new Wall(j+i*height, j+(i-1)*height));

                if(j > 0)
                    maze.add(new Wall(j+i*height, j-1+i*height));
            }
        }

        // Creation of the graph topology of the maze
        graph = new Vector<Room>();
        for(int i = 0; i < dsSize; ++i)
            graph.add(new Room(i));

        // Sort the walls of random way
        generator = new Random(seed);

        for(int i = 0; i < maze.size(); ++i)
        {
            //TODO
        }

        // Initialization of related structures
        ds = new DisjointSet(dsSize);
        path = new Vector<Integer>();
    }

    public void generate()
    {
        //TODO
    }

    public void solve()
    {
        //TODO
    }
}

Я долго искал способ реализации generate() и solve() вместе со случайной сортировкой стен лабиринта, и я не могу найти какой-либо алгоритм или реализацию в Интернете, чтобы сделать это,

Функция generate() должна пройти через переставленные стены в переменной лабиринта и уничтожить ее, если две части (комнаты), соединенные стеной, уже не находятся в одном наборе. Метод также должен добавить ребро в графе комнаты (в каждой комнате есть список смежных именованных путей, а в классе комнаты есть переменная id, которая идентифицирует вершины каждого графа).

solve() должен решить путь лабиринта и сгенерировать вектор класса Лабиринт, содержащий порядок комнат, через которые нужно пройти, чтобы добраться до выхода. Первая комната расположена в 0, а последняя комната в LASTROOM.

Примечание: конструкторы лабиринта и комнаты:

public Wall(int r1, int r2)
{
    room1 = r1;
    room2 = r2;
}

public Room(int i)
{
    id = i;
    distance = -1;
    paths = new Vector<Integer>();
}

Если кто-то будет достаточно любезен, предложит реализацию, которая будет работать на Java, я был бы очень признателен, спасибо.

1 ответ

Решение

Прежде всего, мне очень нравится идея лабиринтов, и я работаю над аналогичным проектом на Java с созданием Torus Mazes.

Чтобы создать свой лабиринт, вам нужно взглянуть на это ключевое предложение:

... каждая комната имеет список смежных именованных путей, а класс Room имеет переменную id, которая идентифицирует вершины каждого графа

Что это говорит нам? Это говорит нам о структуре данных, которая нам нужна! Когда вы имеете дело с такими проблемами, как это; смежные векторы, соединенные ребрами, вы, несомненно, собираетесь создать список смежности.

Есть несколько разных способов сделать это, но, безусловно, самый простой (и, возможно, наиболее эффективный в этом случае) - это создать массив связанных списков. Это список смежности, который я создал без использования встроенных структур из библиотек Java, но ту же логику можно использовать, если вы решите использовать LinkedList<> встроенный.

/*
 * The Node class creates individual elements that populate the 
 * List class. Contains indexes of the node's neighbors and their
 * respective edge weights
 */
class Node {
    public int top;
    public int topWeight;
    public int bottom;
    public int bottomWeight;
    public int left;
    public int leftWeight;
    public int right;
    public int rightWeight;
    public int numConnec;

    // Default constructor, ititializes neghbors to -1 by default and edge
    // weights to 0
    Node () {
        top    = -1;
        right  = -1;
        bottom = -1;
        left   = -1;
    }
} // End Node class


/*
 * The List class contains Nodes, which are linked to one another
 * to create a Linked List. Used as an adjacency list in the
 * UnionFind class
 */
 class List {
    public Node neighbors;

    // Default constructor
    List () {
        neighbors = new Node ();
    }

    /**
     * Generates valid edges for the node, also assigns a randomly generated weight to that edge
     * @param i         The row that the node exists on, used to generate outer-node edges
     * @param j         The index of the node in the maze from 0 to (2^p)^2 - 1
     * @param twoP      Represents the dimensions of the maze, used in calculating valid edges
     * @param choice    Randomly generated number to choose which edge to generate
     * @param weight    Randomly generated number to assign generated edge a weight
     * @return          If the assignment was done correctly, returns true. Else returns false.
     */
    public boolean validEdges (int i, int j, int twoP, int choice, int weight) {
        if (neighbors.numConnec < 4) {
            // Top
            if (choice == 0) {
                neighbors.top = generateTop(i, j, twoP);
                neighbors.topWeight = weight;
                neighbors.numConnec++;
            }

            // Right
            else if (choice == 1) {
                neighbors.right = generateRight(i, j, twoP);
                neighbors.rightWeight = weight;
                neighbors.numConnec++;
            }

            // Bottom
            else if (choice == 2) {
                neighbors.bottom = generateBottom(i, j, twoP);
                neighbors.bottomWeight = weight;
                neighbors.numConnec++;
            }

            // Left
            else if (choice == 3) {
                neighbors.left = generateLeft(i, j, twoP);
                neighbors.leftWeight = weight;
                neighbors.numConnec++;
            }
        }
        else {
            return false;
        }
        return true;
    }

    public int generateTop (int i, int j, int twoP) {
        int neighbor = 0;

        // Set the top neighbor
        if (i == 0) {
            neighbor = j + twoP * (twoP + (-1));
        }
        else {
            neighbor = j + (-twoP);
        }
        return neighbor;
    }

    public int generateRight (int i, int j, int twoP) {
        int neighbor = 0;

        // Set the right neighbor
        if (j == twoP * (i + 1) + (-1)) {
            neighbor = twoP * i;
        }
        else {
            neighbor = j + 1;
        }
        return neighbor;
    }

    public int generateBottom (int i, int j, int twoP) {
        int neighbor = 0;

        // Set the bottom neighbor
        if (i == twoP + (-1)) {
            neighbor = j - twoP * (twoP + (-1));
        }
        else {
            neighbor = j + twoP;
        }
        return neighbor;
    }

    public int generateLeft (int i, int j, int twoP) {
        int neighbor = 0;

        // Set the left neighbor
        if (j == twoP * i) {
            neighbor = twoP * (i + 1) + (-1);
        }
        else {
            neighbor = j + (-1);
        }
        return neighbor;
    }
} // End List class

Чтобы решить лабиринт, это звучит как проблема, которую может решить реализация алгоритма Дейкстры.

Дейкстра работает, начиная с вашего первого узла, чтобы создать известный набор. Затем вы определяете кратчайший путь к следующему ребру и добавляете этот узел к известному набору. Каждый раз, когда вы ищите следующий кратчайший путь, вы добавляете расстояние, пройденное от первого узла.

Этот процесс продолжается до тех пор, пока все узлы не окажутся в известном наборе и не будет рассчитан кратчайший путь к вашей цели.

Кратчайшие пути

Другие вопросы по тегам