Show code
<think>
Okay, let's tackle this problem. The user wants an HTML file that generates a maze using recursive backtracking and then solves it with a wanderer. First, I need to make sure the maze is created properly. The grid should be 25 columns by 18 rows. The recursive backtracking algorithm starts at a random cell, carves paths to unvisited neighbors, and keeps a stack for backtracking. I'll have to represent the grid with walls, maybe using a 2D array where each cell has wall information.
Next, the solving phase: choose a wall-follower or depth-first search. Wall-follower is simpler to animate. The wanderer should move step by step, highlighting its path and marking visited cells. The counters for carved cells and steps taken need to be updated.
I need to use the canvas element for drawing. The animation should be smooth with requestAnimationFrame. The maze should restart automatically after solving. Also, ensure no external libraries are used.
First, set up the HTML structure with a canvas and two divs for counters. Then the JavaScript part.
For the maze generation: Initialize grid with all walls. Start at a random cell. Keep track of visited cells and the stack. When moving to a new cell, remove the common wall between current and next cell. Push next cell onto stack. When stuck, backtrack using the stack. Draw the maze step by step, highlighting the current cell and stack path. Show the counter for carved cells.
Solving phase: When all cells are visited, start the wanderer at one corner (like top-left), and move towards the exit (bottom-right). The wanderer should follow the right-hand rule, for example. Mark each step, track visited cells, and backtrack when hitting a dead end. The solution path needs to be distinguished from dead ends. Show steps taken, and when the exit is reached, reset the maze.
I need to handle the animation loop with requestAnimationFrame. During generation, each step (carving a path) is drawn, then the solving phase steps through the wanderer's moves. Need flags to know when to switch phases.
Potential challenges: Implementing the recursive backtracking correctly without getting stuck, especially the stack management. Also, ensuring the solving algorithm (wall follower) works correctly on the generated maze. Drawing the maze with walls and highlighting paths.
Let me outline the steps in code:
1. Setup canvas and context.
2. Create grid structure (maybe a 2D array where each cell has walls and visited status).
3. Initialize grid with walls.
4. Maze generation loop:
a. Start at random cell, mark as visited.
b. While stack not empty:
i. Look for unvisited neighbors.
ii. If any, pick random, remove walls between current and neighbor.
iii. Push current to stack, move to neighbor, increment counter.
iv. Else, backtrack to previous cell from stack.
5. Draw the maze step by step, highlighting current path and stack.
6. Once maze is done, start solving:
a. Initialize wanderer at start cell.
b. Use wall follower algorithm (like right-hand on walls).
c. Track path, mark visited cells, highlight steps.
d. When exit is reached, reset.
Wait, the solving needs to be animated as well. So after the maze is done generating, the solving steps are handled in the animation loop. The animation loop will switch between phases.
Variables needed: grid, current cell, stack, isGenerating, solvingPhase, wanderer position, etc.
In code structure:
The canvas is 25 cols and 18 rows. Let's set cell size to 20px (so canvas width 500, height 360). But maybe smaller for better visibility. Let me calculate: 25x20 = 500, 18x20=360. That might be okay, but the user might prefer a smaller size? But the problem says 'about' so maybe that's okay.
Now, the grid array. Each cell can have walls (top, right, bottom, left) and visited status.
For the recursive backtracking:
Initialize stack with first cell. The first cell is random. Then, while stack not empty:
currentCell = stack.top
Check all neighbors (up, down, left, right), those not visited.
If there are unvisited neighbors:
Pick one randomly.
Remove the walls between current and neighbor.
Mark neighbor as visited.
Push current to stack, set current to neighbor.
Else:
Pop from stack (backtrack).
So the stack is used for backtracking.
Drawing:
During generation, each step (when a new cell is added to stack) should draw the current cell and the path in the stack. Maybe draw the current cell in red, and stack path in another color. The walls are drawn by checking each cell's walls.
The counters: cellsCarved is the number of visited cells. Since total cells are 25*18=450, once that's reached, switch to solving.
For solving, the wanderer moves step by step. The algorithm could be the right-hand wall follower: always turn right (if possible), else go straight, else turn left, else backtrack. Wait, but how to track direction? Or, use the standard algorithm where the wanderer follows the right wall.
Alternatively, use the depth-first search for solving, but that's similar to how the maze was generated. But the user mentioned wall-follower, so better to pick that.
The solving steps:
Start at (0,0), target (24,17). Wanderer has a direction and current position. Each step:
Check adjacent cells based on the current direction. For example, using the right-hand rule: the wanderer tries to turn right, then straight, then left, then backtrack.
But to implement this, perhaps track the current direction and check the walls in that direction.
Alternatively, here's a step-by-step approach for right-hand wall follower:
At each step, the wanderer looks to turn right (ahead, then right, then left, then back). The exact direction steps need to be defined.
Alternatively, the algorithm could be:
While not at exit:
1. Check if the cell to the right (relative to direction) is open (no wall there) and unvisited.
- If yes, turn right and move forward.
2. Else check forward direction.
- If open and unvisited, move forward.
3. Else check left direction.
- If open and unvisited, turn left and move forward.
4. Else, backtrack (reverse direction and move back, but need a stack for that).
Hmm, this becomes similar to the generating algorithm. Alternatively, using a stack to track path and backtrack when stuck.
Wait, perhaps for simplicity, the solving can use a stack-based approach, keeping track of the path taken. When a dead end is reached, backtrack.
Alternatively, since the maze is perfect (no cycles), the wall follower should find the path.
Implementation steps for solving:
Initialize wanderer at (0,0), direction (e.g., right). Each step:
Check directions in order (based on the rule, like right-hand). Let's say, the wanderer tries to turn right first. So, for each step, the wanderer looks in the direction of the right wall. For example, if facing east, check north wall (right side). Wait, maybe better to use a direction vector and check walls.
Alternatively, let's represent the current direction as a vector (dx, dy). The order of checking directions for turning right would be:
current direction is (dx, dy). The right turn direction would be ( -dy, dx ). For example:
If moving east (dx=1, dy=0), right turn is north (dx=0, dy=-1)?
Wait, directions:
Moving east: direction (1,0). Right turn (clockwise) would be north? Wait, no:
If facing east (right), turning right would face south (down). Hmm, perhaps better to precompute the directions.
Alternatively, represent direction as 0: East, 1: South, 2: West, 3: North. Then turning right would be (current +1) mod 4.
But maybe this is getting too complicated. Alternatively, track the current direction as:
north, east, south, west, each direction has a direction vector and a corresponding wall to check.
The right-hand rule says that the wanderer keeps their right hand on the wall. So, they turn right as much as possible.
The algorithm is:
1. Move forward if possible (in current direction), that is, if there's no wall in front and the next cell is unvisited.
Wait, but in a maze, the walls are between cells. So to move in direction (dx, dy), check if the current cell's adjacent wall allows movement.
Alternatively, when facing direction 'dir', the next cell is current.x + dx[dir], current.y + dy[dir].
The wall for that direction would be:
- If direction is east (dx=1), then the current cell's east wall must be missing, and the next cell's west wall must also be missing (since walls are shared). Wait, but in the grid structure, each cell has its own walls. So perhaps, to move east, we can check if the current cell's east wall is down (i.e., the neighbor to the east has their west wall also down).
Hmm, this might complicate things. Alternatively, when moving to a neighbor cell, we can check if the current cell's wall in that direction is removed.
Wait, in the grid structure, each cell's walls are stored. So to move east, check if the current cell's east wall is not present.
Thus, for the wanderer's direction:
They choose to move in an order (prefer right turn) and check if that direction's wall is open and the cell hasn't been visited yet.
Alternatively, here's a standard right-hand algorithm implementation steps:
The wanderer moves step by step, always moving in a direction that turns right when possible.
The exact steps can be found in pseudocode, but need to map it to code.
Perhaps the following approach:
At each step, the wanderer checks the cell to the right (relative to current direction). If there's a path there (no wall) and it hasn't been visited, move there and change direction to that right direction.
Else check forward direction: if path and unvisited, move forward, direction stays same.
Else check left direction (turn left): path and unvisited? Then move and change direction.
Else, backtrack (reverse direction, but need to have a stack of moves to know where to backtrack).
Hmm, this is getting too involved. Maybe implement the wall follower as follows:
The wanderer always tries to turn right, then go straight, then left, then reverse.
But to do this, need to determine the directions in order.
Let me think of the directions as an array. For each current direction, the order of preference is right, straight, left, back.
Wait, perhaps a better approach:
The wanderer's current direction is, say, east (right). The right-hand rule says that they keep their right hand on the wall. So, when moving, they first check the direction to the right (north?), then straight, then left (south?), then back.
Alternatively, using the standard "right-hand rule" algorithm from Wikipedia:
1. If there is no wall to the right, turn right (clockwise) and proceed straight.
2. Else if there is a wall to the right and in front, turn left (counter-clockwise).
3. Else proceed straight.
But to implement this, need to check the walls in the current direction's right direction.
Alternatively, let's use code for the right hand wall follower:
function move() {
// Determine the directions in order: right, forward, left, back
// For the current direction, the order of directions to try is right turn, then forward, etc.
// Directions represented as indices:
// 0: East, 1: South, 2: West, 3: North
var dirs = [[1, 0], [0, 1], [-1, 0], [0, -1]];
var dirNames = ['East', 'South', 'West', 'North'];
var currentDir = wanderer.direction;
var nextDir = currentDir;
// Check right direction first
var rightDir = (currentDir + 1) % 4;
// Check if can turn right
var dx = dirs[rightDir][0], dy = dirs[rightDir][1];
var nx = wanderer.x + dx;
var ny = wanderer.y + dy;
// Check if this cell is within grid and path is available (no wall in that direction)
if (nx >=0 && ny >=0 && nx < cols && ny < rows) {
var currentCell = grid[ny][nx]; // assuming rows are y, columns x
// Wait, need to check the wall between current cell and the direction.
// Maybe the current cell's wall in the direction's direction.
var wall = getWallForDirection(rightDir);
// Not sure. Maybe better to check the cell's walls.
// Alternatively, check if the current cell's wall in that direction is open.
// Suppose the direction's wall is stored in the cell's walls array.
// For example, if moving to the right direction (east), then check east wall.
// Maybe the current cell's walls are stored as [top, right, bottom, left] or similar.
// Let's assume each cell has walls as an array [top, right, bottom, left].
// For direction rightDir (e.g., East direction index 0):
// The wall to check is the current cell's right wall (if moving east), or the neighbor's left wall?
// This is getting complicated. Maybe better to precompute for each possible direction.
// Alternatively, when moving in the direction dx, dy, check that the current cell's wall in that direction is removed.
// Let's say that to move in direction dx, dy, the current cell must have that direction's wall down.
// For example, direction East (dx=1, dy=0), the current cell's east wall must be false (down).
var cell = grid[ny][nx]; // Wait, actually the current cell is (wanderer.x, wanderer.y), and the next cell is (nx, ny).
// The current cell's east wall is cell.east (or whatever representation).
// The direction from current cell to (nx, ny) is dx, dy. So, the current cell's wall in that direction must be down.
// This requires that the current cell's wall in that direction is false (removed).
// For example, moving east: cell.east == false?
// Also, the next cell's west wall must be down as well, but since walls are shared, maybe we don't need to check.
// Anyway, assuming that the walls are represented such that if current cell's east wall is down, then the next cell to the east is accessible.
// So if current cell has wall in the desired direction (dx, dy) as removed (false), then can move.
// So in this case, to move in direction rightDir (east in example), if cell's east wall is down:
// but since cell is the next cell? Wait, no. The current cell's east wall is between current and next cell.
// This is getting too tangled. Maybe a better approach is to represent each cell as having walls for top, right, bottom, left, and when moving to an adjacent cell, check if the current cell's wall in that direction is down (false).
// So, for the current position, to move east (dx=1, dy=0), check if current cell's east wall is down (no wall).
// Then, the next cell is (x+1, y). That cell's west wall would also be down, but since we have a grid, when we carved the maze, the walls are removed symmetrically.
// So, to ensure, whenever you move east, the current cell's east wall is already removed.
// So, for the right-hand rule, the wanderer can check the current cell's right wall (the direction they want to turn right) to see if it's open.
// Maybe this is too time-consuming, but perhaps I can proceed with code.
// Suppose for the current direction, we can iterate through possible directions in the order of right, forward, left, back.
// Let's try to code this step by step.
// First, current direction is dir (index 0-3). The directions array is as before.
// The current position is (x,y). We need to try moving in the right direction first.
// The right direction is (currentDir +1) %4. Let's get dx and dy for that direction.
var rightDir = (currentDir +1) %4;
var dx = dirs[rightDir][0], dy = dirs[rightDir][1];
var nx = wanderer.x + dx;
var ny = wanderer.y + dy;
// Check if cell is within bounds.
if (nx >=0 && ny >=0 && nx < cols && ny < rows) {
// Check if the current cell's wall in that direction is down (i.e., can move there)
var wall = getWallInDirection(dirs[rightDir]);
if (!wall && !grid[ny][nx].visited) {
// Can move there.
// Update direction to rightDir, move to nx, ny.
wanderer.direction = rightDir;
wanderer.x = nx;
wanderer.y = ny;
return; // done moving this step.
}
}
// If not, try forward direction.
// Same with forward direction, which is the current direction.
dx = dirs[currentDir][0], dy = dirs[currentDir][1];
nx = wanderer.x + dx;
ny = wanderer.y + dy;
// check if can move forward.
if (nx >=0 && ny < rows) {
// Check current cell's wall in current direction is down.
// and next cell not visited.
// etc.
}
// etc. for other directions.
// This is quite involved. Perhaps I should look for a different approach or use a different algorithm.
// Alternatively, let's assume that the maze is generated with passages between cells, and when moving, the wanderer can move to any adjacent cell where the wall is down and the cell hasn't been visited yet, choosing the right direction first.
// To implement the right-hand rule, the order of preference is to turn right (clockwise), then go straight, then left, then back.
// Thus, for each step:
// Create an array of direction indices in the order of right, forward, left, back.
var directionsToTry = [];
var currentDir = wanderer.direction;
directionsToTry.push( (currentDir +1)%4 ); // right
directionsToTry.push( currentDir ); // forward
directionsToTry.push( (currentDir -1 +4)%4 ); // left
directionsToTry.push( (currentDir +2)%4 ); // back (opposite direction)
// Iterate through each direction in order:
for (var i=0; i <4; i++) {
var dir = directionsToTry[i];
var dx = dirs[dir][0], dy = dirs[dir][1];
var nx = wanderer.x + dx;
var ny = wanderer.y + dy;
if (nx >=0 && ny >=0 && nx < cols && ny < rows) {
var wall = getWallInDirection(dir);
if (!wall) { // path is clear
if (grid[ny][nx].visited) continue; // already been here
// can move here
wanderer.direction = dir;
wanderer.x = nx;
wanderer.y = ny;
// mark as visited?
grid[ny][nx].visited = true;
return;
}
}
}
// If no direction found, need to backtrack. But how?
// For backtracking, have a stack of previous positions.
// This suggests that we need to track the path taken and possibly have an undo option, but it complicates things.
// This is getting too complicated for this exercise. Maybe I can proceed by simplifying the algorithm.
// For the purposes of this problem, since the user asked to create a maze and animate a solution (using a simple walk), perhaps using a simple depth-first search or backtracking approach would be better, but the user asked for the right-hand rule.
// Alternatively, I'll proceed with the code structure, even if some parts are placeholders.
Now, stepping back, the overall structure of the code will be:
- Set up the grid with walls (all walls initially present except the start and end? Or randomly?)
Wait, actually the maze creation is part of the problem? The user just said "Create a maze and solve it with a right-hand rule solution using JavaScript." So I need to generate a maze and then solve it using right-hand rule.
Therefore, first, I need to implement a maze generation algorithm (like recursive backtracking, Prim's algorithm, etc.), then implement the right-hand rule solver.
But to keep it manageable, perhaps use a simple maze generation, like recursive backtracking, which also allows the solver to find a path easily.
Then, the animation would show the maze and the solver's path.
However, since the user asked to present the code here, perhaps in a compact form.
But given the complexity, maybe provide a code outline with comments.
Alternatively, here's an outline of the steps:
1. Initialize the grid with walls (all cells have all walls).
2. Generate the maze using a recursive backtracking algorithm, leaving a path from start to end.
3. Implement the right-hand rule solver starting at the start point and moving towards the end.
4. Animate the solver's movement step by step.
However, considering the space and time constraints, I'll outline the code with comments, focusing on the key parts.
First, the maze grid:
Each cell can be represented as an object with walls (top, right, bottom, left) and visited status.
But for simplicity, let's use a 2D array of cells:
var maze = new Array(rows);
for (var y=0; y<rows; y++) {
maze[y] = new Array(cols);
for (var x=0; x<cols; x++) {
maze[y][x] = {
walls: { top: true, right: true, bottom: true, left: true },
visited: false
};
}
}
Then, generate maze using recursive backtracking:
function generateMaze(x, y) {
maze[y][x].visited = true;
// Shuffle the directions
var directions = [[1,0,'right'], [-1,0,'left'], [0,1,'bottom'], [0,-1,'top']];
directions.sort(() => 0.5 - Math.random());
for (var i=0; i<directions.length; i++) {
var dx = directions[i][0];
var dy = directions[i][1];
var dir = directions[i][2];
var nx = x + dx;
var ny = y + dy;
if (nx >=0 && ny >=0 && nx < cols && ny < rows) {
if (!maze[ny][nx].visited) {
// Remove the wall between current and next cell
maze[y][x].walls[dir] = false;
if (dir === 'right') {
maze[ny][nx].walls.left = false;
} else if (dir === 'left') {
maze[ny][nx].walls.right = false;
}
// else similar for top and bottom.
generateMaze(nx, ny);
}
}
}
}
But this is a rough sketch. The exact wall removals depend on direction.
Once the maze is generated, the solver can start at the entrance (e.g., top-left cell) and traverse using right-hand rule.
The solver will need to keep track of its current position and direction, and move accordingly.
The animation would involve drawing the maze and updating the solver's position each step.
Given that this is a text-based answer, I'll provide a code skeleton with comments explaining each part.
But to actually provide a complete JavaScript code with animation, it would be quite lengthy, so here's a concise version with some simplifications:
```javascript
var canvas = document.getElementById('mazeCanvas');
var ctx = canvas.getContext('2d');
var cols = 20, rows = 20;
var cellSize = 20;
var maze = []; // 2D array representing the maze.
// Initialize maze with walls.
function initMaze() {
maze = Array(rows);
for (var y=0; y<rows; y++) {
maze[y] = Array(cols);
for (var x=0; x<cols; x++) {
maze[y][x] = {
walls: { top: true, right: true, bottom: true, left: true },
visited: false
};
}
}
}
// Maze generation using recursive backtracking.
function generateMaze(x, y) {
maze[y][x].visited = true;
var directions = [[1,0,'right'], [-1,0,'left'], [0,1,'bottom'], [0,-1,'top']];
directions.sort(() => 0.5 - Math.random());
for (var i=0; i<directions.length; i++) {
var dx = directions[i][0], dy = directions[i][1];
var dir = directions[i][2];
var nx = x + dx;
var ny = y + dy;
if (nx >=0 && ny >=0 && nx < cols && ny < rows) {
if (!maze[ny][nx].visited) {
// Remove wall between current and next cell
maze[y][x].walls[dir] = false;
if (dir === 'right') {
maze[ny][nx].walls.left = false;
} else if (dir === 'left') {
maze[ny][nx].walls.right = false;
} else if (dir === 'top') {
maze[ny][nx].walls.bottom = false;
} else if (dir === 'bottom') {
maze[ny][nx].walls.top = false;
}
generateMaze(nx, ny);
}
}
}
}
// Start solver at (0,0), end at (cols-1, rows-1).
var solver = { x:0, y:0, direction: 'right' };
var directions = ['right', 'down', 'left', 'up']; // order of directions for right-hand rule.
function drawMaze() {
ctx.clearRect(0,0,canvas.width, canvas.height);
for (var y=0; y<rows; y++) {
for (var x=0; x<cols; x++) {
var cell = maze[y][x];
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
// draw walls
if (cell.walls.top) {
ctx.strokeRect(x*cellSize, y*cellSize, cellSize, 1);
}
if (cell.walls.right) {
ctx.strokeRect((x+1)*cellSize -1, y*cellSize, 1, cellSize);
}
if (cell.walls.bottom) {
ctx.strokeRect(x*cellSize, (y+1)*cellSize -1, cellSize, 1);
}
if (cell.walls.left) {
ctx.strokeRect(x*cellSize, y*cellSize, 1, cellSize);
}
}
}
// draw solver
ctx.fillStyle = 'red';
ctx.fillRect(solver.x*cellSize+4, solver.y*cellSize+4, cellSize-8, cellSize-8);
}
function solveMaze() {
// Implement right-hand rule here.
// Check the wall on the right, move forward if possible.
// If not, turn right, etc.
// This is a simplified placeholder.
// For example:
var currentCell = maze[solver.y][solver.x];
var dir = solver.direction;
var checkWall = function(dx, dy) {
// Determine if the wall in direction (dx, dy) exists
};
// Determine next direction based on right-hand rule.
// This needs to be more robust.
// For now, just move in the current direction if possible.
// Need to implement actual logic here.
}
initMaze();
generateMaze(0,0);
setInterval(drawMaze, 100);
```
However, this code is incomplete and lacks the actual solver logic. The solver function needs to track the current position and direction, check the walls on the right, and move accordingly.
To properly implement the right-hand rule:
1. The solver always extends the right hand and moves forward if the wall is absent.
2. If the wall is present, turn clockwise until a path is found.
This requires checking the adjacent cell's walls in the direction of the right hand.
Given time constraints, a simplified version might be:
At each step:
- Check if there's a wall to the right (relative to current direction).
- If no wall, turn right and move forward.
- If wall exists, move forward if possible.
- If neither, turn left (or back).
But this requires precise handling of directions and cell coordinates.
Given the complexity, here's a more detailed step-by-step plan for the solver function:
First, define directions as an array with their coordinates and order for rotation.
var directions = [
{x:1, y:0, name:'right'}, // East
{x:0, y:1, name:'down'}, // South
{x:-1, y:0, name:'left'}, // West
{x:0, y:-1, name:'up'} // North
];
The solver has a current direction index (0 for right, etc.).
function solveStep() {
var currentDirIndex = solver.directionIndex;
var currentDir = directions[currentDirIndex];
// Check right wall (next direction)
var rightDirIndex = (currentDirIndex +1) %4;
var rightDir = directions[rightDirIndex];
var rightX = solver.x + rightDir.x;
var rightY = solver.y + rightDir.y;
// Check if the wall to the right is present.
// The wall in the cell's direction.
var currentCell = maze[solver.y][solver.x];
var wallPresent = true;
if (rightDir.name === 'right') {
wallPresent = currentCell.walls.right;
} else if (rightDir.name === 'down') {
wallPresent = currentCell.walls.bottom;
} else if (rightDir.name === 'left') {
wallPresent = currentCell.walls.left;
} else if (rightDir.name === 'up') {
wallPresent = currentCell.walls.top;
}
if (!wallPresent) {
// Turn right and move
solver.directionIndex = rightDirIndex;
solver.x += rightDir.x;
solver.y += rightDir.y;
} else {
// Check current direction
var currentX = solver.x + currentDir.x;
var currentY = solver.y + currentDir.y;
var currentWall = ...; // check wall in current direction
if (!currentWall) {
solver.x += currentDir.x;
solver.y += currentDir.y;
} else {
// Turn left (or back)
// Continue until path found
// For simplicity, turn left
solver.directionIndex = (currentDirIndex -1 +4)%4;
}
}
// Check if at exit (cols-1, rows-1)
if (solver.x === cols-1 && solver.y === rows-1) {
clearInterval(interval);
}
}
This is a rough sketch. The actual wall checking needs to reference the maze cell's walls correctly.
Given the time, perhaps the final code should be as follows, combining all parts with proper details:
Wait, but due to the complexity and length, I'll provide a full code example here, though it may be lengthy.