89 |
90 |
91 | Drag source and destination to change them
92 |
93 | Select the current edit mode using the radio buttons provided
94 | To edit the grid simply click or drag over the cells you want to add/remove a wall or add a weight to
95 |
96 |
Symbols and their meanings:
97 |
98 |
99 |
100 |
Symbol
101 |
Meaning
102 |
103 |
104 |
105 |
106 |
Source Cell
107 |
108 |
109 |
110 |
Destination Cell
111 |
112 |
113 |
114 |
Explored Cell
115 |
116 |
117 |
118 |
Unvisited Cell
119 |
120 |
121 |
122 |
On the shortest path from source to destination
123 |
124 |
125 |
126 |
Blocked Cell
127 |
128 |
129 |
fitness_center
130 |
Weight of cell, larger size indicates higher weight
131 |
132 |
133 |
navigation
134 |
Start simulation
135 |
136 |
137 |
refresh
138 |
Reset simulation
139 |
140 |
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/src/app/grid/maze-partation.ts:
--------------------------------------------------------------------------------
1 | import { Node } from './node.model';
2 | import { partition } from 'rxjs';
3 |
4 | interface Hole {
5 | x: number;
6 | y: number;
7 | };
8 |
9 | const timeDelay = 40;
10 |
11 | export class MazePartation {
12 | private grid: Node[][];
13 |
14 | delay(ms) {
15 | return new Promise(resolve => setTimeout(resolve, ms));
16 | }
17 |
18 | // generates a random value in the range [minVal, maxVal]
19 | random(minVal: number, maxVal: number) {
20 | return minVal + Math.floor(Math.random() * (maxVal - minVal));
21 | }
22 |
23 | validCordinate(x: number, y: number) {
24 | return (x >= 0 && x < this.grid.length && y >= 0 && y < this.grid[0].length);
25 | }
26 |
27 | async partition(startX: number, endX: number, startY: number, endY: number) {
28 | const vRange = endX - startX - 1;
29 | let hCut: number;
30 | if (vRange >= 1) {
31 | hCut = this.random(startX + 1, endX - 1);
32 | // make the horizontal cut
33 | // check if first cell doesn't block a gap in a vertical wall
34 | // only then build a wall
35 | if (!this.validCordinate(hCut, startY - 1) || this.grid[hCut][startY - 1].isBlocked) {
36 | this.grid[hCut][startY].isBlocked = true;
37 | await this.delay(timeDelay);
38 | }
39 |
40 | for (let j = startY + 1; j <= endY - 1; ++j) {
41 | this.grid[hCut][j].isBlocked = true;
42 | await this.delay(timeDelay);
43 | }
44 |
45 | // check if last cell doesn't block a gap in a vertical wall
46 | // only then build a wall
47 | if (!this.validCordinate(hCut, endY + 1) || this.grid[hCut][endY + 1].isBlocked) {
48 | this.grid[hCut][endY].isBlocked = true;
49 | await this.delay(timeDelay);
50 | }
51 | }
52 |
53 | let vCut: number;
54 | const hRange = endY - startY - 1;
55 | if (hRange >= 1) {
56 | vCut = this.random(startY + 1, endY - 1);
57 | // make the vertical cut
58 | // check if first cell doesn't block a gap in a horizontal wall
59 | // only then build a wall
60 | if (!this.validCordinate(startX - 1, vCut) || this.grid[startX - 1][vCut].isBlocked) {
61 | this.grid[startX][vCut].isBlocked = true;
62 | await this.delay(timeDelay);
63 | }
64 |
65 | for (let i = startX + 1; i < endX; ++i) {
66 | this.grid[i][vCut].isBlocked = true;
67 | await this.delay(timeDelay);
68 | }
69 |
70 | // check if last cell doesn't block a gap in a horizontal wall
71 | // only then build a wall
72 | if (!this.validCordinate(endX + 1, vCut) || this.grid[endX + 1][vCut].isBlocked) {
73 | this.grid[endX][vCut].isBlocked = true;
74 | await this.delay(timeDelay);
75 | }
76 | }
77 |
78 | // Base condition
79 | // if you made 0 cuts, return
80 | if (vRange < 1 && hRange < 1) {
81 | return ;
82 | }
83 |
84 | // if you made 1 cut, need to unblock one cell and recurse
85 | if (vRange < 1) {
86 | // this means hRange >=1 and we only made a vertical cut
87 | // generate a number to make a hole in the vertical wall
88 | const hole = this.random(startX, endX);
89 | this.grid[hole][vCut].isBlocked = false;
90 | this.partition(startX, endX, startY, vCut - 1);
91 | this.partition(startX, endX, vCut + 1, endY);
92 | return ;
93 | }
94 |
95 | if (hRange < 1) {
96 | // this means vRange >=1 and we only made a horizontal cut
97 | // generate a number to make a hole in the horizontal wall
98 | const hole = this.random(startY, endY);
99 | this.grid[hCut][hole].isBlocked = false;
100 | this.partition(startX, hCut -1, startY, endY);
101 | this.partition(hCut + 1, endX, startY, endY);
102 | return ;
103 | }
104 |
105 | // we made a horizontal and vertical cut
106 | // need to generate 3 holes
107 | // to do that, we make holes in all four segments of walls
108 | // and choose one to fill back
109 |
110 | let holes: Hole[] = [];
111 | // make 2 holes in vertical wall
112 | holes.push({
113 | x: this.random(startX, hCut - 1),
114 | y: vCut
115 | });
116 |
117 | holes.push({
118 | x: this.random(hCut + 1, endX),
119 | y: vCut
120 | });
121 |
122 | // make 2 holes in horizontal wall
123 | holes.push({
124 | x: hCut,
125 | y: this.random(startY, vCut - 1)
126 | });
127 | holes.push({
128 | x: hCut,
129 | y: this.random(vCut + 1, endY)
130 | });
131 |
132 | // choose an index to leave
133 | const leaveIndex = this.random(0, 3);
134 |
135 | for (let i = 0; i < 4; ++i) {
136 | if (i != leaveIndex) {
137 | await this.delay(timeDelay);
138 | this.grid[holes[i].x][holes[i].y].isBlocked = false;
139 | }
140 | }
141 |
142 | // recurse on 4 smaller parts
143 | this.partition(startX, hCut - 1, startY, vCut - 1);
144 | this.partition(startX, hCut - 1, vCut + 1, endY);
145 | this.partition(hCut + 1, endX, startY, vCut - 1);
146 | this.partition(hCut + 1, endX, vCut + 1, endY);
147 | }
148 |
149 | mazify(grid: Node[][]) {
150 | this.grid = grid;
151 | const height = grid.length;
152 | const width = grid[0].length;
153 |
154 | this.partition(0, height - 1, 0, width - 1);
155 | }
156 | }
157 |
--------------------------------------------------------------------------------