Алгоритми створення лабіринту — це автоматичні методи для створення лабіринтів.
Методи теорії графів
Лабіринт може бути створено, починаючи з заздалегідь визначеного розташування клітин (найчастіше прямокутної сітки, але можливі інші форми) зі стінками між ними. Ця наперед визначена домовленість може розглядатися як зв'язний граф з ребрами, що представляють можливі стінки, і вузлами, що представляють клітини. Призначенням алгоритму створення лабіринту можна вважати створення підграфу, в якому важко знайти шлях між двома окремими вузлами.
Якщо підграф не є зв'язним графом, то існують марні області графу, тому що вони не сприяють простору пошуку. Якщо граф містить петлі, то між вибраними вузлами може бути кілька шляхів. Через це, для створення лабіринтів часто звертаються до генерування випадкового кістякового дерева. Петлі, які можуть заплутати наївні алгоритми розв'язування лабіринтів, можуть бути введені шляхом додавання випадкових ребер до результату під час виконання алгоритму.
Анімація показує покрокове створення лабіринту по графу, який не розташований на прямокутній сітці. Спочатку комп'ютер створює випадковий планарний граф G, показаний синім кольором, а його двозв'язний граф F показано жовтим кольором. Потім комп'ютер проходить по F за допомогою обраного алгоритму, наприклад, такого як пошук у глибину, та зафарбовує шлях червоним. Під час обходу, коли червоне ребро перетинає синє ребро, синє ребро видаляється. Нарешті, коли всі вершини F будуть відвідані, видаляється F. Також, видаляються два ребра графу G, які використовуються для входу і для виходу.
Алгоритми на основі обходу графу
Пошук у глибину
Цей алгоритм є рандомізованою версією алгоритму пошуку у глибину. Часто реалізований з використанням стека, цей підхід є одним з найпростіших способів створення лабіринту за допомогою комп'ютера. Розглянемо простір для лабіринту, який є великою сіткою клітин (як велика шахова дошка), кожна комірка починається з чотирьох стін. Починаючи з випадкової клітинки, комп'ютер потім вибирає сусідню клітинку, яка ще не була відвідана. Комп'ютер видаляє стіну між двома клітинками і позначає нову клітинку як відвідану, і додає її до стека, щоб полегшити зворотне відтворення. Комп'ютер продовжує цей процес, і клітина, в якій немає невідвіданих сусідів, вважається тупиковим. Потрапивши у тупикову клітину, він відступає по шляху, поки не досягне клітини з невідвіданим сусідом, продовжуючи генерацію шляху, відвідавши цю нову, невідвідану клітинку (створюючи новий вузол). Цей процес триває до тих пір, поки не буде відвідано кожну клітину, що призведе до того, що комп'ютер повернеться назад до початкової клітини. Ми можемо бути впевнені, що кожна клітина відвідується.
Як зазначено вище, цей алгоритм передбачає глибоку рекурсію, яка може викликати проблеми з переповненням стека на деяких архітектурах комп'ютера. Цього можна уникнути, якщо використовувати явний стек. Нижче наведено два приклади на мові Java, які реалізують обидва варіанти. Алгоритм може бути представлений через цикл шляхом збереження інформації про зворотний порядок у самому лабіринті. Це також забезпечує швидкий спосіб зображення рішення, починаючи з будь-якої точки і повертаючи до початку.
Лабіринти, що генеруються завдяки пошуку у глибину, мають низький коефіцієнт розгалуження і містять багато довгих коридорів, тому що алгоритм досліджує, наскільки це можливо, по кожній гілці, перш ніж повернутись назад.
Алгоритм пошуку у глибину для створення лабіринтів часто реалізується за допомогою пошуку з вертанням:
- Зробіть початкову клітинку поточною клітиною та позначте її як відвідану
- У той час як є невідвідані клітини
- Якщо поточна клітина має сусідів, які не були відвідані
- Вибирайте випадково одного з невідвіданих сусідів
- Покладіть поточну клітину у стек
- Зніміть стінку між поточною клітиною і вибраною клітиною
- Зробіть вибрану клітину поточною клітиною та позначте її як відвідану
- Інакше, якщо стек не порожній
- Видаліть зі стека клітину
- Зробіть її поточною клітиною
- Якщо поточна клітина має сусідів, які не були відвідані
Рандомізований алгоритм Крускала
Цей алгоритм є рандомізованою версією алгоритму Крускала.
- Створіть список всіх стін і створіть набір для кожної клітини, кожна з яких містить тільки одну клітинку.
- Для кожної стіни, в якомусь випадковому порядку:
- Якщо клітини, розділені цією стіною, належать до різних наборів:
- Видаліть поточну стіну.
- Приєднуйтеся до наборів раніше розділених клітин.
- Якщо клітини, розділені цією стіною, належать до різних наборів:
Існує кілька структур даних, які можна використовувати для моделювання наборів клітин. Ефективна реалізація, що використовує структуру даних непересічної множини, може виконувати кожне об'єднання і знаходити операцію на двох наборах в майже сталий час (зокрема, складність часу ; для будь-якої правдоподібної величини ), тому час роботи цього алгоритму по суті пропорційний кількості стінок, доступних для лабіринту.
Незалежно від того, чи спочатку перелік стін рандомізований, або якщо стіна випадковим чином вибирається з невипадкового списку, так чи це інакше просто перекласти в формат коду.
Оскільки ефект цього алгоритму полягає у створенні мінімального кістякового дерева з графу з рівно зваженими ребрами, його метою є створення регулярних шаблонів, які достатньо легко вирішити.
Рандомізований алгоритм Прима
Цей алгоритм є рандомізованою версією алгоритму Прима.
- Почніть з сітки, повної стін.
- Виберіть клітинку, позначте її як частину лабіринту. Додайте стінки клітинки до списку стін.
- У той час як у списку є стіни:
- Виберіть випадкову стіну зі списку. Якщо відвідано лише одну з двох клітин, які розділяє стіна:
- Зробіть стіну проходом і позначте невідвідану клітинку як частину лабіринту.
- Додайте сусідні стіни клітинки до списку стін.
- Видаліть стіну зі списку.
- Виберіть випадкову стіну зі списку. Якщо відвідано лише одну з двох клітин, які розділяє стіна:
Як правило, відносно легко знайти шлях до стартової клітини, але важко знайти шлях де-небудь ще.
Зауважимо, що просто запуск класичного алгоритму Прима на графі з випадковими рельєфними вагами створить лабіринти, стилістично ідентичні алгоритму Крускала, тому що вони обидва є алгоритмами для побудування мінімального кістякового дерева. Замість цього, цей алгоритм вводить стилістичну варіацію, оскільки ребра, розташовані ближче до початкової точки, мають меншу ефективну вагу.
Модифікована версія
Хоча класичний алгоритм Прима зберігає список ребер, для генерації лабіринтів можна замість цього зберегти список сусідніх клітин. Якщо випадково обрана клітина має декілька ребер, які з'єднують її з лабіринтом, що існує, виберіть одне з цих ребер випадковим чином. Це, як правило, буде трохи більше, ніж вищезазначена версія на основі ребер.
Алгоритм Вільсона
Всі перераховані вище алгоритми мають різного роду зміщення: пошук у глибину зміщено у бік довгих коридорів, тоді як алгоритми Крускала/Прима зміщені до багатої кількості коротких тупиків. З іншого боку, алгоритм Вільсона генерує незміщену вибірку з дискретним рівномірним розподілом над усіма лабіринтами, використовуючи випадковий пошук з виключенням циклічності.
Розпочнемо алгоритм, ініціалізуючи лабіринт однією довільно вибраною клітинкою. Потім ми починаємо з нової клітини, обраної довільно, і виконуємо випадковий пошук, поки не дістанемося до клітини, яка вже знаходиться в лабіринті — однак, якщо в будь-якій точці випадковий пошук досягне свого шляху, утворюючи петлю, ми виключаємо цикл з шляху перед тим, як продовжити. Коли шлях доходить до лабіринту, ми додаємо його до лабіринту. Потім ми виконуємо інший випадковий пошук з виключенням циклічності з іншої довільної початкової клітинки, повторюючи, поки всі клітини не будуть заповнені.
Ця процедура залишається неупередженою незалежно від того, який метод ми використовуємо для довільного вибору стартових клітинок. Отже, ми завжди можемо вибрати для простоти першу незаповнену клітинку (скажімо) зліва-направо, зверху вниз.
Метод рекурсивного поділу
початкова камера | поділ двома стінками | отвори у стінках | продовження підподілу… | завершення |
---|---|---|---|---|
Лабіринти можуть бути створені за допомогою рекурсивного поділу, алгоритму, який працює наступним чином: Почніть з простору лабіринту без стін. Назвемо це камерою. Розділіть камеру випадково розташованою стінкою (або декількома стінами), де кожна стіна містить випадково розташований прохідний отвір. Потім рекурсивно повторіть процес на підкамерах, поки всі камери не будуть мінімальними. Цей метод призводить до того, що лабіринти з довгими прямими стінами перетинають їхній простір, що полегшує перегляд, які ділянки слід уникати.
Наприклад, у прямокутному лабіринті будують у випадкових точках дві стіни, перпендикулярні одна одній. Ці дві стіни ділять велику камеру на чотири менші камери, розділені чотирма стінами. Вибирають випадково три з чотирьох стін, і відкривають по одному отвору шириною з клітинку у випадковій точці в кожній з трьох. Продовжують таким чином рекурсивно, поки кожна камера не має ширину однієї клітини в одному з двох напрямків.
Прості алгоритми
Існують інші алгоритми, які вимагають достатньо пам'яті для зберігання однієї лінії 2D лабіринту або однієї площини 3D лабіринту.
Алгоритм Еллера запобігає петлям, зберігаючи які клітини в поточному рядку з'єднані через клітинки попередніх рядків, і ніколи не видаляє стінки між будь-якими двома вже з'єднаними клітинами. Алгоритм бічного намотування починається з відкритого проходу по всьому верхньому рядку, а наступні рядки складаються з коротших горизонтальних проходів з одним включенням до проходу вище. Алгоритм бічного намотування тривіально вирішується знизу вгору, тому що він не має висхідних кінців. Враховуючи початкову ширину, обидва алгоритми створюють ідеальні лабіринти необмеженої висоти.
Більшість алгоритмів генерації лабіринтів вимагають збереження зв'язків між клітинами всередині неї, щоб кінцевий результат був розв'язуваним. Проте можуть бути створені допустимі, просто пов'язані лабіринти, незалежно фокусуючись на кожній клітині. Бінарний деревуватий лабіринт — це стандартний ортогональний лабіринт, де кожна клітина завжди має прохід, що веде ліворуч або ліворуч, але ніколи не обидва. Щоб створити бінарний деревуватий лабіринт, для кожної клітинки киньте монетку, щоб вирішити, чи слід додати прохід, що веде вгору або вліво. Завжди вибирайте той самий напрямок для клітин на межі, а кінцевий результат буде валідний просто пов'язаним лабіринтом, який виглядає як бінарне дерево, при цьому верхній лівий кут буде його коренем. Як і в алгоритмі бічного намотування, бінарний лабіринт дерева не має мертвих кінців у напрямках зміщення.
Пов'язана форма перегортання монети для кожної клітинки полягає у створенні зображення за допомогою випадкового поєднання символів вперед, косих рис і зворотних рисок. Це не генерує валідний просто пов'язаний лабіринт, а скоріше вибір замкнутих петель і однокурсових уривків (посібник для Commodore 64 представляє програму мовою BASIC, яка використовує цей алгоритм, але використовує графічні символи [en] діагональної лінії замість більш гладкого графічного вигляду).
Алгоритми клітинного автомата
Деякі типи клітинних автоматів можна використовувати для створення лабіринтів. Два клітинних автомати відомих як, Maze і Mazectric, мають лінійки B3/S12345 і B3/S1234. У першому це означає, що клітини виживають з однієї покоління до наступного, якщо у них є принаймні один і не більше п'яти сусідів Мура. В останньому це означає, що клітини виживають, якщо мають від одного до чотирьох сусідів. Якщо клітина має рівно трьох сусідів, вона народжується. Це схоже на автомат гра життя в тому, що структури, які не мають живої клітини, що прилягає до 1, 4 або 5 інших живих клітин у будь-якому поколінні, будуть поводитися ідентично до неї. Однак для великих моделей він поводитися зовсім інакше, ніж в житті., для великих моделей він поводитися зовсім інакше, ніж автомат гра життя.
Для випадкового стартового шаблону ці клітинні автомати, що генерують лабіринт, перетворяться на складні лабіринти з чітко визначеними стінами, що викладають коридори. Mazecetric, що має правило B3/S1234, має тенденцію генерувати довші і прямі коридори у порівнянні з Maze, з правилом B3/S12345. Оскільки ці правила клітинного автомата є детермінованими, кожен згенерований лабіринт однозначно визначається його випадковим початковим шаблоном. Це є суттєвим недоліком, оскільки лабіринти мають тенденцію бути відносно передбачуваними.
Як і деякі з описаних вище методів теорії графів, ці клітинні автомати зазвичай генерують лабіринти з одного початкового шаблону; отже, як правило, відносно легко знайти шлях до стартової клітини, але важче знайти шлях в іншому місці.
Приклад коду мовою Python
Приклад реалізації варіанту алгоритму Прима мовою Python. Вище зазначений алгоритм Прима починається з сітки, повної стін, і росте по одному компоненту прохідних плиток. У цьому прикладі ми починаємо з відкритої сітки і ростом кількох компонентів стін.
Цей алгоритм працює шляхом створення n (щільності) островів довжиною p (складність). Острів створюється вибором випадкової початкової точки з непарними координатами, потім вибирається випадковий напрямок. Якщо клітина на двох кроках вільна у напрямку, то в цьому напрямку додають стіну на одному кроці і на два кроки. Процес повторюється для n кроків для цього острова. Створюються острови. n і p виражаються як float для пристосування їх до розміру лабіринту. З низькою складністю острови дуже малі і лабіринт легко вирішити. З низькою щільністю лабіринт має більше «великих порожніх кімнат».
import numpy from numpy.random import randint as rand import matplotlib.pyplot as pyplot def maze(width=81, height=51, complexity=.75, density=.75): # Тільки непарні форми shape = ((height // 2) * 2 + 1, (width // 2) * 2 + 1) # Відрегулюємо складність і щільність щодо розміру лабіринту complexity = int(complexity * (5 * (shape[0] + shape[1]))) # кількість компонентів density = int(density * ((shape[0] // 2) * (shape[1] // 2))) # розмір компонентів # Побудуємо справжній лабіринт Z = numpy.zeros(shape, dtype=bool) # Заповнюємо кордони Z[0, :] = Z[-1, :] = 1 Z[:, 0] = Z[:, -1] = 1 # Зробимо проходи for i in range(density): x, y = rand(0, shape[1] // 2) * 2, rand(0, shape[0] // 2) * 2 # вибираємо випадкову позицію Z[y, x] = 1 for j in range(complexity): neighbours = [] if x > 1: neighbours.append((y, x - 2)) if x < shape[1] - 2: neighbours.append((y, x + 2)) if y > 1: neighbours.append((y - 2, x)) if y < shape[0] - 2: neighbours.append((y + 2, x)) if len(neighbours): y_,x_ = neighbours[rand(0, len(neighbours) - 1)] if Z[y_, x_] == 0: Z[y_, x_] = 1 Z[y_ + (y - y_) // 2, x_ + (x - x_) // 2] = 1 x, y = x_, y_ return Z pyplot.figure(figsize=(10, 5)) pyplot.imshow(maze(80, 40), cmap=pyplot.cm.binary, interpolation='nearest') pyplot.xticks([]), pyplot.yticks([]) pyplot.show()
Приклад коду мовою Java
Приклад реалізації алгоритму рандомізованого пошуку у глибину.
RandomizedDFS.java:
package de.amr.maze.simple; import java.util.ArrayDeque; import java.util.BitSet; import java.util.Deque; import java.util.Random; /** * Створює лабіринт з графу сітки, використовуючи рандомізований пошук у глибину. * * @author Armin Reichert */ public class RandomizedDFS{ public static void main(String[] args) { // Використання рекурсивної процедури: System.out.println(maze(10, 10, 0, 0, true)); // Використання нерекурсивної процедури: System.out.println(maze(10, 10, 0, 0, false)); } /** Повертає лабіринт заданого розміру початкової генерації в даній позиції сітки. */ public static Grid maze(int numCols, int numRows, int startCol, int startRow, boolean recursive) { Grid grid = new Grid(numCols, numRows); BitSet visited = new BitSet(numCols * numRows); if (recursive) { traverseRecursive(grid, grid.vertex(startCol, startRow), visited); } else { traverseUsingStack(grid, grid.vertex(startCol, startRow), visited); } return grid; } /** Рекурсивна процедура, що проходить через сітку і створює лабіринт (кістякове дерево). */ private static void traverseRecursive(Grid grid, int v, BitSet visited) { visited.set(v); for (int dir = unvisitedDir(grid, v, visited); dir != -1; dir = unvisitedDir(grid, v, visited)) { grid.addEdge(v, dir); traverseRecursive(grid, grid.neighbor(v, dir), visited); } } /** Нерекурсивна процедура, що проходить через сітку і створює лабіринт (кістякове дерево). */ private static void traverseUsingStack(Grid grid, int v, BitSet visited) { Deque<Integer> stack = new ArrayDeque<>(); visited.set(v); stack.push(v); while (!stack.isEmpty()) { int dir = unvisitedDir(grid, v, visited); if (dir != -1) { int neighbor = grid.neighbor(v, dir); grid.addEdge(v, dir); visited.set(neighbor); stack.push(neighbor); v = neighbor; } else { v = stack.pop(); } } } /** Повертає випадковий напрямок невідомому сусіду або -1. */ private static int unvisitedDir(Grid grid, int v, BitSet visited) { int[] candidates = new int[4]; int numCandidates = 0; for (int dir : new int[] { Grid.NORTH, Grid.EAST, Grid.SOUTH, Grid.WEST }) { int neighbor = grid.neighbor(v, dir); if (neighbor != Grid.NO_VERTEX && !visited.get(neighbor)) { candidates[numCandidates++] = dir; } } return numCandidates == 0 ? -1 : candidates[new Random().nextInt(numCandidates)]; } }
Grid.java:
package de.amr.maze.simple; import java.util.BitSet; /** * Реалізація сітки графу. Набір ребер представлений одним набором бітів. */ public class Grid { /** Напрямки. */ public static final int NORTH = 0, EAST = 1, SOUTH = 2, WEST = 3; /** Протилежні напрямки. */ public static final int[] OPPOSITE = { SOUTH, WEST, NORTH, EAST }; /* Напрямні вектори. */ public static final int[] X = { 0, 1, 0, -1 }; public static final int[] Y = { -1, 0, 1, 0 }; /** Індекс не представляє вершини. */ public static final int NO_VERTEX = -1; /** Кількість стовпців сітки. */ public final int numCols; /** Кількість рядків сітки. */ public final int numRows; private BitSet edges; /** Індекс бітового набору ребра, що залишає вершину v до напрямку dir. */ private int bit(int v, int dir) { return 4 * v + dir; } /** Створює порожню сітку заданого розміру. */ public Grid(int numCols, int numRows) { this.numCols = numCols; this.numRows = numRows; edges = new BitSet(numCols * numRows * 4); } /** Вершина в колонці col і в рядку row. */ public int vertex(int col, int row) { return numCols * row + col; } /** Стовпчик вершини v. */ public int col(int v) { return v % numCols; } /** Рядок вершини v. */ public int row(int v) { return v / numCols; } /** Повертає кількість (неорієнтованих) ребер. */ public int numEdges() { return edges.cardinality() / 2; } /** Додає ребро з вершини v до напрямку dir. */ public void addEdge(int v, int dir) { edges.set(bit(v, dir)); edges.set(bit(neighbor(v, dir), OPPOSITE[dir])); } /** Вказує, чи ребро від вершини v до напрямку dir існує. */ public boolean hasEdge(int v, int dir) { return edges.get(bit(v, dir)); } /** * Повертає сусіда вершини v до напрямку dir або #NO_VERTEX. */ public int neighbor(int v, int dir) { int col = col(v) + X[dir], row = row(v) + Y[dir]; return col >= 0 && col < numCols && row >= 0 && row < numRows ? vertex(col, row) : NO_VERTEX; } /** Повертає текстове представлення цієї сітки. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); for (int row = 0; row < numRows; ++row) { for (int col = 0; col < numCols; ++col) { sb.append(String.format("[%2d,%2d]", row, col)); sb.append(col < numCols - 1 && hasEdge(vertex(col, row), EAST) ? "--" : " "); } if (row < numRows - 1) { sb.append("\n"); for (int col = 0; col < numCols; ++col) { sb.append(hasEdge(vertex(col, row), SOUTH) ? " {{!}} ": " "); } sb.append(«\n»); } } sb.append(String.format(«\n\n[%d cols, %d rows, %d edges]\n», numCols, numRows, numEdges())); return sb.toString(); } }
Приклад коду мовою С
Нижче наведено приклад алгоритму пошуку в глибину для створення лабіринту мовою С.
//Код написано Яцеком Вічореком #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> typedef struct { int x, y; //Позиція вузла - мала витрата пам'яті, але сприяє швидшому генеруванню void *parent; //Покажчик на батьківський вузол char c; //Символ для відображення char dirs; //Напрямки, які ще не визначені } Node; Node *nodes; //Масив вузлів int width, height; //Розміри лабіринту int init( ) { int i, j; Node *n; //Виділення пам'яті для лабіринту nodes = calloc( width * height, sizeof( Node ) ); if ( nodes == NULL ) return 1; //Встановлення важливих вузлів for ( i = 0; i < width; i++ ) { for ( j = 0; j < height; j++ ) { n = nodes + i + j * width; if ( i * j % 2 ) { n->x = i; n->y = j; n->dirs = 15; //Припустимо, що всі напрямки можна дослідити //(встановлено 4 наймолодших біти) n->c = ' '; } else n->c = '#'; //Додаємо стінки між вузлами } } return 0; } Node *link( Node *n ) { //Підключаємо вузол до випадкового сусіда (якщо можливо) і повертаємо //адресу наступного вузла, який слід відвідати int x, y; char dir; Node *dest; //Нічого не можна зробити, якщо задано нульовий покажчик - вихід if ( n == NULL ) return NULL; //Поки є напрямки досі невизначені while ( n->dirs ) { //Довільно вибирається один напрямок dir = ( 1 << ( rand( ) % 4 ) ); //Якщо його вже було досліджено - спробуйте ще раз if ( ~n->dirs & dir ) continue; //Позначимо напрямок як досліджений n->dirs &= ~dir; //Залежно від обраного напрямку switch ( dir ) { //Перевіряємо, чи можна йти праворуч case 1: if ( n->x + 2 < width ) { x = n->x + 2; y = n->y; } else continue; break; //Перевіряємо, чи можна спуститися case 2: if ( n->y + 2 < height ) { x = n->x; y = n->y + 2; } else continue; break; //Перевіряємо, чи можна йти ліворуч case 4: if ( n->x - 2 >= 0 ) { x = n->x - 2; y = n->y; } else continue; break; //Перевіряємо, чи можна піднятися case 8: if ( n->y - 2 >= 0 ) { x = n->x; y = n->y - 2; } else continue; break; } //Отримаємо вузол призначення через покажчик (що робить речі трохи швидшими) dest = nodes + x + y * width; //Переконаємося, що вузол призначення не є стіною if ( dest->c == ' ' ) { //Якщо призначення вже є зв'язаним вузлом - перервати if ( dest->parent != NULL ) continue; //В іншому випадку прийняти вузол dest->parent = n; //Видалення стінки між вузлами nodes[n->x + ( x - n->x ) / 2 + ( n->y + ( y - n->y ) / 2 ) * width].c = ' '; //Повернення адреси дочірнього вузла return dest; } } //Якщо нічого більше тут не можна зробити - повернемо батьківську адресу return n->parent; } void draw( ) { int i, j; //Вихідні дані лабіринту до терміналу - нічого особливого for ( i = 0; i < height; i++ ) { for ( j = 0; j < width; j++ ) { printf( "%c", nodes[j + i * width].c ); } printf( "\n" ); } } int main( int argc, char **argv ) { Node *start, *last; //Перевірка кількості аргументів if ( argc < 3 ) { fprintf( stderr, "%s: please specify maze dimensions!\n", argv[0] ); exit( 1 ); } //Читання розмірів лабіринту з аргументів командного рядка if ( sscanf( argv[1], "%d", &width ) + sscanf( argv[2], "%d", &height ) < 2 ) { fprintf( stderr, "%s: invalid maze size value!\n", argv[0] ); exit( 1 ); } //Дозволяємо лише непарні розміри if ( !( width % 2 ) {{!}}{{!}} !(height % 2) ) { fprintf( stderr, «%s: dimensions must be odd!\n», argv[0] ); exit( 1 ); } //Не допускаємо від'ємних розмірів if ( width <= 0 {{!}}{{!}} height <= 0 ) { fprintf( stderr, «%s: dimensions must be greater than 0!\n», argv[0] ); exit( 1 ); } //Насінний випадковий генератор srand(time(NULL)); //Ініціалізація лабіринту if ( init() ) { fprintf( stderr, «%s: out of memory!\n», argv[0] ); exit( 1 ); } //Встановлення початкового вузла start = nodes + 1 + width; start->parent = start; last = start; //Підключаємо вузли до тих пір, поки не буде досягнутий початковий вузол і //не буде покинутим while ((last = link(last)) != start); draw(); }
Див. також
Примітки
- Вільсон, Девід Брюс (22–24 Травня 1996). . Симпозіум з теорії обчислень. Філадельфія: ACM. с. 296—303. doi:10.1145/237814.237880. ISBN . Архів оригіналу (PDF) за 14 травня 2013. Процитовано 30 квітня 2019.
- Джеміс Бак (29 Грудня 2010). . Архів оригіналу за 28 квітня 2019. Процитовано 30 квітня 2019.
- Джеміс Бак (3 Лютого 2011). . Архів оригіналу за 3 травня 2019. Процитовано 30 квітня 2019.
- Nathaniel Johnston та ін. (21 Серпня 2010). . LifeWiki. Архів оригіналу за 14 грудня 2013. Процитовано 1 Березня 2011.
Посилання
- Думайте про лабіринт: алгоритми лабіринту [ 6 квітня 2019 у Wayback Machine.] (подробиці щодо цих та інших алгоритмів генерації лабіринту)
- Джеміс Бак: HTML 5 Презентація з demo версіями алгоритмів створення лабіринтів [ 28 квітня 2019 у Wayback Machine.]
- Візуалізація генерування лабіринтів [ 13 лютого 2020 у Wayback Machine.]
- Реалізація мовою Java алгоритму Прима [ 24 квітня 2019 у Wayback Machine.]
- Реалізація алгоритму пошуку в глибину для створення лабіринту [ 26 квітня 2019 у Wayback Machine.] декількома мовами в Rosetta Code
- Армін Рейхерт: 34 алгоритми створення лабіринту мовою Java 8, з демо-додатками [ 12 червня 2018 у Wayback Machine.]
- CADforum: алгоритм створення лабіринту в VisualLISP [ 14 вересня 2018 у Wayback Machine.]
- Кодовий виклик #10.1: Генератор лабіринтів з p5.js — Частина 1: Алгоритм створення лабіринту в JavaScript з p5 [ 25 червня 2020 у Wayback Machine.]
- Генератор лабіринтів Чарльза Бонда, COMPUTE! Журнал, Грудень 1981
Вікіпедія, Українська, Україна, книга, книги, бібліотека, стаття, читати, завантажити, безкоштовно, безкоштовно завантажити, mp3, відео, mp4, 3gp, jpg, jpeg, gif, png, малюнок, музика, пісня, фільм, книга, гра, ігри, мобільний, телефон, android, ios, apple, мобільний телефон, samsung, iphone, xiomi, xiaomi, redmi, honor, oppo, nokia, sonya, mi, ПК, web, Інтернет
Algoritmi stvorennya labirintu ce avtomatichni metodi dlya stvorennya labirintiv Cej labirint stvorenij modifikovanoyu versiyeyu algoritmu Prima vin opisanij nizhche Metodi teoriyi grafivAnimaciya metodu zasnovanogo na teoriyi grafiv Labirint mozhe buti stvoreno pochinayuchi z zazdalegid viznachenogo roztashuvannya klitin najchastishe pryamokutnoyi sitki ale mozhlivi inshi formi zi stinkami mizh nimi Cya napered viznachena domovlenist mozhe rozglyadatisya yak zv yaznij graf z rebrami sho predstavlyayut mozhlivi stinki i vuzlami sho predstavlyayut klitini Priznachennyam algoritmu stvorennya labirintu mozhna vvazhati stvorennya pidgrafu v yakomu vazhko znajti shlyah mizh dvoma okremimi vuzlami Yaksho pidgraf ne ye zv yaznim grafom to isnuyut marni oblasti grafu tomu sho voni ne spriyayut prostoru poshuku Yaksho graf mistit petli to mizh vibranimi vuzlami mozhe buti kilka shlyahiv Cherez ce dlya stvorennya labirintiv chasto zvertayutsya do generuvannya vipadkovogo kistyakovogo dereva Petli yaki mozhut zaplutati nayivni algoritmi rozv yazuvannya labirintiv mozhut buti vvedeni shlyahom dodavannya vipadkovih reber do rezultatu pid chas vikonannya algoritmu Animaciya pokazuye pokrokove stvorennya labirintu po grafu yakij ne roztashovanij na pryamokutnij sitci Spochatku komp yuter stvoryuye vipadkovij planarnij graf G pokazanij sinim kolorom a jogo dvozv yaznij graf F pokazano zhovtim kolorom Potim komp yuter prohodit po F za dopomogoyu obranogo algoritmu napriklad takogo yak poshuk u glibinu ta zafarbovuye shlyah chervonim Pid chas obhodu koli chervone rebro peretinaye sinye rebro sinye rebro vidalyayetsya Nareshti koli vsi vershini F budut vidvidani vidalyayetsya F Takozh vidalyayutsya dva rebra grafu G yaki vikoristovuyutsya dlya vhodu i dlya vihodu Algoritmi na osnovi obhodu grafu Poshuk u glibinu source source source source source Animaciya procesu mislennya generatora z vikoristannyam poshuku u glibinu Cej algoritm ye randomizovanoyu versiyeyu algoritmu poshuku u glibinu Chasto realizovanij z vikoristannyam steka cej pidhid ye odnim z najprostishih sposobiv stvorennya labirintu za dopomogoyu komp yutera Rozglyanemo prostir dlya labirintu yakij ye velikoyu sitkoyu klitin yak velika shahova doshka kozhna komirka pochinayetsya z chotiroh stin Pochinayuchi z vipadkovoyi klitinki komp yuter potim vibiraye susidnyu klitinku yaka she ne bula vidvidana Komp yuter vidalyaye stinu mizh dvoma klitinkami i poznachaye novu klitinku yak vidvidanu i dodaye yiyi do steka shob polegshiti zvorotne vidtvorennya Komp yuter prodovzhuye cej proces i klitina v yakij nemaye nevidvidanih susidiv vvazhayetsya tupikovim Potrapivshi u tupikovu klitinu vin vidstupaye po shlyahu poki ne dosyagne klitini z nevidvidanim susidom prodovzhuyuchi generaciyu shlyahu vidvidavshi cyu novu nevidvidanu klitinku stvoryuyuchi novij vuzol Cej proces trivaye do tih pir poki ne bude vidvidano kozhnu klitinu sho prizvede do togo sho komp yuter povernetsya nazad do pochatkovoyi klitini Mi mozhemo buti vpevneni sho kozhna klitina vidviduyetsya Yak zaznacheno vishe cej algoritm peredbachaye gliboku rekursiyu yaka mozhe viklikati problemi z perepovnennyam steka na deyakih arhitekturah komp yutera Cogo mozhna uniknuti yaksho vikoristovuvati yavnij stek Nizhche navedeno dva prikladi na movi Java yaki realizuyut obidva varianti Algoritm mozhe buti predstavlenij cherez cikl shlyahom zberezhennya informaciyi pro zvorotnij poryadok u samomu labirinti Ce takozh zabezpechuye shvidkij sposib zobrazhennya rishennya pochinayuchi z bud yakoyi tochki i povertayuchi do pochatku Gorizontalne zmishennya prohodu Labirinti sho generuyutsya zavdyaki poshuku u glibinu mayut nizkij koeficiyent rozgaluzhennya i mistyat bagato dovgih koridoriv tomu sho algoritm doslidzhuye naskilki ce mozhlivo po kozhnij gilci persh nizh povernutis nazad source source source source source source source Rekursivnij poshuk z vertannyam na geksagonalnij sitci Algoritm poshuku u glibinu dlya stvorennya labirintiv chasto realizuyetsya za dopomogoyu poshuku z vertannyam Zrobit pochatkovu klitinku potochnoyu klitinoyu ta poznachte yiyi yak vidvidanu U toj chas yak ye nevidvidani klitini Yaksho potochna klitina maye susidiv yaki ne buli vidvidani Vibirajte vipadkovo odnogo z nevidvidanih susidiv Pokladit potochnu klitinu u stek Znimit stinku mizh potochnoyu klitinoyu i vibranoyu klitinoyu Zrobit vibranu klitinu potochnoyu klitinoyu ta poznachte yiyi yak vidvidanu Inakshe yaksho stek ne porozhnij Vidalit zi steka klitinu Zrobit yiyi potochnoyu klitinoyuRandomizovanij algoritm Kruskala source source source source source source source Animaciya generaciyi labirintu rozmirom 30 na 20 za dopomogoyu algoritmu Kruskala Cej algoritm ye randomizovanoyu versiyeyu algoritmu Kruskala Stvorit spisok vsih stin i stvorit nabir dlya kozhnoyi klitini kozhna z yakih mistit tilki odnu klitinku Dlya kozhnoyi stini v yakomus vipadkovomu poryadku Yaksho klitini rozdileni ciyeyu stinoyu nalezhat do riznih naboriv Vidalit potochnu stinu Priyednujtesya do naboriv ranishe rozdilenih klitin Isnuye kilka struktur danih yaki mozhna vikoristovuvati dlya modelyuvannya naboriv klitin Efektivna realizaciya sho vikoristovuye strukturu danih neperesichnoyi mnozhini mozhe vikonuvati kozhne ob yednannya i znahoditi operaciyu na dvoh naborah v majzhe stalij chas zokrema skladnist chasu O a V displaystyle O alpha V a x lt 5 displaystyle alpha x lt 5 dlya bud yakoyi pravdopodibnoyi velichini x displaystyle x tomu chas roboti cogo algoritmu po suti proporcijnij kilkosti stinok dostupnih dlya labirintu Nezalezhno vid togo chi spochatku perelik stin randomizovanij abo yaksho stina vipadkovim chinom vibirayetsya z nevipadkovogo spisku tak chi ce inakshe prosto pereklasti v format kodu Oskilki efekt cogo algoritmu polyagaye u stvorenni minimalnogo kistyakovogo dereva z grafu z rivno zvazhenimi rebrami jogo metoyu ye stvorennya regulyarnih shabloniv yaki dostatno legko virishiti Randomizovanij algoritm Prima source source source source source source Animaciya generaciyi labirintu rozmirom 30 na 20 za dopomogoyu algoritmu Prima Cej algoritm ye randomizovanoyu versiyeyu algoritmu Prima Pochnit z sitki povnoyi stin Viberit klitinku poznachte yiyi yak chastinu labirintu Dodajte stinki klitinki do spisku stin U toj chas yak u spisku ye stini Viberit vipadkovu stinu zi spisku Yaksho vidvidano lishe odnu z dvoh klitin yaki rozdilyaye stina Zrobit stinu prohodom i poznachte nevidvidanu klitinku yak chastinu labirintu Dodajte susidni stini klitinki do spisku stin Vidalit stinu zi spisku Yak pravilo vidnosno legko znajti shlyah do startovoyi klitini ale vazhko znajti shlyah de nebud she Zauvazhimo sho prosto zapusk klasichnogo algoritmu Prima na grafi z vipadkovimi relyefnimi vagami stvorit labirinti stilistichno identichni algoritmu Kruskala tomu sho voni obidva ye algoritmami dlya pobuduvannya minimalnogo kistyakovogo dereva Zamist cogo cej algoritm vvodit stilistichnu variaciyu oskilki rebra roztashovani blizhche do pochatkovoyi tochki mayut menshu efektivnu vagu Modifikovana versiya Hocha klasichnij algoritm Prima zberigaye spisok reber dlya generaciyi labirintiv mozhna zamist cogo zberegti spisok susidnih klitin Yaksho vipadkovo obrana klitina maye dekilka reber yaki z yednuyut yiyi z labirintom sho isnuye viberit odne z cih reber vipadkovim chinom Ce yak pravilo bude trohi bilshe nizh vishezaznachena versiya na osnovi reber Algoritm Vilsona Vsi pererahovani vishe algoritmi mayut riznogo rodu zmishennya poshuk u glibinu zmisheno u bik dovgih koridoriv todi yak algoritmi Kruskala Prima zmisheni do bagatoyi kilkosti korotkih tupikiv Z inshogo boku algoritm Vilsona generuye nezmishenu vibirku z diskretnim rivnomirnim rozpodilom nad usima labirintami vikoristovuyuchi vipadkovij poshuk z viklyuchennyam ciklichnosti Rozpochnemo algoritm inicializuyuchi labirint odniyeyu dovilno vibranoyu klitinkoyu Potim mi pochinayemo z novoyi klitini obranoyi dovilno i vikonuyemo vipadkovij poshuk poki ne distanemosya do klitini yaka vzhe znahoditsya v labirinti odnak yaksho v bud yakij tochci vipadkovij poshuk dosyagne svogo shlyahu utvoryuyuchi petlyu mi viklyuchayemo cikl z shlyahu pered tim yak prodovzhiti Koli shlyah dohodit do labirintu mi dodayemo jogo do labirintu Potim mi vikonuyemo inshij vipadkovij poshuk z viklyuchennyam ciklichnosti z inshoyi dovilnoyi pochatkovoyi klitinki povtoryuyuchi poki vsi klitini ne budut zapovneni Cya procedura zalishayetsya neuperedzhenoyu nezalezhno vid togo yakij metod mi vikoristovuyemo dlya dovilnogo viboru startovih klitinok Otzhe mi zavzhdi mozhemo vibrati dlya prostoti pershu nezapovnenu klitinku skazhimo zliva napravo zverhu vniz Metod rekursivnogo podilu Ilyustraciya rekursivnogo podilu pochatkova kamera podil dvoma stinkami otvori u stinkah prodovzhennya pidpodilu zavershennyakrok 1 krok 2 krok 3 krok 4 krok 5 Labirinti mozhut buti stvoreni za dopomogoyu rekursivnogo podilu algoritmu yakij pracyuye nastupnim chinom Pochnit z prostoru labirintu bez stin Nazvemo ce kameroyu Rozdilit kameru vipadkovo roztashovanoyu stinkoyu abo dekilkoma stinami de kozhna stina mistit vipadkovo roztashovanij prohidnij otvir Potim rekursivno povtorit proces na pidkamerah poki vsi kameri ne budut minimalnimi Cej metod prizvodit do togo sho labirinti z dovgimi pryamimi stinami peretinayut yihnij prostir sho polegshuye pereglyad yaki dilyanki slid unikati Rekursivne stvorennya labirintu Napriklad u pryamokutnomu labirinti buduyut u vipadkovih tochkah dvi stini perpendikulyarni odna odnij Ci dvi stini dilyat veliku kameru na chotiri menshi kameri rozdileni chotirma stinami Vibirayut vipadkovo tri z chotiroh stin i vidkrivayut po odnomu otvoru shirinoyu z klitinku u vipadkovij tochci v kozhnij z troh Prodovzhuyut takim chinom rekursivno poki kozhna kamera ne maye shirinu odniyeyi klitini v odnomu z dvoh napryamkiv Prosti algoritmi 3D versiya algoritmu Prima Vertikalni shari poznacheni vid 1 do 4 znizu vgoru Shodi vgoru poznacheni simvolom shodi vniz yak i shodi vgoru vniz yak x Vihidnij kod vklyucheno do opisu zobrazhennya Isnuyut inshi algoritmi yaki vimagayut dostatno pam yati dlya zberigannya odniyeyi liniyi 2D labirintu abo odniyeyi ploshini 3D labirintu Algoritm Ellera zapobigaye petlyam zberigayuchi yaki klitini v potochnomu ryadku z yednani cherez klitinki poperednih ryadkiv i nikoli ne vidalyaye stinki mizh bud yakimi dvoma vzhe z yednanimi klitinami Algoritm bichnogo namotuvannya pochinayetsya z vidkritogo prohodu po vsomu verhnomu ryadku a nastupni ryadki skladayutsya z korotshih gorizontalnih prohodiv z odnim vklyuchennyam do prohodu vishe Algoritm bichnogo namotuvannya trivialno virishuyetsya znizu vgoru tomu sho vin ne maye vishidnih kinciv Vrahovuyuchi pochatkovu shirinu obidva algoritmi stvoryuyut idealni labirinti neobmezhenoyi visoti Bilshist algoritmiv generaciyi labirintiv vimagayut zberezhennya zv yazkiv mizh klitinami vseredini neyi shob kincevij rezultat buv rozv yazuvanim Prote mozhut buti stvoreni dopustimi prosto pov yazani labirinti nezalezhno fokusuyuchis na kozhnij klitini Binarnij derevuvatij labirint ce standartnij ortogonalnij labirint de kozhna klitina zavzhdi maye prohid sho vede livoruch abo livoruch ale nikoli ne obidva Shob stvoriti binarnij derevuvatij labirint dlya kozhnoyi klitinki kinte monetku shob virishiti chi slid dodati prohid sho vede vgoru abo vlivo Zavzhdi vibirajte toj samij napryamok dlya klitin na mezhi a kincevij rezultat bude validnij prosto pov yazanim labirintom yakij viglyadaye yak binarne derevo pri comu verhnij livij kut bude jogo korenem Yak i v algoritmi bichnogo namotuvannya binarnij labirint dereva ne maye mertvih kinciv u napryamkah zmishennya Pov yazana forma peregortannya moneti dlya kozhnoyi klitinki polyagaye u stvorenni zobrazhennya za dopomogoyu vipadkovogo poyednannya simvoliv vpered kosih ris i zvorotnih risok Ce ne generuye validnij prosto pov yazanij labirint a skorishe vibir zamknutih petel i odnokursovih urivkiv posibnik dlya Commodore 64 predstavlyaye programu movoyu BASIC yaka vikoristovuye cej algoritm ale vikoristovuye grafichni simvoli en diagonalnoyi liniyi zamist bilsh gladkogo grafichnogo viglyadu Algoritmi klitinnogo avtomataDeyaki tipi klitinnih avtomativ mozhna vikoristovuvati dlya stvorennya labirintiv Dva klitinnih avtomati vidomih yak Maze i Mazectric mayut linijki B3 S12345 i B3 S1234 U pershomu ce oznachaye sho klitini vizhivayut z odniyeyi pokolinnya do nastupnogo yaksho u nih ye prinajmni odin i ne bilshe p yati susidiv Mura V ostannomu ce oznachaye sho klitini vizhivayut yaksho mayut vid odnogo do chotiroh susidiv Yaksho klitina maye rivno troh susidiv vona narodzhuyetsya Ce shozhe na avtomat gra zhittya v tomu sho strukturi yaki ne mayut zhivoyi klitini sho prilyagaye do 1 4 abo 5 inshih zhivih klitin u bud yakomu pokolinni budut povoditisya identichno do neyi Odnak dlya velikih modelej vin povoditisya zovsim inakshe nizh v zhitti dlya velikih modelej vin povoditisya zovsim inakshe nizh avtomat gra zhittya Dlya vipadkovogo startovogo shablonu ci klitinni avtomati sho generuyut labirint peretvoryatsya na skladni labirinti z chitko viznachenimi stinami sho vikladayut koridori Mazecetric sho maye pravilo B3 S1234 maye tendenciyu generuvati dovshi i pryami koridori u porivnyanni z Maze z pravilom B3 S12345 Oskilki ci pravila klitinnogo avtomata ye determinovanimi kozhen zgenerovanij labirint odnoznachno viznachayetsya jogo vipadkovim pochatkovim shablonom Ce ye suttyevim nedolikom oskilki labirinti mayut tendenciyu buti vidnosno peredbachuvanimi Yak i deyaki z opisanih vishe metodiv teoriyi grafiv ci klitinni avtomati zazvichaj generuyut labirinti z odnogo pochatkovogo shablonu otzhe yak pravilo vidnosno legko znajti shlyah do startovoyi klitini ale vazhche znajti shlyah v inshomu misci Priklad kodu movoyu PythonPriklad realizaciyi variantu algoritmu Prima movoyu Python Vishe zaznachenij algoritm Prima pochinayetsya z sitki povnoyi stin i roste po odnomu komponentu prohidnih plitok U comu prikladi mi pochinayemo z vidkritoyi sitki i rostom kilkoh komponentiv stin Cej algoritm pracyuye shlyahom stvorennya n shilnosti ostroviv dovzhinoyu p skladnist Ostriv stvoryuyetsya viborom vipadkovoyi pochatkovoyi tochki z neparnimi koordinatami potim vibirayetsya vipadkovij napryamok Yaksho klitina na dvoh krokah vilna u napryamku to v comu napryamku dodayut stinu na odnomu kroci i na dva kroki Proces povtoryuyetsya dlya n krokiv dlya cogo ostrova Stvoryuyutsya ostrovi n i p virazhayutsya yak float dlya pristosuvannya yih do rozmiru labirintu Z nizkoyu skladnistyu ostrovi duzhe mali i labirint legko virishiti Z nizkoyu shilnistyu labirint maye bilshe velikih porozhnih kimnat import numpy from numpy random import randint as rand import matplotlib pyplot as pyplot def maze width 81 height 51 complexity 75 density 75 Tilki neparni formi shape height 2 2 1 width 2 2 1 Vidregulyuyemo skladnist i shilnist shodo rozmiru labirintu complexity int complexity 5 shape 0 shape 1 kilkist komponentiv density int density shape 0 2 shape 1 2 rozmir komponentiv Pobuduyemo spravzhnij labirint Z numpy zeros shape dtype bool Zapovnyuyemo kordoni Z 0 Z 1 1 Z 0 Z 1 1 Zrobimo prohodi for i in range density x y rand 0 shape 1 2 2 rand 0 shape 0 2 2 vibirayemo vipadkovu poziciyu Z y x 1 for j in range complexity neighbours if x gt 1 neighbours append y x 2 if x lt shape 1 2 neighbours append y x 2 if y gt 1 neighbours append y 2 x if y lt shape 0 2 neighbours append y 2 x if len neighbours y x neighbours rand 0 len neighbours 1 if Z y x 0 Z y x 1 Z y y y 2 x x x 2 1 x y x y return Z pyplot figure figsize 10 5 pyplot imshow maze 80 40 cmap pyplot cm binary interpolation nearest pyplot xticks pyplot yticks pyplot show Priklad kodu movoyu JavaPriklad realizaciyi algoritmu randomizovanogo poshuku u glibinu RandomizedDFS java package de amr maze simple import java util ArrayDeque import java util BitSet import java util Deque import java util Random Stvoryuye labirint z grafu sitki vikoristovuyuchi randomizovanij poshuk u glibinu author Armin Reichert public class RandomizedDFS public static void main String args Vikoristannya rekursivnoyi proceduri System out println maze 10 10 0 0 true Vikoristannya nerekursivnoyi proceduri System out println maze 10 10 0 0 false Povertaye labirint zadanogo rozmiru pochatkovoyi generaciyi v danij poziciyi sitki public static Grid maze int numCols int numRows int startCol int startRow boolean recursive Grid grid new Grid numCols numRows BitSet visited new BitSet numCols numRows if recursive traverseRecursive grid grid vertex startCol startRow visited else traverseUsingStack grid grid vertex startCol startRow visited return grid Rekursivna procedura sho prohodit cherez sitku i stvoryuye labirint kistyakove derevo private static void traverseRecursive Grid grid int v BitSet visited visited set v for int dir unvisitedDir grid v visited dir 1 dir unvisitedDir grid v visited grid addEdge v dir traverseRecursive grid grid neighbor v dir visited Nerekursivna procedura sho prohodit cherez sitku i stvoryuye labirint kistyakove derevo private static void traverseUsingStack Grid grid int v BitSet visited Deque lt Integer gt stack new ArrayDeque lt gt visited set v stack push v while stack isEmpty int dir unvisitedDir grid v visited if dir 1 int neighbor grid neighbor v dir grid addEdge v dir visited set neighbor stack push neighbor v neighbor else v stack pop Povertaye vipadkovij napryamok nevidomomu susidu abo 1 private static int unvisitedDir Grid grid int v BitSet visited int candidates new int 4 int numCandidates 0 for int dir new int Grid NORTH Grid EAST Grid SOUTH Grid WEST int neighbor grid neighbor v dir if neighbor Grid NO VERTEX amp amp visited get neighbor candidates numCandidates dir return numCandidates 0 1 candidates new Random nextInt numCandidates Grid java package de amr maze simple import java util BitSet Realizaciya sitki grafu Nabir reber predstavlenij odnim naborom bitiv public class Grid Napryamki public static final int NORTH 0 EAST 1 SOUTH 2 WEST 3 Protilezhni napryamki public static final int OPPOSITE SOUTH WEST NORTH EAST Napryamni vektori public static final int X 0 1 0 1 public static final int Y 1 0 1 0 Indeks ne predstavlyaye vershini public static final int NO VERTEX 1 Kilkist stovpciv sitki public final int numCols Kilkist ryadkiv sitki public final int numRows private BitSet edges Indeks bitovogo naboru rebra sho zalishaye vershinu v do napryamku dir private int bit int v int dir return 4 v dir Stvoryuye porozhnyu sitku zadanogo rozmiru public Grid int numCols int numRows this numCols numCols this numRows numRows edges new BitSet numCols numRows 4 Vershina v kolonci col i v ryadku row public int vertex int col int row return numCols row col Stovpchik vershini v public int col int v return v numCols Ryadok vershini v public int row int v return v numCols Povertaye kilkist neoriyentovanih reber public int numEdges return edges cardinality 2 Dodaye rebro z vershini v do napryamku dir public void addEdge int v int dir edges set bit v dir edges set bit neighbor v dir OPPOSITE dir Vkazuye chi rebro vid vershini v do napryamku dir isnuye public boolean hasEdge int v int dir return edges get bit v dir Povertaye susida vershini v do napryamku dir abo NO VERTEX public int neighbor int v int dir int col col v X dir row row v Y dir return col gt 0 amp amp col lt numCols amp amp row gt 0 amp amp row lt numRows vertex col row NO VERTEX Povertaye tekstove predstavlennya ciyeyi sitki Override public String toString StringBuilder sb new StringBuilder for int row 0 row lt numRows row for int col 0 col lt numCols col sb append String format 2d 2d row col sb append col lt numCols 1 amp amp hasEdge vertex col row EAST if row lt numRows 1 sb append n for int col 0 col lt numCols col sb append hasEdge vertex col row SOUTH sb append n sb append String format n n d cols d rows d edges n numCols numRows numEdges return sb toString Priklad kodu movoyu SNizhche navedeno priklad algoritmu poshuku v glibinu dlya stvorennya labirintu movoyu S Kod napisano Yacekom Vichorekom include lt stdio h gt include lt stdlib h gt include lt time h gt include lt string h gt typedef struct int x y Poziciya vuzla mala vitrata pam yati ale spriyaye shvidshomu generuvannyu void parent Pokazhchik na batkivskij vuzol char c Simvol dlya vidobrazhennya char dirs Napryamki yaki she ne viznacheni Node Node nodes Masiv vuzliv int width height Rozmiri labirintu int init int i j Node n Vidilennya pam yati dlya labirintu nodes calloc width height sizeof Node if nodes NULL return 1 Vstanovlennya vazhlivih vuzliv for i 0 i lt width i for j 0 j lt height j n nodes i j width if i j 2 n gt x i n gt y j n gt dirs 15 Pripustimo sho vsi napryamki mozhna dosliditi vstanovleno 4 najmolodshih biti n gt c else n gt c Dodayemo stinki mizh vuzlami return 0 Node link Node n Pidklyuchayemo vuzol do vipadkovogo susida yaksho mozhlivo i povertayemo adresu nastupnogo vuzla yakij slid vidvidati int x y char dir Node dest Nichogo ne mozhna zrobiti yaksho zadano nulovij pokazhchik vihid if n NULL return NULL Poki ye napryamki dosi neviznacheni while n gt dirs Dovilno vibirayetsya odin napryamok dir 1 lt lt rand 4 Yaksho jogo vzhe bulo doslidzheno sprobujte she raz if n gt dirs amp dir continue Poznachimo napryamok yak doslidzhenij n gt dirs amp dir Zalezhno vid obranogo napryamku switch dir Pereviryayemo chi mozhna jti pravoruch case 1 if n gt x 2 lt width x n gt x 2 y n gt y else continue break Pereviryayemo chi mozhna spustitisya case 2 if n gt y 2 lt height x n gt x y n gt y 2 else continue break Pereviryayemo chi mozhna jti livoruch case 4 if n gt x 2 gt 0 x n gt x 2 y n gt y else continue break Pereviryayemo chi mozhna pidnyatisya case 8 if n gt y 2 gt 0 x n gt x y n gt y 2 else continue break Otrimayemo vuzol priznachennya cherez pokazhchik sho robit rechi trohi shvidshimi dest nodes x y width Perekonayemosya sho vuzol priznachennya ne ye stinoyu if dest gt c Yaksho priznachennya vzhe ye zv yazanim vuzlom perervati if dest gt parent NULL continue V inshomu vipadku prijnyati vuzol dest gt parent n Vidalennya stinki mizh vuzlami nodes n gt x x n gt x 2 n gt y y n gt y 2 width c Povernennya adresi dochirnogo vuzla return dest Yaksho nichogo bilshe tut ne mozhna zrobiti povernemo batkivsku adresu return n gt parent void draw int i j Vihidni dani labirintu do terminalu nichogo osoblivogo for i 0 i lt height i for j 0 j lt width j printf c nodes j i width c printf n int main int argc char argv Node start last Perevirka kilkosti argumentiv if argc lt 3 fprintf stderr s please specify maze dimensions n argv 0 exit 1 Chitannya rozmiriv labirintu z argumentiv komandnogo ryadka if sscanf argv 1 d amp width sscanf argv 2 d amp height lt 2 fprintf stderr s invalid maze size value n argv 0 exit 1 Dozvolyayemo lishe neparni rozmiri if width 2 height 2 fprintf stderr s dimensions must be odd n argv 0 exit 1 Ne dopuskayemo vid yemnih rozmiriv if width lt 0 height lt 0 fprintf stderr s dimensions must be greater than 0 n argv 0 exit 1 Nasinnij vipadkovij generator srand time NULL Inicializaciya labirintu if init fprintf stderr s out of memory n argv 0 exit 1 Vstanovlennya pochatkovogo vuzla start nodes 1 width start gt parent start last start Pidklyuchayemo vuzli do tih pir poki ne bude dosyagnutij pochatkovij vuzol i ne bude pokinutim while last link last start draw Div takozhAlgoritm rozv yazuvannya labirintiv en PrimitkiVilson Devid Bryus 22 24 Travnya 1996 Simpozium z teoriyi obchislen Filadelfiya ACM s 296 303 doi 10 1145 237814 237880 ISBN 0 89791 785 5 Arhiv originalu PDF za 14 travnya 2013 Procitovano 30 kvitnya 2019 Dzhemis Bak 29 Grudnya 2010 Arhiv originalu za 28 kvitnya 2019 Procitovano 30 kvitnya 2019 Dzhemis Bak 3 Lyutogo 2011 Arhiv originalu za 3 travnya 2019 Procitovano 30 kvitnya 2019 Nathaniel Johnston ta in 21 Serpnya 2010 LifeWiki Arhiv originalu za 14 grudnya 2013 Procitovano 1 Bereznya 2011 PosilannyaDumajte pro labirint algoritmi labirintu 6 kvitnya 2019 u Wayback Machine podrobici shodo cih ta inshih algoritmiv generaciyi labirintu Dzhemis Bak HTML 5 Prezentaciya z demo versiyami algoritmiv stvorennya labirintiv 28 kvitnya 2019 u Wayback Machine Vizualizaciya generuvannya labirintiv 13 lyutogo 2020 u Wayback Machine Realizaciya movoyu Java algoritmu Prima 24 kvitnya 2019 u Wayback Machine Realizaciya algoritmu poshuku v glibinu dlya stvorennya labirintu 26 kvitnya 2019 u Wayback Machine dekilkoma movami v Rosetta Code Armin Rejhert 34 algoritmi stvorennya labirintu movoyu Java 8 z demo dodatkami 12 chervnya 2018 u Wayback Machine CADforum algoritm stvorennya labirintu v VisualLISP 14 veresnya 2018 u Wayback Machine Kodovij viklik 10 1 Generator labirintiv z p5 js Chastina 1 Algoritm stvorennya labirintu v JavaScript z p5 25 chervnya 2020 u Wayback Machine Generator labirintiv Charlza Bonda COMPUTE Zhurnal Gruden 1981