Skip to content

Commit d09710e

Browse files
committed
revert
1 parent 0f146f5 commit d09710e

15 files changed

+2149
-58
lines changed

src/Main/Algorithms/Maze/Kruskal.js

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import {
2+
rand,
3+
changeClassName,
4+
delay,
5+
isHorizontalCut,
6+
} from "../../utilities/utilities";
7+
8+
/**
9+
* The time to be waited.
10+
*/
11+
var deltaTime;
12+
13+
/**
14+
* A function to generate a maze by Randomized Kruskal algorithm.
15+
* @param {Object} input The input of the algorithm.
16+
* @param {Array<Array<Object>>} input.grid The grid to be used.
17+
* @param {Number} input.maxRow The maximum row of the grid.
18+
* @param {Number} input.maxCol The maximum column of the grid.
19+
* @param {Number} input.timeRatio The thime to be waited.
20+
* @param {Boolean} input.dark Whether currently is dark mode or not.
21+
*/
22+
const Kruskal = async (input) => {
23+
const { dark, grid, maxRow, maxCol, timeRatio } = input;
24+
deltaTime = timeRatio;
25+
26+
// initalize the following:
27+
// the closed set.
28+
let closed = [];
29+
30+
// the groups of set.
31+
// groups := {idx:set}
32+
let groups = new Map();
33+
34+
// the idx (based 0) of the group.
35+
let idx = 0;
36+
37+
// the boundary of the grid is the first group.
38+
let bound = [];
39+
for (let j = 0; j < maxCol; ++j) {
40+
let node = grid[0][j];
41+
if (!(node.isStart || node.isEnd)) {
42+
node.idx = idx;
43+
bound.push(node);
44+
}
45+
node = grid[maxRow - 1][j];
46+
if (!(node.isStart || node.isEnd)) {
47+
node.idx = idx;
48+
bound.push(node);
49+
}
50+
}
51+
52+
for (let i = 0; i < maxRow; ++i) {
53+
let node = grid[i][0];
54+
if (!(node.isStart || node.isEnd)) {
55+
node.idx = idx;
56+
bound.push(node);
57+
}
58+
node = grid[i][maxCol - 1];
59+
if (!(node.isStart || node.isEnd)) {
60+
node.idx = idx;
61+
bound.push(node);
62+
}
63+
}
64+
65+
// add the boundary to the closed set and the group.
66+
closed = closed.concat(bound);
67+
groups.set(idx++, bound);
68+
69+
/**
70+
* for each node,
71+
* if the row and column is divisible by 2
72+
* label them as a new group.
73+
* The final ids of the grid will be something like:
74+
* [[0, 0, 0, 0, 0, 0, 0]],
75+
* [[0,-1,-1,-1,-1,-1, 0]],
76+
* [[0,-1, 1,-1, 2,-1, 0]],
77+
* [[0,-1,-1,-1,-1,-1,-1]],
78+
* [[0, 0, 0, 0, 0, 0, 0]]
79+
*/
80+
for (let i = 2; i < maxRow - 2; i += 2) {
81+
for (let j = 2; j < maxCol - 2; j += 2) {
82+
const node = grid[i][j];
83+
if (!(node.isStart || node.isEnd)) {
84+
node.idx = idx;
85+
closed.push(node);
86+
groups.set(idx++, [node]);
87+
}
88+
}
89+
}
90+
91+
/**
92+
* For each node not near the nodes with non-negative idx:
93+
* add them to the closed set.
94+
* near mean the closed horizonal/vertical nodes,
95+
* not near means the closed diagonal nodes
96+
*/
97+
for (let i = 1; i < maxRow - 1; i += 2) {
98+
for (let j = 1; j < maxCol - 1; j += 2) {
99+
const node = grid[i][j];
100+
if (!(node.isStart || node.isEnd)) {
101+
closed.push(node);
102+
}
103+
}
104+
}
105+
106+
// get the nodes to be visited in the future.
107+
let open = grid.flat().filter((e) => !closed.includes(e));
108+
109+
// loop until the open set is empty.
110+
while (open.length !== 0) {
111+
// randomly determin this node should horizontally divide the sub-grid or not.
112+
const isHorizontal = isHorizontalCut(0, 0);
113+
114+
// ranodmly get a node from the open set.
115+
const node = open[rand(0, open.length - 1)];
116+
117+
// remove the node from the open set.
118+
open = open.filter((e) => e !== node);
119+
120+
// horizontal cut.
121+
if (isHorizontal) {
122+
const nodeA = grid[node.row][node.col - 1];
123+
const nodeB = grid[node.row][node.col + 1];
124+
125+
// if horizontal cut is not allowed, try vertical cut.
126+
if (!(await combineGroup(dark, node, nodeA, nodeB, groups))) {
127+
const nodeA = grid[node.row - 1][node.col];
128+
const nodeB = grid[node.row + 1][node.col];
129+
// vertical cut
130+
await combineGroup(dark, node, nodeA, nodeB, groups);
131+
}
132+
} else {
133+
// vertical cut.
134+
const nodeA = grid[node.row - 1][node.col];
135+
const nodeB = grid[node.row + 1][node.col];
136+
137+
// if vertical cut is not allowed, try horizontal cut.
138+
if (!(await combineGroup(dark, node, nodeA, nodeB, groups))) {
139+
const nodeA = grid[node.row][node.col - 1];
140+
const nodeB = grid[node.row][node.col + 1];
141+
// horizontal cut
142+
await combineGroup(dark, node, nodeA, nodeB, groups);
143+
}
144+
}
145+
}
146+
};
147+
148+
/**
149+
* A function to combine the vertical/horizontal nodes.
150+
* @param {Boolean} dark Whether currently is dark mode or not.
151+
* @param {Object} node The select node.
152+
* @param {Object} nodeA The nearest node.
153+
* @param {Object} nodeB The nearest node.
154+
* @param {Map} groups The map of groups.
155+
* @returns {Promise} Whether this cut is allowed or not.
156+
*/
157+
const combineGroup = async (dark, node, nodeA, nodeB, groups) => {
158+
// if idx of the nearest nodes are the same,
159+
// there will be the closed loop,
160+
// thus return false
161+
if (nodeA.idx === nodeB.idx) {
162+
return false;
163+
}
164+
165+
// get the set 1 of nodeA.
166+
let set1 = groups.get(nodeA.idx);
167+
168+
// get the set 2 of nodeB.
169+
let set2 = groups.get(nodeB.idx);
170+
171+
// delete the group of nodeB.
172+
groups.delete(nodeB.idx);
173+
174+
// change the group that nodeB in to the idx of the nodeA.
175+
for (let i = 0; i < set2.length; ++i) {
176+
set2[i].idx = nodeA.idx;
177+
}
178+
179+
// update the group of nodeA belongs to.
180+
groups.set(nodeA.idx, set1.concat(set2));
181+
node.idx = nodeA.idx;
182+
183+
// change node, nodeA, nodeB to a wall node.
184+
if (!(node.isStart || node.isEnd)) {
185+
node.isWall = true;
186+
}
187+
if (!(nodeA.isStart || nodeA.isEnd)) {
188+
nodeA.isWall = true;
189+
}
190+
if (!(nodeB.isStart || nodeB.isEnd)) {
191+
nodeB.isWall = true;
192+
}
193+
changeClassName(dark, node);
194+
changeClassName(dark, nodeB);
195+
changeClassName(dark, nodeA);
196+
await delay(5 * deltaTime);
197+
// return true because the cut is allowed.
198+
return true;
199+
};
200+
201+
export default Kruskal;

src/Main/Algorithms/Maze/Mazes.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { delay, changeClassName } from "../../utilities/utilities";
2+
import NoWalls from "./NoWalls";
3+
import SimpleRandomWalls from "./SimpleRandomWalls";
4+
import PrimMaze from "./Prim";
5+
import Kruskal from "./Kruskal";
6+
import RecursiveDivisionMaze from "./RecursiveDivision";
7+
import RecursiveBacktrackingMaze from "./RecursiveBacktracking";
8+
9+
/**
10+
* A function to reverse the grid
11+
* @param {Object} input The input of the function.
12+
* @param {Array<Array<Object>>} input.grid The grid to be used.
13+
* @param {Boolean} input.dark Whether currently is dark mode or not.
14+
* @param {Number} input.maxRow The maximum row of the grid.
15+
* @param {Number} input.maxCol The maximum column of the grid.
16+
* @param {Number} input.timeRatio The time to be waited.
17+
*/
18+
const Reverse = async (input) => {
19+
const {dark, grid, maxRow, maxCol, timeRatio} = input
20+
for (let row = 0; row < maxRow; ++row) {
21+
for (let col = 0; col < maxCol; ++col) {
22+
setTimeout(() => {
23+
let node = grid[row][col];
24+
if (!(node.isStart || node.isEnd)) {
25+
node.isWall = !node.isWall;
26+
changeClassName(dark, node);
27+
}
28+
}, Math.max(timeRatio, 30) * (row + col));
29+
}
30+
}
31+
await delay(Math.max(timeRatio, 30) * (maxRow + maxCol));
32+
};
33+
34+
/**
35+
* A function to add boundary wall to the grid.
36+
* @param {Object} input The input of the function.
37+
* @param {Array<Array<Object>>} input.grid The grid to be used.
38+
* @param {Boolean} input.dark Whether currently is dark mode or not.
39+
* @param {Number} input.maxRow The maximum row of the grid.
40+
* @param {Number} input.maxCol The maximum column of the grid.
41+
* @param {Number} input.timeRatio The time to be waited.
42+
*/
43+
const Boundary = async (input) => {
44+
const {dark, grid, maxRow, maxCol, timeRatio} = input
45+
for (let i = 0; i < maxRow; ++i) {
46+
setTimeout(() => {
47+
let node = grid[i][0];
48+
if (!(node.isStart || node.isEnd)) {
49+
node.isWall = true;
50+
changeClassName(dark, node);
51+
}
52+
node = grid[i][maxCol - 1];
53+
if (!(node.isStart || node.isEnd)) {
54+
node.isWall = true;
55+
changeClassName(dark, node);
56+
}
57+
}, timeRatio * i);
58+
}
59+
for (let j = 0; j < maxCol; ++j) {
60+
setTimeout(() => {
61+
let node = grid[0][j];
62+
if (!(node.isStart || node.isEnd)) {
63+
node.isWall = true;
64+
changeClassName(dark, node);
65+
}
66+
node = grid[maxRow - 1][j];
67+
if (!(node.isStart || node.isEnd)) {
68+
node.isWall = true;
69+
changeClassName(dark, node);
70+
}
71+
}, timeRatio * j);
72+
}
73+
await delay(timeRatio * Math.max(maxRow, maxCol));
74+
};
75+
76+
/**
77+
* export a object that contains the maze generating algorithm
78+
*/
79+
export default {
80+
"Recursive Backtracking": async (input) => {
81+
await NoWalls(input);
82+
await Reverse(input);
83+
await RecursiveBacktrackingMaze(input);
84+
},
85+
"Recursive Division": async (input) => {
86+
await NoWalls(input);
87+
await Boundary(input);
88+
await RecursiveDivisionMaze(input);
89+
},
90+
"Prim's Algorithm": async (input) => {
91+
await NoWalls(input);
92+
await Reverse(input);
93+
await PrimMaze(input);
94+
},
95+
"Simple Random Walls": async (input) => {
96+
await NoWalls(input);
97+
SimpleRandomWalls(input);
98+
},
99+
"Kruskal's Algorithm": async (input) => {
100+
await NoWalls(input);
101+
await Boundary(input);
102+
await Kruskal(input);
103+
},
104+
"No Walls": NoWalls,
105+
};

0 commit comments

Comments
 (0)