@principal-ai/logo-component 0.1.5 → 0.1.6

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.
@@ -1,339 +0,0 @@
1
- import React, { useMemo, useState, useEffect } from "react";
2
- import { MazeGenerator } from "./utils/mazeGenerator";
3
- export const MazeDemoNew = ({ width = 450, height = 620, mazeColor = "#6366f1", errorColor = "#ef4444", searchColor = "#f59e0b", solutionColor = "#10b981", mazeSeed = 42, }) => {
4
- // Maze configuration
5
- const gridSize = 10;
6
- const cellSize = 30;
7
- const padding = 50;
8
- const mazeWidth = gridSize * cellSize;
9
- const mazeHeight = gridSize * cellSize;
10
- const mazeX = (width - mazeWidth) / 2;
11
- // Key positions
12
- const startCol = 0;
13
- const startRow = 0;
14
- const destCol = 9;
15
- const destRow = 9;
16
- // State
17
- const [revealedCells, setRevealedCells] = useState([]);
18
- const [directionHint, setDirectionHint] = useState("");
19
- const [timeCost, setTimeCost] = useState(0);
20
- const [clickCost, setClickCost] = useState(0);
21
- const [blockageFound, setBlockageFound] = useState(false);
22
- const [startTime, setStartTime] = useState(null);
23
- const [started, setStarted] = useState(false);
24
- const [currentSeed, setCurrentSeed] = useState(() => Math.floor(Math.random() * 10000));
25
- const [blockageInjected, setBlockageInjected] = useState(false);
26
- const [deployed, setDeployed] = useState(false);
27
- const [mode, setMode] = useState('initial');
28
- // Total incident cost
29
- const incidentCost = timeCost + clickCost;
30
- // Update currentSeed when mazeSeed prop changes
31
- useEffect(() => {
32
- setCurrentSeed(mazeSeed);
33
- }, [mazeSeed]);
34
- // Increment time cost
35
- useEffect(() => {
36
- if (blockageFound || !startTime || !started)
37
- return;
38
- const interval = setInterval(() => {
39
- setTimeCost(prev => prev + 1);
40
- }, 10);
41
- return () => clearInterval(interval);
42
- }, [blockageFound, startTime, started]);
43
- // Generate maze
44
- const { horizontalWalls, verticalWalls, blockageWall, actualBlockageCol, actualBlockageRow } = useMemo(() => {
45
- let seed = currentSeed;
46
- const seededRandom = () => {
47
- seed = (seed * 9301 + 49297) % 233280;
48
- return seed / 233280;
49
- };
50
- const originalRandom = Math.random;
51
- Math.random = seededRandom;
52
- const generator = new MazeGenerator(gridSize, gridSize);
53
- generator.generate(startRow, startCol);
54
- let blockCell = { row: 5, col: 5 };
55
- let direction = 'east';
56
- let blockageWall = null;
57
- if (blockageInjected) {
58
- const path = generator.findPath(startRow, startCol, destRow, destCol);
59
- if (path.length > 2) {
60
- const middleStart = Math.floor(path.length * 0.3);
61
- const middleEnd = Math.floor(path.length * 0.7);
62
- const blockIndex = Math.floor(seededRandom() * (middleEnd - middleStart)) + middleStart;
63
- blockCell = path[blockIndex];
64
- if (blockIndex < path.length - 1) {
65
- const nextCell = path[blockIndex + 1];
66
- const rowDiff = nextCell.row - blockCell.row;
67
- const colDiff = nextCell.col - blockCell.col;
68
- if (rowDiff === 1)
69
- direction = 'south';
70
- else if (rowDiff === -1)
71
- direction = 'north';
72
- else if (colDiff === 1)
73
- direction = 'east';
74
- else if (colDiff === -1)
75
- direction = 'west';
76
- }
77
- }
78
- generator.addBlockage(blockCell.row, blockCell.col, direction);
79
- }
80
- const walls = generator.getWalls();
81
- if (blockageInjected) {
82
- if (direction === 'east' || direction === 'west') {
83
- const targetCol = direction === 'east' ? blockCell.col + 1 : blockCell.col;
84
- const targetRow = blockCell.row;
85
- const wallSegment = walls.vertical.find(wall => {
86
- const [col, row1, , row2] = wall;
87
- return col === targetCol && row1 <= targetRow && row2 > targetRow;
88
- });
89
- if (wallSegment) {
90
- const [col, row1, , row2] = wallSegment;
91
- blockageWall = { type: 'vertical', col, row1, row2 };
92
- }
93
- }
94
- else {
95
- const targetRow = direction === 'south' ? blockCell.row + 1 : blockCell.row;
96
- const targetCol = blockCell.col;
97
- const wallSegment = walls.horizontal.find(wall => {
98
- const [col1, row, col2] = wall;
99
- return row === targetRow && col1 <= targetCol && col2 > targetCol;
100
- });
101
- if (wallSegment) {
102
- const [col1, row, col2] = wallSegment;
103
- blockageWall = { type: 'horizontal', row, col1, col2 };
104
- }
105
- }
106
- }
107
- Math.random = originalRandom;
108
- return {
109
- horizontalWalls: walls.horizontal,
110
- verticalWalls: walls.vertical,
111
- blockageWall,
112
- actualBlockageCol: blockCell.col,
113
- actualBlockageRow: blockCell.row,
114
- };
115
- }, [currentSeed, blockageInjected]);
116
- // Handlers
117
- const handleModeSelect = (selectedMode) => {
118
- setMode(selectedMode);
119
- };
120
- const handleDeploy = () => {
121
- setDeployed(true);
122
- setTimeout(() => {
123
- setBlockageInjected(true);
124
- setStarted(true);
125
- setStartTime(Date.now());
126
- }, 3000);
127
- };
128
- const handleTryPrincipal = () => {
129
- setMode('principal');
130
- setRevealedCells([]);
131
- setDirectionHint("");
132
- setBlockageFound(false);
133
- setTimeCost(0);
134
- setClickCost(0);
135
- setStartTime(null);
136
- setStarted(false);
137
- setDeployed(false);
138
- setBlockageInjected(false); // Principal mode still needs to deploy first
139
- };
140
- const handleTryAgain = () => {
141
- setRevealedCells([]);
142
- setDirectionHint("");
143
- setBlockageFound(false);
144
- setTimeCost(0);
145
- setClickCost(0);
146
- setStartTime(null);
147
- setStarted(false);
148
- setBlockageInjected(false);
149
- setDeployed(false);
150
- setMode('initial');
151
- setCurrentSeed(Math.floor(Math.random() * 10000));
152
- };
153
- const handleCellClick = (col, row) => {
154
- if (blockageFound || !started)
155
- return;
156
- // In principal mode, only allow clicking the blockage cell
157
- if (mode === 'principal') {
158
- const isBlockageCell = col === actualBlockageCol && row === actualBlockageRow;
159
- if (isBlockageCell) {
160
- setDirectionHint("🎯 Blockage Found!");
161
- setBlockageFound(true);
162
- }
163
- return;
164
- }
165
- const alreadyRevealed = revealedCells.some(cell => cell.col === col && cell.row === row);
166
- if (alreadyRevealed)
167
- return;
168
- setClickCost(prev => prev + 500);
169
- setRevealedCells([...revealedCells, { col, row }]);
170
- const isBlockageCell = col === actualBlockageCol && row === actualBlockageRow;
171
- if (isBlockageCell) {
172
- setDirectionHint("🎯 Blockage Found!");
173
- setBlockageFound(true);
174
- const cellsToReveal = [...revealedCells];
175
- for (let r = 0; r < gridSize; r++) {
176
- for (let c = 0; c < gridSize; c++) {
177
- const distance = Math.abs(actualBlockageCol - c) + Math.abs(actualBlockageRow - r);
178
- if (distance <= 3) {
179
- const alreadyInList = cellsToReveal.some(cell => cell.col === c && cell.row === r);
180
- if (!alreadyInList) {
181
- cellsToReveal.push({ col: c, row: r });
182
- }
183
- }
184
- }
185
- }
186
- setRevealedCells(cellsToReveal);
187
- }
188
- else {
189
- const colDiff = actualBlockageCol - col;
190
- const rowDiff = actualBlockageRow - row;
191
- let direction = "";
192
- if (Math.abs(rowDiff) > 1) {
193
- direction += rowDiff > 0 ? "↓ " : "↑ ";
194
- }
195
- if (Math.abs(colDiff) > 1) {
196
- direction += colDiff > 0 ? "→" : "←";
197
- }
198
- const distance = Math.abs(colDiff) + Math.abs(rowDiff);
199
- let proximity = "";
200
- if (distance <= 2)
201
- proximity = "Very close!";
202
- else if (distance <= 4)
203
- proximity = "Getting warmer...";
204
- else if (distance <= 6)
205
- proximity = "Still far...";
206
- else
207
- proximity = "Cold...";
208
- setDirectionHint(`${direction} ${proximity}`);
209
- }
210
- };
211
- // Render maze
212
- const renderMaze = (showBlockage, opacity = 1) => {
213
- const elements = [];
214
- elements.push(React.createElement("rect", { key: "background", x: mazeX, y: padding, width: mazeWidth, height: mazeHeight, fill: "none", stroke: mazeColor, strokeWidth: "3", opacity: 0.3 * opacity }));
215
- const isBlockageWall = (type, wall) => {
216
- if (!showBlockage || !blockageWall)
217
- return false;
218
- if (type === 'vertical') {
219
- const [x, y1, , y2] = wall;
220
- return (blockageWall.type === 'vertical' &&
221
- x === blockageWall.col &&
222
- y1 === blockageWall.row1 &&
223
- y2 === blockageWall.row2);
224
- }
225
- else if (type === 'horizontal') {
226
- const [x1, y, x2] = wall;
227
- return (blockageWall.type === 'horizontal' &&
228
- y === blockageWall.row &&
229
- x1 === blockageWall.col1 &&
230
- x2 === blockageWall.col2);
231
- }
232
- return false;
233
- };
234
- horizontalWalls.forEach((wall, idx) => {
235
- const [x1, y, x2] = wall;
236
- const isBlockage = isBlockageWall('horizontal', wall);
237
- elements.push(React.createElement("line", { key: `h-wall-${idx}`, x1: mazeX + x1 * cellSize, y1: padding + y * cellSize, x2: mazeX + x2 * cellSize, y2: padding + y * cellSize, stroke: isBlockage ? errorColor : mazeColor, strokeWidth: isBlockage ? 4 : 2.5, strokeLinecap: "round", opacity: opacity }, isBlockage && showBlockage && (React.createElement("animate", { attributeName: "stroke-width", values: "4;6;4", dur: "1.5s", repeatCount: "indefinite" }))));
238
- });
239
- verticalWalls.forEach((wall, idx) => {
240
- const [x, y1, , y2] = wall;
241
- const isBlockage = isBlockageWall('vertical', wall);
242
- elements.push(React.createElement("line", { key: `v-wall-${idx}`, x1: mazeX + x * cellSize, y1: padding + y1 * cellSize, x2: mazeX + x * cellSize, y2: padding + y2 * cellSize, stroke: isBlockage ? errorColor : mazeColor, strokeWidth: isBlockage ? 4 : 2.5, strokeLinecap: "round", opacity: opacity }, isBlockage && showBlockage && (React.createElement("animate", { attributeName: "stroke-width", values: "4;6;4", dur: "1.5s", repeatCount: "indefinite" }))));
243
- });
244
- for (let row = 0; row <= gridSize; row++) {
245
- elements.push(React.createElement("line", { key: `grid-h-${row}`, x1: mazeX, y1: padding + row * cellSize, x2: mazeX + mazeWidth, y2: padding + row * cellSize, stroke: mazeColor, strokeWidth: "0.5", opacity: 0.15 * opacity }));
246
- }
247
- for (let col = 0; col <= gridSize; col++) {
248
- elements.push(React.createElement("line", { key: `grid-v-${col}`, x1: mazeX + col * cellSize, y1: padding, x2: mazeX + col * cellSize, y2: padding + mazeHeight, stroke: mazeColor, strokeWidth: "0.5", opacity: 0.15 * opacity }));
249
- }
250
- return elements;
251
- };
252
- const getTitle = () => {
253
- if (mode === 'principal')
254
- return "With Principal";
255
- if (mode === 'agentic')
256
- return "With Agentic Coding";
257
- if (mode === 'no-agentic')
258
- return "Without Agentic Coding";
259
- return "Choose Your Approach";
260
- };
261
- const getSubtitle = () => {
262
- if (mode === 'principal')
263
- return "Full visibility - See everything";
264
- if (started)
265
- return "Find the blockage";
266
- if (deployed)
267
- return "Running smoothly...";
268
- return "How will you handle production?";
269
- };
270
- const showCover = mode === 'agentic' || (deployed && mode === 'no-agentic');
271
- const coverOpacity = 1; // Always full opacity for the cover
272
- const mazeOpacity = 1; // Maze always at full opacity, cover handles visibility
273
- return (React.createElement("svg", { width: width, height: height, xmlns: "http://www.w3.org/2000/svg" },
274
- React.createElement("text", { x: width / 2, y: 25, textAnchor: "middle", fill: mazeColor, fontSize: "16", fontWeight: "bold" }, getTitle()),
275
- React.createElement("text", { x: width / 2, y: 40, textAnchor: "middle", fill: mazeColor, fontSize: "11", opacity: "0.6" }, getSubtitle()),
276
- mode !== 'initial' && (React.createElement(React.Fragment, null,
277
- React.createElement("g", null, renderMaze(mode === 'principal' || blockageFound, mazeOpacity)),
278
- React.createElement("circle", { cx: mazeX + (startCol + 0.5) * cellSize, cy: padding + (startRow + 0.5) * cellSize, r: "5", fill: searchColor }),
279
- React.createElement("text", { x: mazeX - 5, y: padding - 5, textAnchor: "end", fontSize: "10", fill: searchColor, fontWeight: "bold" }, "START"),
280
- React.createElement("g", null,
281
- React.createElement("circle", { cx: mazeX + (destCol + 0.5) * cellSize, cy: padding + (destRow + 0.5) * cellSize, r: "8", fill: "none", stroke: mazeColor, strokeWidth: "1.5", opacity: "0.5" }),
282
- React.createElement("circle", { cx: mazeX + (destCol + 0.5) * cellSize, cy: padding + (destRow + 0.5) * cellSize, r: "5", fill: "none", stroke: mazeColor, strokeWidth: "1.5", opacity: "0.6" }),
283
- React.createElement("circle", { cx: mazeX + (destCol + 0.5) * cellSize, cy: padding + (destRow + 0.5) * cellSize, r: "2", fill: mazeColor, opacity: "0.7" })),
284
- React.createElement("text", { x: mazeX + mazeWidth + 5, y: padding + mazeHeight + 15, textAnchor: "start", fontSize: "9", fill: mazeColor, fontWeight: "bold" }, "DEST"))),
285
- showCover && (React.createElement(React.Fragment, null,
286
- React.createElement("defs", null,
287
- React.createElement("mask", { id: "mazeCoverMask" },
288
- React.createElement("rect", { x: mazeX, y: padding, width: mazeWidth, height: mazeHeight, fill: "white" }),
289
- revealedCells.map((cell, idx) => (React.createElement("rect", { key: idx, x: mazeX + cell.col * cellSize, y: padding + cell.row * cellSize, width: cellSize, height: cellSize, fill: "black" }))))),
290
- React.createElement("g", null,
291
- React.createElement("rect", { x: mazeX, y: padding, width: mazeWidth, height: mazeHeight, fill: "#2a2a2a", opacity: coverOpacity, mask: "url(#mazeCoverMask)" }),
292
- React.createElement("rect", { x: mazeX, y: padding, width: mazeWidth, height: mazeHeight, fill: "none", stroke: mazeColor, strokeWidth: "3", opacity: "0.4" }),
293
- started && revealedCells.length === 0 && (React.createElement("g", null,
294
- React.createElement("rect", { x: mazeX + mazeWidth / 2 - 130, y: padding + mazeHeight / 2 - 40, width: 260, height: 80, fill: "#000000", opacity: "0.7", rx: "8", pointerEvents: "none" }),
295
- React.createElement("text", { x: mazeX + mazeWidth / 2, y: padding + mazeHeight / 2 - 15, textAnchor: "middle", fontSize: "18", fill: errorColor, fontWeight: "bold", pointerEvents: "none" }, "\uD83D\uDEA8 INCIDENT ALERT"),
296
- React.createElement("text", { x: mazeX + mazeWidth / 2, y: padding + mazeHeight / 2 + 10, textAnchor: "middle", fontSize: "13", fill: "#ffffff", fontWeight: "normal", pointerEvents: "none" }, "It's 3:00 AM - Find the blockage!"))),
297
- started && revealedCells.length > 0 && directionHint && (React.createElement("g", null,
298
- React.createElement("rect", { x: mazeX + mazeWidth / 2 - 100, y: padding + mazeHeight / 2 - 25, width: 200, height: 40, fill: "#000000", opacity: "0.7", rx: "6", pointerEvents: "none" }),
299
- React.createElement("text", { x: mazeX + mazeWidth / 2, y: padding + mazeHeight / 2, textAnchor: "middle", fontSize: "16", fill: "#ffffff", fontWeight: "bold", pointerEvents: "none" }, directionHint)))))),
300
- started && (React.createElement("g", null, Array.from({ length: gridSize }).map((_, row) => Array.from({ length: gridSize }).map((_, col) => (React.createElement("rect", { key: `cell-${row}-${col}`, x: mazeX + col * cellSize, y: padding + row * cellSize, width: cellSize, height: cellSize, fill: "transparent", stroke: "none", style: { cursor: 'pointer' }, onClick: () => handleCellClick(col, row), onMouseEnter: (e) => {
301
- e.currentTarget.setAttribute('fill', searchColor);
302
- e.currentTarget.setAttribute('opacity', '0.1');
303
- }, onMouseLeave: (e) => {
304
- e.currentTarget.setAttribute('fill', 'transparent');
305
- e.currentTarget.setAttribute('opacity', '1');
306
- } })))))),
307
- mode === 'initial' && (React.createElement("g", null,
308
- React.createElement("g", { style: { cursor: 'pointer' }, onClick: () => handleModeSelect('no-agentic') },
309
- React.createElement("rect", { x: mazeX + mazeWidth / 2 - 130, y: padding + mazeHeight + 25, width: 120, height: 40, fill: searchColor, rx: "6" }),
310
- React.createElement("text", { x: mazeX + mazeWidth / 2 - 70, y: padding + mazeHeight + 52, textAnchor: "middle", fontSize: "12", fill: "#ffffff", fontWeight: "bold", pointerEvents: "none" }, "No Agentic")),
311
- React.createElement("g", { style: { cursor: 'pointer' }, onClick: () => handleModeSelect('agentic') },
312
- React.createElement("rect", { x: mazeX + mazeWidth / 2 + 10, y: padding + mazeHeight + 25, width: 120, height: 40, fill: mazeColor, rx: "6" }),
313
- React.createElement("text", { x: mazeX + mazeWidth / 2 + 70, y: padding + mazeHeight + 52, textAnchor: "middle", fontSize: "12", fill: "#ffffff", fontWeight: "bold", pointerEvents: "none" }, "Agentic Coding")))),
314
- (mode === 'no-agentic' || mode === 'agentic' || mode === 'principal') && !deployed && (React.createElement("g", null,
315
- React.createElement("g", { style: { cursor: 'pointer' }, onClick: handleDeploy },
316
- React.createElement("rect", { x: mazeX + mazeWidth / 2 - 60, y: padding + mazeHeight + 25, width: 120, height: 40, fill: searchColor, rx: "6" }),
317
- React.createElement("text", { x: mazeX + mazeWidth / 2, y: padding + mazeHeight + 52, textAnchor: "middle", fontSize: "16", fill: "#ffffff", fontWeight: "bold", pointerEvents: "none" }, "DEPLOY")))),
318
- deployed && !started && (React.createElement("g", null,
319
- React.createElement("rect", { x: mazeX, y: padding + mazeHeight + 20, width: mazeWidth, height: 35, fill: solutionColor, opacity: "0.9", rx: "4" }),
320
- React.createElement("text", { x: mazeX + mazeWidth / 2, y: padding + mazeHeight + 35, textAnchor: "middle", fontSize: "11", fill: "#ffffff", fontWeight: "normal" }, "\u2713 Deployment Successful"),
321
- React.createElement("text", { x: mazeX + mazeWidth / 2, y: padding + mazeHeight + 50, textAnchor: "middle", fontSize: "14", fill: "#ffffff", fontWeight: "bold" }, "All systems operational"))),
322
- started && (React.createElement("g", null,
323
- React.createElement("rect", { x: mazeX, y: padding + mazeHeight + 20, width: mazeWidth, height: 35, fill: blockageFound ? solutionColor : errorColor, opacity: "0.9", rx: "4" }),
324
- React.createElement("text", { x: mazeX + mazeWidth / 2, y: padding + mazeHeight + 35, textAnchor: "middle", fontSize: "11", fill: "#ffffff", fontWeight: "normal" }, blockageFound ? "Incident Resolved!" : "Incident Cost"),
325
- React.createElement("text", { x: mazeX + mazeWidth / 2, y: padding + mazeHeight + 50, textAnchor: "middle", fontSize: "18", fill: "#ffffff", fontWeight: "bold" },
326
- "$",
327
- incidentCost.toLocaleString()))),
328
- started && blockageFound && mode !== 'principal' && (React.createElement("g", null,
329
- React.createElement("g", { style: { cursor: 'pointer' }, onClick: handleTryAgain },
330
- React.createElement("rect", { x: mazeX + mazeWidth / 2 - 130, y: padding + mazeHeight + 65, width: 120, height: 25, fill: mazeColor, opacity: "0.2", rx: "4" }),
331
- React.createElement("text", { x: mazeX + mazeWidth / 2 - 70, y: padding + mazeHeight + 82, textAnchor: "middle", fontSize: "10", fill: mazeColor, fontWeight: "bold", pointerEvents: "none" }, "Try Again")),
332
- React.createElement("g", { style: { cursor: 'pointer' }, onClick: handleTryPrincipal },
333
- React.createElement("rect", { x: mazeX + mazeWidth / 2 + 10, y: padding + mazeHeight + 65, width: 120, height: 25, fill: solutionColor, opacity: "0.8", rx: "4" }),
334
- React.createElement("text", { x: mazeX + mazeWidth / 2 + 70, y: padding + mazeHeight + 82, textAnchor: "middle", fontSize: "10", fill: "#ffffff", fontWeight: "bold", pointerEvents: "none" }, "Try with Principal")))),
335
- mode === 'principal' && blockageFound && (React.createElement("g", null,
336
- React.createElement("g", { style: { cursor: 'pointer' }, onClick: handleTryAgain },
337
- React.createElement("rect", { x: mazeX + mazeWidth / 2 - 60, y: padding + mazeHeight + 65, width: 120, height: 25, fill: mazeColor, opacity: "0.3", rx: "4" }),
338
- React.createElement("text", { x: mazeX + mazeWidth / 2, y: padding + mazeHeight + 82, textAnchor: "middle", fontSize: "10", fill: mazeColor, fontWeight: "bold", pointerEvents: "none" }, "Try Again"))))));
339
- };
@@ -1,266 +0,0 @@
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
- }
@@ -1,56 +0,0 @@
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;