@principal-ai/logo-component 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/MazeDemo.d.ts +12 -0
- package/dist/MazeDemo.js +376 -0
- package/dist/MazeDemoNew.d.ts +12 -0
- package/dist/MazeDemoNew.js +376 -0
- package/dist/SquareLogo.d.ts +9 -0
- package/dist/SquareLogo.js +57 -0
- package/dist/WreathLogo.d.ts +12 -0
- package/dist/WreathLogo.js +92 -0
- package/dist/esm/MazeDemo.js +339 -0
- package/dist/esm/MazeDemoNew.js +339 -0
- package/dist/esm/SquareLogo.js +50 -0
- package/dist/esm/WreathLogo.js +85 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/utils/mazeGenerator.js +266 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.esm.js +4 -0
- package/dist/index.js +9 -1
- package/dist/utils/mazeGenerator.d.ts +56 -0
- package/dist/utils/mazeGenerator.js +271 -0
- package/package.json +1 -1
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maze Generator using Recursive Backtracking
|
|
3
|
+
* Creates a perfect maze (exactly one path between any two cells)
|
|
4
|
+
*/
|
|
5
|
+
export class MazeGenerator {
|
|
6
|
+
constructor(rows, cols) {
|
|
7
|
+
this.rows = rows;
|
|
8
|
+
this.cols = cols;
|
|
9
|
+
this.grid = this.initializeGrid();
|
|
10
|
+
}
|
|
11
|
+
initializeGrid() {
|
|
12
|
+
const grid = [];
|
|
13
|
+
for (let row = 0; row < this.rows; row++) {
|
|
14
|
+
grid[row] = [];
|
|
15
|
+
for (let col = 0; col < this.cols; col++) {
|
|
16
|
+
grid[row][col] = {
|
|
17
|
+
row,
|
|
18
|
+
col,
|
|
19
|
+
visited: false,
|
|
20
|
+
walls: {
|
|
21
|
+
north: true,
|
|
22
|
+
south: true,
|
|
23
|
+
east: true,
|
|
24
|
+
west: true,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return grid;
|
|
30
|
+
}
|
|
31
|
+
getCell(row, col) {
|
|
32
|
+
if (row < 0 || row >= this.rows || col < 0 || col >= this.cols) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return this.grid[row][col];
|
|
36
|
+
}
|
|
37
|
+
getUnvisitedNeighbors(cell) {
|
|
38
|
+
const neighbors = [];
|
|
39
|
+
const { row, col } = cell;
|
|
40
|
+
// North
|
|
41
|
+
const north = this.getCell(row - 1, col);
|
|
42
|
+
if (north && !north.visited)
|
|
43
|
+
neighbors.push(north);
|
|
44
|
+
// South
|
|
45
|
+
const south = this.getCell(row + 1, col);
|
|
46
|
+
if (south && !south.visited)
|
|
47
|
+
neighbors.push(south);
|
|
48
|
+
// East
|
|
49
|
+
const east = this.getCell(row, col + 1);
|
|
50
|
+
if (east && !east.visited)
|
|
51
|
+
neighbors.push(east);
|
|
52
|
+
// West
|
|
53
|
+
const west = this.getCell(row, col - 1);
|
|
54
|
+
if (west && !west.visited)
|
|
55
|
+
neighbors.push(west);
|
|
56
|
+
return neighbors;
|
|
57
|
+
}
|
|
58
|
+
removeWallBetween(current, next) {
|
|
59
|
+
const rowDiff = current.row - next.row;
|
|
60
|
+
const colDiff = current.col - next.col;
|
|
61
|
+
// North
|
|
62
|
+
if (rowDiff === 1) {
|
|
63
|
+
current.walls.north = false;
|
|
64
|
+
next.walls.south = false;
|
|
65
|
+
}
|
|
66
|
+
// South
|
|
67
|
+
else if (rowDiff === -1) {
|
|
68
|
+
current.walls.south = false;
|
|
69
|
+
next.walls.north = false;
|
|
70
|
+
}
|
|
71
|
+
// East
|
|
72
|
+
else if (colDiff === -1) {
|
|
73
|
+
current.walls.east = false;
|
|
74
|
+
next.walls.west = false;
|
|
75
|
+
}
|
|
76
|
+
// West
|
|
77
|
+
else if (colDiff === 1) {
|
|
78
|
+
current.walls.west = false;
|
|
79
|
+
next.walls.east = false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
recursiveBacktrack(cell) {
|
|
83
|
+
cell.visited = true;
|
|
84
|
+
const neighbors = this.getUnvisitedNeighbors(cell);
|
|
85
|
+
// Shuffle neighbors for randomness
|
|
86
|
+
this.shuffle(neighbors);
|
|
87
|
+
for (const neighbor of neighbors) {
|
|
88
|
+
if (!neighbor.visited) {
|
|
89
|
+
this.removeWallBetween(cell, neighbor);
|
|
90
|
+
this.recursiveBacktrack(neighbor);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
shuffle(array) {
|
|
95
|
+
for (let i = array.length - 1; i > 0; i--) {
|
|
96
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
97
|
+
[array[i], array[j]] = [array[j], array[i]];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Generate the maze starting from a specific cell
|
|
102
|
+
*/
|
|
103
|
+
generate(startRow = 0, startCol = 0) {
|
|
104
|
+
const startCell = this.getCell(startRow, startCol);
|
|
105
|
+
if (startCell) {
|
|
106
|
+
this.recursiveBacktrack(startCell);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Convert the maze to wall segments for rendering
|
|
111
|
+
*/
|
|
112
|
+
getWalls() {
|
|
113
|
+
const horizontal = [];
|
|
114
|
+
const vertical = [];
|
|
115
|
+
// Convert cell walls to line segments
|
|
116
|
+
for (let row = 0; row < this.rows; row++) {
|
|
117
|
+
for (let col = 0; col < this.cols; col++) {
|
|
118
|
+
const cell = this.grid[row][col];
|
|
119
|
+
// North wall
|
|
120
|
+
if (cell.walls.north) {
|
|
121
|
+
// Check if we can extend an existing horizontal wall
|
|
122
|
+
const lastHWall = horizontal[horizontal.length - 1];
|
|
123
|
+
if (lastHWall && lastHWall[1] === row && lastHWall[2] === col) {
|
|
124
|
+
// Extend the wall
|
|
125
|
+
lastHWall[2] = col + 1;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
// Create new wall segment
|
|
129
|
+
horizontal.push([col, row, col + 1, row]);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// West wall
|
|
133
|
+
if (cell.walls.west) {
|
|
134
|
+
// Check if we can extend an existing vertical wall
|
|
135
|
+
const lastVWall = vertical[vertical.length - 1];
|
|
136
|
+
if (lastVWall && lastVWall[0] === col && lastVWall[3] === row) {
|
|
137
|
+
// Extend the wall
|
|
138
|
+
lastVWall[3] = row + 1;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// Create new wall segment
|
|
142
|
+
vertical.push([col, row, col, row + 1]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// South wall (only for bottom row)
|
|
146
|
+
if (row === this.rows - 1 && cell.walls.south) {
|
|
147
|
+
const lastHWall = horizontal[horizontal.length - 1];
|
|
148
|
+
if (lastHWall && lastHWall[1] === row + 1 && lastHWall[2] === col) {
|
|
149
|
+
lastHWall[2] = col + 1;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
horizontal.push([col, row + 1, col + 1, row + 1]);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// East wall (only for rightmost column)
|
|
156
|
+
if (col === this.cols - 1 && cell.walls.east) {
|
|
157
|
+
const lastVWall = vertical[vertical.length - 1];
|
|
158
|
+
if (lastVWall && lastVWall[0] === col + 1 && lastVWall[3] === row) {
|
|
159
|
+
lastVWall[3] = row + 1;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
vertical.push([col + 1, row, col + 1, row + 1]);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return { horizontal, vertical };
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Get a copy of the grid for debugging or analysis
|
|
171
|
+
*/
|
|
172
|
+
getGrid() {
|
|
173
|
+
return this.grid;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Find path from start to end using BFS
|
|
177
|
+
* Returns array of cells representing the path
|
|
178
|
+
*/
|
|
179
|
+
findPath(startRow, startCol, endRow, endCol) {
|
|
180
|
+
const visited = new Set();
|
|
181
|
+
const queue = [];
|
|
182
|
+
const startCell = this.getCell(startRow, startCol);
|
|
183
|
+
if (!startCell)
|
|
184
|
+
return [];
|
|
185
|
+
queue.push({ cell: startCell, path: [startCell] });
|
|
186
|
+
visited.add(`${startRow},${startCol}`);
|
|
187
|
+
while (queue.length > 0) {
|
|
188
|
+
const { cell, path } = queue.shift();
|
|
189
|
+
if (cell.row === endRow && cell.col === endCol) {
|
|
190
|
+
return path;
|
|
191
|
+
}
|
|
192
|
+
// Check all four directions
|
|
193
|
+
const neighbors = [
|
|
194
|
+
{ dir: 'north', dr: -1, dc: 0, wall: cell.walls.north },
|
|
195
|
+
{ dir: 'south', dr: 1, dc: 0, wall: cell.walls.south },
|
|
196
|
+
{ dir: 'east', dr: 0, dc: 1, wall: cell.walls.east },
|
|
197
|
+
{ dir: 'west', dr: 0, dc: -1, wall: cell.walls.west },
|
|
198
|
+
];
|
|
199
|
+
for (const { dr, dc, wall } of neighbors) {
|
|
200
|
+
if (wall)
|
|
201
|
+
continue; // Can't go through wall
|
|
202
|
+
const nextRow = cell.row + dr;
|
|
203
|
+
const nextCol = cell.col + dc;
|
|
204
|
+
const key = `${nextRow},${nextCol}`;
|
|
205
|
+
if (visited.has(key))
|
|
206
|
+
continue;
|
|
207
|
+
const nextCell = this.getCell(nextRow, nextCol);
|
|
208
|
+
if (!nextCell)
|
|
209
|
+
continue;
|
|
210
|
+
visited.add(key);
|
|
211
|
+
queue.push({
|
|
212
|
+
cell: nextCell,
|
|
213
|
+
path: [...path, nextCell]
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return []; // No path found
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Add a blockage (wall) at a specific position
|
|
221
|
+
*/
|
|
222
|
+
addBlockage(row, col, direction) {
|
|
223
|
+
const cell = this.getCell(row, col);
|
|
224
|
+
if (!cell)
|
|
225
|
+
return;
|
|
226
|
+
cell.walls[direction] = true;
|
|
227
|
+
// Also add the wall to the adjacent cell
|
|
228
|
+
if (direction === 'north') {
|
|
229
|
+
const neighbor = this.getCell(row - 1, col);
|
|
230
|
+
if (neighbor)
|
|
231
|
+
neighbor.walls.south = true;
|
|
232
|
+
}
|
|
233
|
+
else if (direction === 'south') {
|
|
234
|
+
const neighbor = this.getCell(row + 1, col);
|
|
235
|
+
if (neighbor)
|
|
236
|
+
neighbor.walls.north = true;
|
|
237
|
+
}
|
|
238
|
+
else if (direction === 'east') {
|
|
239
|
+
const neighbor = this.getCell(row, col + 1);
|
|
240
|
+
if (neighbor)
|
|
241
|
+
neighbor.walls.west = true;
|
|
242
|
+
}
|
|
243
|
+
else if (direction === 'west') {
|
|
244
|
+
const neighbor = this.getCell(row, col - 1);
|
|
245
|
+
if (neighbor)
|
|
246
|
+
neighbor.walls.east = true;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Helper function to generate a maze with default settings
|
|
252
|
+
*/
|
|
253
|
+
export function generateMaze(rows, cols, seed) {
|
|
254
|
+
// Use seed for reproducible mazes (if provided)
|
|
255
|
+
if (seed !== undefined) {
|
|
256
|
+
// Simple seeded random (not cryptographically secure)
|
|
257
|
+
let currentSeed = seed;
|
|
258
|
+
Math.random = () => {
|
|
259
|
+
currentSeed = (currentSeed * 9301 + 49297) % 233280;
|
|
260
|
+
return currentSeed / 233280;
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
const generator = new MazeGenerator(rows, cols);
|
|
264
|
+
generator.generate(0, 0);
|
|
265
|
+
return generator.getWalls();
|
|
266
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export { Logo } from './Logo';
|
|
2
2
|
export { LogoSmall } from './LogoSmall';
|
|
3
3
|
export { ForksLogo } from './ForksLogo';
|
|
4
|
+
export { SquareLogo } from './SquareLogo';
|
|
5
|
+
export { WreathLogo } from './WreathLogo';
|
|
6
|
+
export { MazeDemo } from './MazeDemo';
|
|
7
|
+
export { MazeDemoNew } from './MazeDemoNew';
|
package/dist/index.esm.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export { Logo } from './Logo';
|
|
2
2
|
export { LogoSmall } from './LogoSmall';
|
|
3
3
|
export { ForksLogo } from './ForksLogo';
|
|
4
|
+
export { SquareLogo } from './SquareLogo';
|
|
5
|
+
export { WreathLogo } from './WreathLogo';
|
|
6
|
+
export { MazeDemo } from './MazeDemo';
|
|
7
|
+
export { MazeDemoNew } from './MazeDemoNew';
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ForksLogo = exports.LogoSmall = exports.Logo = void 0;
|
|
3
|
+
exports.MazeDemoNew = exports.MazeDemo = exports.WreathLogo = exports.SquareLogo = exports.ForksLogo = exports.LogoSmall = exports.Logo = void 0;
|
|
4
4
|
var Logo_1 = require("./Logo");
|
|
5
5
|
Object.defineProperty(exports, "Logo", { enumerable: true, get: function () { return Logo_1.Logo; } });
|
|
6
6
|
var LogoSmall_1 = require("./LogoSmall");
|
|
7
7
|
Object.defineProperty(exports, "LogoSmall", { enumerable: true, get: function () { return LogoSmall_1.LogoSmall; } });
|
|
8
8
|
var ForksLogo_1 = require("./ForksLogo");
|
|
9
9
|
Object.defineProperty(exports, "ForksLogo", { enumerable: true, get: function () { return ForksLogo_1.ForksLogo; } });
|
|
10
|
+
var SquareLogo_1 = require("./SquareLogo");
|
|
11
|
+
Object.defineProperty(exports, "SquareLogo", { enumerable: true, get: function () { return SquareLogo_1.SquareLogo; } });
|
|
12
|
+
var WreathLogo_1 = require("./WreathLogo");
|
|
13
|
+
Object.defineProperty(exports, "WreathLogo", { enumerable: true, get: function () { return WreathLogo_1.WreathLogo; } });
|
|
14
|
+
var MazeDemo_1 = require("./MazeDemo");
|
|
15
|
+
Object.defineProperty(exports, "MazeDemo", { enumerable: true, get: function () { return MazeDemo_1.MazeDemo; } });
|
|
16
|
+
var MazeDemoNew_1 = require("./MazeDemoNew");
|
|
17
|
+
Object.defineProperty(exports, "MazeDemoNew", { enumerable: true, get: function () { return MazeDemoNew_1.MazeDemoNew; } });
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maze Generator using Recursive Backtracking
|
|
3
|
+
* Creates a perfect maze (exactly one path between any two cells)
|
|
4
|
+
*/
|
|
5
|
+
export interface MazeCell {
|
|
6
|
+
row: number;
|
|
7
|
+
col: number;
|
|
8
|
+
visited: boolean;
|
|
9
|
+
walls: {
|
|
10
|
+
north: boolean;
|
|
11
|
+
south: boolean;
|
|
12
|
+
east: boolean;
|
|
13
|
+
west: boolean;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface MazeWalls {
|
|
17
|
+
horizontal: number[][];
|
|
18
|
+
vertical: number[][];
|
|
19
|
+
}
|
|
20
|
+
export declare class MazeGenerator {
|
|
21
|
+
private grid;
|
|
22
|
+
private rows;
|
|
23
|
+
private cols;
|
|
24
|
+
constructor(rows: number, cols: number);
|
|
25
|
+
private initializeGrid;
|
|
26
|
+
private getCell;
|
|
27
|
+
private getUnvisitedNeighbors;
|
|
28
|
+
private removeWallBetween;
|
|
29
|
+
private recursiveBacktrack;
|
|
30
|
+
private shuffle;
|
|
31
|
+
/**
|
|
32
|
+
* Generate the maze starting from a specific cell
|
|
33
|
+
*/
|
|
34
|
+
generate(startRow?: number, startCol?: number): void;
|
|
35
|
+
/**
|
|
36
|
+
* Convert the maze to wall segments for rendering
|
|
37
|
+
*/
|
|
38
|
+
getWalls(): MazeWalls;
|
|
39
|
+
/**
|
|
40
|
+
* Get a copy of the grid for debugging or analysis
|
|
41
|
+
*/
|
|
42
|
+
getGrid(): MazeCell[][];
|
|
43
|
+
/**
|
|
44
|
+
* Find path from start to end using BFS
|
|
45
|
+
* Returns array of cells representing the path
|
|
46
|
+
*/
|
|
47
|
+
findPath(startRow: number, startCol: number, endRow: number, endCol: number): MazeCell[];
|
|
48
|
+
/**
|
|
49
|
+
* Add a blockage (wall) at a specific position
|
|
50
|
+
*/
|
|
51
|
+
addBlockage(row: number, col: number, direction: 'north' | 'south' | 'east' | 'west'): void;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Helper function to generate a maze with default settings
|
|
55
|
+
*/
|
|
56
|
+
export declare function generateMaze(rows: number, cols: number, seed?: number): MazeWalls;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Maze Generator using Recursive Backtracking
|
|
4
|
+
* Creates a perfect maze (exactly one path between any two cells)
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.MazeGenerator = void 0;
|
|
8
|
+
exports.generateMaze = generateMaze;
|
|
9
|
+
class MazeGenerator {
|
|
10
|
+
constructor(rows, cols) {
|
|
11
|
+
this.rows = rows;
|
|
12
|
+
this.cols = cols;
|
|
13
|
+
this.grid = this.initializeGrid();
|
|
14
|
+
}
|
|
15
|
+
initializeGrid() {
|
|
16
|
+
const grid = [];
|
|
17
|
+
for (let row = 0; row < this.rows; row++) {
|
|
18
|
+
grid[row] = [];
|
|
19
|
+
for (let col = 0; col < this.cols; col++) {
|
|
20
|
+
grid[row][col] = {
|
|
21
|
+
row,
|
|
22
|
+
col,
|
|
23
|
+
visited: false,
|
|
24
|
+
walls: {
|
|
25
|
+
north: true,
|
|
26
|
+
south: true,
|
|
27
|
+
east: true,
|
|
28
|
+
west: true,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return grid;
|
|
34
|
+
}
|
|
35
|
+
getCell(row, col) {
|
|
36
|
+
if (row < 0 || row >= this.rows || col < 0 || col >= this.cols) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return this.grid[row][col];
|
|
40
|
+
}
|
|
41
|
+
getUnvisitedNeighbors(cell) {
|
|
42
|
+
const neighbors = [];
|
|
43
|
+
const { row, col } = cell;
|
|
44
|
+
// North
|
|
45
|
+
const north = this.getCell(row - 1, col);
|
|
46
|
+
if (north && !north.visited)
|
|
47
|
+
neighbors.push(north);
|
|
48
|
+
// South
|
|
49
|
+
const south = this.getCell(row + 1, col);
|
|
50
|
+
if (south && !south.visited)
|
|
51
|
+
neighbors.push(south);
|
|
52
|
+
// East
|
|
53
|
+
const east = this.getCell(row, col + 1);
|
|
54
|
+
if (east && !east.visited)
|
|
55
|
+
neighbors.push(east);
|
|
56
|
+
// West
|
|
57
|
+
const west = this.getCell(row, col - 1);
|
|
58
|
+
if (west && !west.visited)
|
|
59
|
+
neighbors.push(west);
|
|
60
|
+
return neighbors;
|
|
61
|
+
}
|
|
62
|
+
removeWallBetween(current, next) {
|
|
63
|
+
const rowDiff = current.row - next.row;
|
|
64
|
+
const colDiff = current.col - next.col;
|
|
65
|
+
// North
|
|
66
|
+
if (rowDiff === 1) {
|
|
67
|
+
current.walls.north = false;
|
|
68
|
+
next.walls.south = false;
|
|
69
|
+
}
|
|
70
|
+
// South
|
|
71
|
+
else if (rowDiff === -1) {
|
|
72
|
+
current.walls.south = false;
|
|
73
|
+
next.walls.north = false;
|
|
74
|
+
}
|
|
75
|
+
// East
|
|
76
|
+
else if (colDiff === -1) {
|
|
77
|
+
current.walls.east = false;
|
|
78
|
+
next.walls.west = false;
|
|
79
|
+
}
|
|
80
|
+
// West
|
|
81
|
+
else if (colDiff === 1) {
|
|
82
|
+
current.walls.west = false;
|
|
83
|
+
next.walls.east = false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
recursiveBacktrack(cell) {
|
|
87
|
+
cell.visited = true;
|
|
88
|
+
const neighbors = this.getUnvisitedNeighbors(cell);
|
|
89
|
+
// Shuffle neighbors for randomness
|
|
90
|
+
this.shuffle(neighbors);
|
|
91
|
+
for (const neighbor of neighbors) {
|
|
92
|
+
if (!neighbor.visited) {
|
|
93
|
+
this.removeWallBetween(cell, neighbor);
|
|
94
|
+
this.recursiveBacktrack(neighbor);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
shuffle(array) {
|
|
99
|
+
for (let i = array.length - 1; i > 0; i--) {
|
|
100
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
101
|
+
[array[i], array[j]] = [array[j], array[i]];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Generate the maze starting from a specific cell
|
|
106
|
+
*/
|
|
107
|
+
generate(startRow = 0, startCol = 0) {
|
|
108
|
+
const startCell = this.getCell(startRow, startCol);
|
|
109
|
+
if (startCell) {
|
|
110
|
+
this.recursiveBacktrack(startCell);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Convert the maze to wall segments for rendering
|
|
115
|
+
*/
|
|
116
|
+
getWalls() {
|
|
117
|
+
const horizontal = [];
|
|
118
|
+
const vertical = [];
|
|
119
|
+
// Convert cell walls to line segments
|
|
120
|
+
for (let row = 0; row < this.rows; row++) {
|
|
121
|
+
for (let col = 0; col < this.cols; col++) {
|
|
122
|
+
const cell = this.grid[row][col];
|
|
123
|
+
// North wall
|
|
124
|
+
if (cell.walls.north) {
|
|
125
|
+
// Check if we can extend an existing horizontal wall
|
|
126
|
+
const lastHWall = horizontal[horizontal.length - 1];
|
|
127
|
+
if (lastHWall && lastHWall[1] === row && lastHWall[2] === col) {
|
|
128
|
+
// Extend the wall
|
|
129
|
+
lastHWall[2] = col + 1;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
// Create new wall segment
|
|
133
|
+
horizontal.push([col, row, col + 1, row]);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// West wall
|
|
137
|
+
if (cell.walls.west) {
|
|
138
|
+
// Check if we can extend an existing vertical wall
|
|
139
|
+
const lastVWall = vertical[vertical.length - 1];
|
|
140
|
+
if (lastVWall && lastVWall[0] === col && lastVWall[3] === row) {
|
|
141
|
+
// Extend the wall
|
|
142
|
+
lastVWall[3] = row + 1;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// Create new wall segment
|
|
146
|
+
vertical.push([col, row, col, row + 1]);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// South wall (only for bottom row)
|
|
150
|
+
if (row === this.rows - 1 && cell.walls.south) {
|
|
151
|
+
const lastHWall = horizontal[horizontal.length - 1];
|
|
152
|
+
if (lastHWall && lastHWall[1] === row + 1 && lastHWall[2] === col) {
|
|
153
|
+
lastHWall[2] = col + 1;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
horizontal.push([col, row + 1, col + 1, row + 1]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// East wall (only for rightmost column)
|
|
160
|
+
if (col === this.cols - 1 && cell.walls.east) {
|
|
161
|
+
const lastVWall = vertical[vertical.length - 1];
|
|
162
|
+
if (lastVWall && lastVWall[0] === col + 1 && lastVWall[3] === row) {
|
|
163
|
+
lastVWall[3] = row + 1;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
vertical.push([col + 1, row, col + 1, row + 1]);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return { horizontal, vertical };
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get a copy of the grid for debugging or analysis
|
|
175
|
+
*/
|
|
176
|
+
getGrid() {
|
|
177
|
+
return this.grid;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Find path from start to end using BFS
|
|
181
|
+
* Returns array of cells representing the path
|
|
182
|
+
*/
|
|
183
|
+
findPath(startRow, startCol, endRow, endCol) {
|
|
184
|
+
const visited = new Set();
|
|
185
|
+
const queue = [];
|
|
186
|
+
const startCell = this.getCell(startRow, startCol);
|
|
187
|
+
if (!startCell)
|
|
188
|
+
return [];
|
|
189
|
+
queue.push({ cell: startCell, path: [startCell] });
|
|
190
|
+
visited.add(`${startRow},${startCol}`);
|
|
191
|
+
while (queue.length > 0) {
|
|
192
|
+
const { cell, path } = queue.shift();
|
|
193
|
+
if (cell.row === endRow && cell.col === endCol) {
|
|
194
|
+
return path;
|
|
195
|
+
}
|
|
196
|
+
// Check all four directions
|
|
197
|
+
const neighbors = [
|
|
198
|
+
{ dir: 'north', dr: -1, dc: 0, wall: cell.walls.north },
|
|
199
|
+
{ dir: 'south', dr: 1, dc: 0, wall: cell.walls.south },
|
|
200
|
+
{ dir: 'east', dr: 0, dc: 1, wall: cell.walls.east },
|
|
201
|
+
{ dir: 'west', dr: 0, dc: -1, wall: cell.walls.west },
|
|
202
|
+
];
|
|
203
|
+
for (const { dr, dc, wall } of neighbors) {
|
|
204
|
+
if (wall)
|
|
205
|
+
continue; // Can't go through wall
|
|
206
|
+
const nextRow = cell.row + dr;
|
|
207
|
+
const nextCol = cell.col + dc;
|
|
208
|
+
const key = `${nextRow},${nextCol}`;
|
|
209
|
+
if (visited.has(key))
|
|
210
|
+
continue;
|
|
211
|
+
const nextCell = this.getCell(nextRow, nextCol);
|
|
212
|
+
if (!nextCell)
|
|
213
|
+
continue;
|
|
214
|
+
visited.add(key);
|
|
215
|
+
queue.push({
|
|
216
|
+
cell: nextCell,
|
|
217
|
+
path: [...path, nextCell]
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return []; // No path found
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Add a blockage (wall) at a specific position
|
|
225
|
+
*/
|
|
226
|
+
addBlockage(row, col, direction) {
|
|
227
|
+
const cell = this.getCell(row, col);
|
|
228
|
+
if (!cell)
|
|
229
|
+
return;
|
|
230
|
+
cell.walls[direction] = true;
|
|
231
|
+
// Also add the wall to the adjacent cell
|
|
232
|
+
if (direction === 'north') {
|
|
233
|
+
const neighbor = this.getCell(row - 1, col);
|
|
234
|
+
if (neighbor)
|
|
235
|
+
neighbor.walls.south = true;
|
|
236
|
+
}
|
|
237
|
+
else if (direction === 'south') {
|
|
238
|
+
const neighbor = this.getCell(row + 1, col);
|
|
239
|
+
if (neighbor)
|
|
240
|
+
neighbor.walls.north = true;
|
|
241
|
+
}
|
|
242
|
+
else if (direction === 'east') {
|
|
243
|
+
const neighbor = this.getCell(row, col + 1);
|
|
244
|
+
if (neighbor)
|
|
245
|
+
neighbor.walls.west = true;
|
|
246
|
+
}
|
|
247
|
+
else if (direction === 'west') {
|
|
248
|
+
const neighbor = this.getCell(row, col - 1);
|
|
249
|
+
if (neighbor)
|
|
250
|
+
neighbor.walls.east = true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
exports.MazeGenerator = MazeGenerator;
|
|
255
|
+
/**
|
|
256
|
+
* Helper function to generate a maze with default settings
|
|
257
|
+
*/
|
|
258
|
+
function generateMaze(rows, cols, seed) {
|
|
259
|
+
// Use seed for reproducible mazes (if provided)
|
|
260
|
+
if (seed !== undefined) {
|
|
261
|
+
// Simple seeded random (not cryptographically secure)
|
|
262
|
+
let currentSeed = seed;
|
|
263
|
+
Math.random = () => {
|
|
264
|
+
currentSeed = (currentSeed * 9301 + 49297) % 233280;
|
|
265
|
+
return currentSeed / 233280;
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
const generator = new MazeGenerator(rows, cols);
|
|
269
|
+
generator.generate(0, 0);
|
|
270
|
+
return generator.getWalls();
|
|
271
|
+
}
|