Maze generator & solver · mistral-small-3.2-24b-instruct
Generate a random maze with the recursive-backtracker algorithm on a canvas, then animate an A-star or BFS solver finding the path from start to finish.
transcript · 4 messages
Generate a random maze with the recursive-backtracker algorithm on a canvas, then animate an A-star or BFS solver finding the path from start to finish.
index.html (8521 bytes)Successfully wrote 8521 bytes to index.html
Here's a single-page web app that generates a random maze using the recursive-backtracker algorithm and animates an A* solver finding the path from start to finish. The app is contained in a single index.html file: ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Maze Solver</title> <style> body { display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f0f0f0; } canvas { background-color: white; border: 2px solid #333; } </style> </head> <body> <canvas id="mazeCanvas" width="600" height="600"></canvas> <script> const canvas = document.getElementById('mazeCanvas'); const ctx = canvas.getContext('2d'); const cellSize = 20; const widthInCells = canvas.width / cellSize; const heightInCells = canvas.height / cellSize; // Maze generation: Recursive Backtracker Algorithm class Maze { constructor(width, height) { this.width = width; this.height = height; this.grid = this.initializeGrid(); this.visited = this.initializeVisited(); } initializeGrid() { const grid = new Array(this.width); for (let i = 0; i < this.width; i++) { grid[i] = new Array(this.height).fill(1); // 1 represents a wall } return grid; } initializeVisited() { const visited = new Array(this.width); for (let i = 0; i < this.width; i++) { visited[i] = new Array(this.height).fill(false); } return visited; } generate() { const stack = []; const startX = 0; const startY = 0; this.grid[startX][startY] = 0; // 0 represents a path this.visited[startX][startY] = true; stack.push({ x: startX, y: startY }); while (stack.length > 0) { const cell = stack[stack.length - 1]; const neighbors = this.getUnvisitedNeighbors(cell.x, cell.y); if (neighbors.length > 0) { const randomNeighbor = neighbors[Math.floor(Math.random() * neighbors.length)]; this.grid[randomNeighbor.x][randomNeighbor.y] = 0; this.visited[randomNeighbor.x][randomNeighbor.y] = true; this.grid[(cell.x + randomNeighbor.x) / 2][(cell.y + randomNeighbor.y) / 2] = 0; // Carve a path stack.push(randomNeighbor); } else { stack.pop(); } } } getUnvisitedNeighbors(x, y) { const neighbors = []; const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; for (const [dx, dy] of directions) { const nx = x + dx; const ny = y + dy; if (nx >= 0 && nx < this.width && ny >= 0 && ny < this.height && !this.visited[nx][ny]) { neighbors.push({ x: nx, y: ny }); } } return neighbors; } draw() { for (let i = 0; i < this.width; i++) { for (let j = 0; j < this.height; j++) { if (this.grid[i][j] === 1) { ctx.fillStyle = '#333'; ctx.fillRect(i * cellSize, j * cellSize, cellSize, cellSize); } } } } } // A* Pathfinding Algorithm class AStar { constructor(maze) { this.maze = maze; this.openSet = []; this.cameFrom = {}; this.gScore = {}; this.fScore = {}; } heuristic(a, b) { return Math.abs(a.x - b.x) + Math.abs(a.y - b.y); } solve(start, end) { this.openSet.push(start); for (let i = 0; i < this.maze.width; i++) { for (let j = 0; j < this.maze.height; j++) { this.gScore[`${i},${j}`] = Infinity; this.fScore[`${i},${j}`] = Infinity; } } this.gScore[`${start.x},${start.y}`] = 0; this.fScore[`${start.x},${start.y}`] = this.heuristic(start, end); while (this.openSet.length > 0) { const current = this.getLowestFScoreNode(); if (current.x === end.x && current.y === end.y) { return this.reconstructPath(current); } this.openSet.splice(this.openSet.indexOf(current), 1); const neighbors = this.getNeighbors(current); for (const neighbor of neighbors) { const tentativeGScore = this.gScore[`${current.x},${current.y}`] + 1; if (tentativeGScore < this.gScore[`${neighbor.x},${neighbor.y}`]) { this.cameFrom[`${neighbor.x},${neighbor.y}`] = current; this.gScore[`${neighbor.x},${neighbor.y}`] = tentativeGScore; this.fScore[`${neighbor.x},${neighbor.y}`] = tentativeGScore + this.heuristic(neighbor, end); if (!this.openSet.some(node => node.x === neighbor.x && node.y === neighbor.y)) { this.openSet.push(neighbor); } } } } return []; // No path found } getLowestFScoreNode() { let lowestIndex = 0; for (let i = 1; i < this.openSet.length; i++) { if (this.fScore[`${this.openSet[i].x},${this.openSet[i].y}`] < this.fScore[`${this.openSet[lowestIndex].x},${this.openSet[lowestIndex].y}`]) { lowestIndex = i; } } return this.openSet[lowestIndex]; } getNeighbors(node) { const neighbors = []; const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; for (const [dx, dy] of directions) { const nx = node.x + dx; const ny = node.y + dy; if (nx >= 0 && nx < this.maze.width && ny >= 0 && ny < this.maze.height && this.maze.grid[nx][ny] === 0) { neighbors.push({ x: nx, y: ny }); } } return neighbors; } reconstructPath(end) { const path = []; let current = end; while (current) { path.unshift(current); current = this.cameFrom[`${current.x},${current.y}`]; } return path; } } // Draw the maze and solve it const maze = new Maze(widthInCells, heightInCells); maze.generate(); maze.draw(); // Define start and end points const start = { x: 1, y: 1 }; const end = { x: widthInCells - 2, y: heightInCells - 2 }; // Solve the maze using A* const aStar = new AStar(maze); const path = aStar.solve(start, end); // Animate the pathfinding let currentStep = 0; const animatePathfinding = () => { if (currentStep < path.length) { const cell = path[currentStep]; ctx.fillStyle = 'blue'; ctx.fillRect(cell.x * cellSize, cell.y * cellSize, cellSize, cellSize); currentStep++; setTimeout(animatePathfinding, 50); } }; // Start the animation after a delay setTimeout(animatePathfinding, 1000); // Mark start and end points ctx.fillStyle = 'green'; ctx.fillRect(start.x * cellSize, start.y * cellSize, cellSize, cellSize); ctx.fillStyle = 'red'; ctx.fillRect(end.x * cellSize, end.y * cellSize, cellSize, cellSize); </script> </body> </html> ``` This code creates a 600x600 canvas, generates a maze using the recursive backtracker algorithm, and then animates the A* pathfinding algorithm to find a path from the start (green) to the end (red) of the maze. The path is shown in blue as it's being discovered.
TAKE IT TO CODE
A single self-contained HTML file — copy it and open in any browser, or reuse the prompt with your own pi-agent run.
RUN
EVALUATION
Canvas remains completely blank white throughout; a JS error prevented the maze from rendering.
Vision grade · anthropic/claude-sonnet-5
MEASUREMENTS
Raw readings from the capture, not a verdict — the grade above is the only judgement. Percentages are how much of the screen changed; the measure under-reports sparse motion on a dark background.
- Cannot set properties of undefined (setting '1')