Maze Generation - Kruskal's Algorithm


https://courses.cs.washington.edu/courses/cse326/07su/prj2/kruskal.html

http://codehowtos.blogspot.com/2011/04/building-maze.html
Now, lets think of a maze as a grid of cells. For cells X and Y, you can reach from X to Y through the winding maze if there is a continuous route without walls between the two. So, lets start with a maze in which each cell has all its 4 walls built. The goal is to get a maze in which there is exactly 1 way to get from any cell X to any cell Y. Now lets add the data structure into the image. We start with a Disjoint-set that has a group for each of the cells. Each turn we destroy a wall between 2 cells that belong to different groups. This way we open exactly 1 passage between each of the cells in one group to each in the other. We know we've finished when there is exactly one group in the set.

For those who like algorithms, this is actually an implementation of the Kruskal algorithm for finding Minimal Spanning Trees in graphs (if you take the grid cells as vertices and the walls as edges).
You can read more here: http://en.wikipedia.org/wiki/Kruskal%27s_algorithm
or you can look at some boos here: Books on Algorithms on Amazon

Maze createRandomMaze(int rows, int columns) { Maze maze = new Maze(rows, columns); // create all walls List<Wall> walls = maze.getAllInnerWalls(); // remove all the walls you can DisjointSet diset = new DisjointSet(rows*columns); while (diset.size() > 1) { int wallIndex = random.nextInt(walls.size()); int cell1 = walls.get(wallIndex).cell1; int cell2 = walls.get(wallIndex).cell2; if (diset.find(cell1) != diset.find(cell2)) { // we can remove the wall maze.removeWall(walls.get(wallIndex)); diset.join(cell1, cell2); } walls.remove(wallIndex); } return maze; }
http://www.algostructure.com/specials/maze.php
This algorithm is a randomized version of Kruskal's algorithm.
  1. Create a list of all walls, and create a set for each cell, each containing just that one cell.
  2. For each wall, in some random order:
    1. If the cells divided by this wall belong to distinct sets:
      1. Remove the current wall.
      2. Join the sets of the formerly divided cells.
There are several data structures that can be used to model the sets of cells. An efficient implementation using a disjoint-set data structure can perform each union and find operation on two sets in nearly constant amortized time, so the running time of this algorithm is essentially proportional to the number of walls available to the maze.
It matters little whether the list of walls is initially randomized or if a wall is randomly chosen from a nonrandom list, either way is just as easy to code.
 protected void generatePathways() {
  board = new int[width][height];
  int counter = 1;       
  
  for (int i = 0; i < width; i++){  //creates a 2d array and fills it with unique numbers for each index
   for (int j = 0; j < height; j++){
    board[i][j] = counter;
    counter++;
   }
  }
 
  final ArrayList<Wall> candidates = new ArrayList<Wall>(); //creates an array list because the number of walls changes with difficulty so the list may need to expand
  createListOfWalls(candidates);         // fills array list using the method
  
  while(!candidates.isEmpty()){     // While the list of walls is not empty                                              
   
   Wall currWall = extractWallFromCandidateSetRandomly(candidates); // grab a random wall
   int currX = currWall.getX();                                     // grab that wall's x coordinate
   int currY = currWall.getY();                                     // grab that wall's y coordinate
   int neighX = currWall.getNeighborX();                            // grab neighbor's x coordinate
   int neighY = currWall.getNeighborY();        // grab neighbor's y coordinate
   
   if (neighX >= 0 && neighY >= 0 && neighX < width && neighY < height){   // makes sure that the neighbor is in bounds
    if (board[currX][currY] != board[neighX][neighY]){                  // if the values of the current index and its neighbor are not equal
     changeNeighborValue(currWall, currX, currY, neighX, neighY);  // then update the neighbor's value to that of the current index
    }
   }
  }
 }
 
 /**
  * This method takes in the coordinates of a wall and its neighbor, deletes the wall
  * and then updates the board array so that the neighbor and all indices in the array
  * with the same value as the neighbor are changed to the value of the current index.
  * 
  * 
  * @param wall
  * @param currX
  * @param currY
  * @param neighX
  * @param neighY
  */
 private void changeNeighborValue(Wall wall, int currX, int currY, int neighX, int neighY){ // the integer values are passed because if you take them after the deletion of the wall then they change
  
  cells.deleteWall(wall); // self explanatory(deletes from both sides)
  
  int current = board[currX][currY];   //stores indices,
  int neighbor = board[neighX][neighY];
  
  for (int i = 0; i < width; i++){  // for all of the indices with the value of the neighbor, it changes them to the value of the current
   for (int j = 0; j < height; j++){
    if (board[i][j] == neighbor){
     board[i][j] = current;
    }
   }
  }
 }
  
 /**
  * Pick a random position in the list of candidates, remove the candidate from the list and return it
  * @param candidates
  * @return candidate from the list, randomly chosen
  */
 private Wall extractWallFromCandidateSetRandomly(ArrayList<Wall> candidates) {
  return candidates.remove(random.nextIntWithinInterval(0, candidates.size()-1)); 
 }
 /**
  * Simply goes through all the walls in the maze and adds them to a list if they are not borders.
  * @param walls
  */
 private void createListOfWalls(ArrayList<Wall> walls) {
  
  for (int i = 0; i < width; i++){
   for (int j = 0; j < height; j++){
    for (CardinalDirection cd : CardinalDirection.values()) {
     Wall wall = new Wall(i, j, cd);
     if (cells.canBreak(wall)){        // If the wall is not a border, it is added to the list
      walls.add(wall);
     }
    }
   }
  }
 }
https://github.com/njrahman/Maze/blob/master/src/generation/Wall.java

https://weblog.jamisbuck.org/2011/1/3/maze-generation-kruskal-s-algorithm.html
Kruskal’s algorithm is a method for producing a minimal spanning tree from a weighted graph. The algorithm I’ll cover here is actually a randomized version of Kruskal’s; the original works something like this:
  1. Throw all of the edges in the graph into a big burlap sack. (Or, you know, a set or something.)
  2. Pull out the edge with the lowest weight. If the edge connects two disjoint trees, join the trees. Otherwise, throw that edge away.
  3. Repeat until there are no more edges left.
The randomized algorithm just changes the second step, so that instead of pulling out the edge with the lowest weight, you remove an edge from the bag at random. Making that change, the algorithm now produces a fairly convincing maze.
Let’s walk through an example manually, to see how the process works in practice.

An example

For this example, I’ll use a simple 3×3 grid. I’ve assigned each cell a letter, indicating which set it belongs to.
ABC
DEF
GHI
The algorithm is straightforward: simply select an edge at random, and join the cells it connects if they are not already connected by a path. We can know if the cells are already connected if they are in the same set. So, let’s choose the edge between (2,2) and (2,3). The cells are in different sets, so we join the two into a single set and connect the cells:
ABC
DEF
GEI
Let’s do a few more passes of the algorithm, to get to the interesting part:
AAC
DEF
GEI
AAC
DEC
GEI
AAC
DEC
GEE
AAC
DEC
EEE
Here’s where things start to move fast. Note what happens when the edge between (2,1) and (2,2) is pulled from the bag:
AAC
DAC
AAA
The two trees, A and E, were joined into one set, A, implying that any cell in A is reachable from any other cell in A. Let’s try joining (1,2) and (1,3) now:
AAC
AAC
AAA
Now, consider the edges (1,1)–(1,2) and (1,2)–(2,2). Neither of these has been drawn from the bag yet. What would happen if one of them were? Well, in both cases, the cells on either side of the edge belong to the same set. Connecting the cells in either case would result in a cycle, so we discard the edge and try again.
After a one more pass, we’ll have:
AAA
AAA
AAA
The algorithm finishes when there are no more edges to consider (which, in this case, is when there is only a single set left). And the result is a perfect maze!

Implementation

Implementing Kruskal’s algorithm is straightforward, but for best results you need to find a very efficient way to join sets. If you do it like I illustrated above, assigning a set identifier to each cell, you’ll need to iterate on every merge, which will be expensive. Using trees to represent the sets is much faster, allowing you to merge sets efficiently simply by adding one tree as a subtree of the other. Testing whether two cells share a set is done by comparing the roots of their corresponding trees.
Once you have the tree data structure, the algorithm is extremely straightforward. Begin by initializing the grid (which will represent the maze itself), and the sets (one per cell):
1
2
grid = Array.new(height) { Array.new(width, 0) }
sets = Array.new(height) { Array.new(width) { Tree.new } }
Note that it would probably be more efficient to join the two representations, but I’ve split them apart for clarity.
Then, build the list of edges. Here I’m representing each edge as one of its end-points, and a direction:



Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts