Last active
December 28, 2024 22:58
-
-
Save taesiri/401e85a386fa86057784333624c9bab6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<style> | |
.container { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
gap: 20px; | |
padding: 20px; | |
font-family: Arial, sans-serif; | |
} | |
.maze { | |
border: 2px solid #333; | |
cursor: pointer; | |
} | |
.controls { | |
display: flex; | |
flex-direction: column; | |
gap: 15px; | |
align-items: center; | |
} | |
.control-group { | |
display: flex; | |
gap: 15px; | |
align-items: center; | |
} | |
button { | |
padding: 8px 16px; | |
background-color: #4CAF50; | |
color: white; | |
border: none; | |
border-radius: 4px; | |
cursor: pointer; | |
} | |
button:hover { | |
background-color: #45a049; | |
} | |
label { | |
font-weight: bold; | |
} | |
input[type="range"] { | |
width: 200px; | |
} | |
.value-display { | |
min-width: 30px; | |
text-align: left; | |
} | |
.position-controls { | |
display: flex; | |
gap: 20px; | |
margin-top: 10px; | |
} | |
.position-group { | |
display: flex; | |
flex-direction: column; | |
gap: 5px; | |
align-items: center; | |
} | |
.coordinate-inputs { | |
display: flex; | |
gap: 10px; | |
} | |
.coordinate-input { | |
width: 50px; | |
} | |
.instructions { | |
font-style: italic; | |
color: #666; | |
margin-top: 5px; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h2>Random Maze Generator</h2> | |
<canvas id="mazeCanvas" class="maze"></canvas> | |
<div class="controls"> | |
<div class="control-group"> | |
<label>Width:</label> | |
<input type="range" id="widthSlider" min="5" max="40" value="20"> | |
<span id="widthValue" class="value-display">20</span> | |
</div> | |
<div class="control-group"> | |
<label>Height:</label> | |
<input type="range" id="heightSlider" min="5" max="40" value="20"> | |
<span id="heightValue" class="value-display">20</span> | |
</div> | |
<div class="control-group"> | |
<label>Complexity:</label> | |
<input type="range" id="complexitySlider" min="0" max="100" value="100"> | |
<span id="complexityValue" class="value-display">100%</span> | |
</div> | |
<div class="position-controls"> | |
<div class="position-group"> | |
<label style="color: #4CAF50;">Start Position:</label> | |
<div class="coordinate-inputs"> | |
<input type="number" id="startX" class="coordinate-input" min="0" value="0"> | |
<input type="number" id="startY" class="coordinate-input" min="0" value="0"> | |
</div> | |
</div> | |
<div class="position-group"> | |
<label style="color: #f44336;">End Position:</label> | |
<div class="coordinate-inputs"> | |
<input type="number" id="endX" class="coordinate-input" min="0" value="19"> | |
<input type="number" id="endY" class="coordinate-input" min="0" value="19"> | |
</div> | |
</div> | |
</div> | |
<p class="instructions">Click on the maze to set start (first click) and end (second click) positions</p> | |
<button onclick="generateNewMaze()">Generate New Maze</button> | |
</div> | |
</div> | |
<script> | |
const canvas = document.getElementById('mazeCanvas'); | |
const ctx = canvas.getContext('2d'); | |
const cellSize = 20; | |
let width, height, complexity; | |
let startPos = { x: 0, y: 0 }; | |
let endPos = { x: 19, y: 19 }; | |
let clickMode = 'none'; | |
// Setup slider and input events | |
const widthSlider = document.getElementById('widthSlider'); | |
const heightSlider = document.getElementById('heightSlider'); | |
const complexitySlider = document.getElementById('complexitySlider'); | |
const startXInput = document.getElementById('startX'); | |
const startYInput = document.getElementById('startY'); | |
const endXInput = document.getElementById('endX'); | |
const endYInput = document.getElementById('endY'); | |
function updateStartEndInputs() { | |
startXInput.value = startPos.x; | |
startYInput.value = startPos.y; | |
endXInput.value = endPos.x; | |
endYInput.value = endPos.y; | |
// Update max values | |
startXInput.max = width - 1; | |
startYInput.max = height - 1; | |
endXInput.max = width - 1; | |
endYInput.max = height - 1; | |
} | |
widthSlider.addEventListener('input', function() { | |
document.getElementById('widthValue').textContent = this.value; | |
updateCanvasSize(); | |
}); | |
heightSlider.addEventListener('input', function() { | |
document.getElementById('heightValue').textContent = this.value; | |
updateCanvasSize(); | |
}); | |
complexitySlider.addEventListener('input', function() { | |
document.getElementById('complexityValue').textContent = this.value + '%'; | |
}); | |
[startXInput, startYInput, endXInput, endYInput].forEach(input => { | |
input.addEventListener('change', function() { | |
const value = Math.min(Math.max(0, parseInt(this.value) || 0), parseInt(this.max)); | |
this.value = value; | |
if (this.id.startsWith('start')) { | |
startPos.x = startXInput.value; | |
startPos.y = startYInput.value; | |
} else { | |
endPos.x = endXInput.value; | |
endPos.y = endYInput.value; | |
} | |
drawMaze(); | |
}); | |
}); | |
canvas.addEventListener('click', function(event) { | |
const rect = canvas.getBoundingClientRect(); | |
const x = Math.floor((event.clientX - rect.left) / cellSize); | |
const y = Math.floor((event.clientY - rect.top) / cellSize); | |
if (x >= 0 && x < width && y >= 0 && y < height) { | |
if (clickMode === 'none' || clickMode === 'end') { | |
startPos = { x, y }; | |
clickMode = 'start'; | |
} else { | |
endPos = { x, y }; | |
clickMode = 'end'; | |
} | |
updateStartEndInputs(); | |
drawMaze(); | |
} | |
}); | |
function updateCanvasSize() { | |
width = parseInt(widthSlider.value); | |
height = parseInt(heightSlider.value); | |
complexity = parseInt(complexitySlider.value); | |
canvas.width = width * cellSize; | |
canvas.height = height * cellSize; | |
// Ensure start and end positions are within bounds | |
startPos.x = Math.min(startPos.x, width - 1); | |
startPos.y = Math.min(startPos.y, height - 1); | |
endPos.x = Math.min(endPos.x, width - 1); | |
endPos.y = Math.min(endPos.y, height - 1); | |
updateStartEndInputs(); | |
} | |
let maze = []; | |
function initializeMaze() { | |
maze = []; | |
for (let y = 0; y < height; y++) { | |
const row = []; | |
for (let x = 0; x < width; x++) { | |
row.push({ | |
x, | |
y, | |
walls: { top: true, right: true, bottom: true, left: true }, | |
visited: false | |
}); | |
} | |
maze.push(row); | |
} | |
} | |
function getNeighbors(cell, unvisitedOnly = true) { | |
const neighbors = []; | |
const { x, y } = cell; | |
if (y > 0) neighbors.push({ cell: maze[y-1][x], direction: 'top' }); | |
if (x < width-1) neighbors.push({ cell: maze[y][x+1], direction: 'right' }); | |
if (y < height-1) neighbors.push({ cell: maze[y+1][x], direction: 'bottom' }); | |
if (x > 0) neighbors.push({ cell: maze[y][x-1], direction: 'left' }); | |
return unvisitedOnly ? neighbors.filter(n => !n.cell.visited) : neighbors; | |
} | |
function removeWalls(current, next, direction) { | |
current.walls[direction] = false; | |
const oppositeWalls = { | |
top: 'bottom', | |
right: 'left', | |
bottom: 'top', | |
left: 'right' | |
}; | |
next.walls[oppositeWalls[direction]] = false; | |
} | |
function generateMaze() { | |
initializeMaze(); | |
// Start generation from the start position | |
const stack = []; | |
const start = maze[startPos.y][startPos.x]; | |
start.visited = true; | |
stack.push(start); | |
while (stack.length > 0) { | |
const current = stack[stack.length - 1]; | |
const neighbors = getNeighbors(current); | |
if (neighbors.length === 0) { | |
stack.pop(); | |
continue; | |
} | |
const { cell: next, direction } = neighbors[Math.floor(Math.random() * neighbors.length)]; | |
removeWalls(current, next, direction); | |
next.visited = true; | |
stack.push(next); | |
} | |
// Add additional paths based on complexity | |
const maxExtraPaths = Math.floor((width * height) * (1 - complexity / 100)); | |
let addedPaths = 0; | |
while (addedPaths < maxExtraPaths) { | |
const x = Math.floor(Math.random() * width); | |
const y = Math.floor(Math.random() * height); | |
const cell = maze[y][x]; | |
const neighbors = getNeighbors(cell, false); | |
if (neighbors.length > 0) { | |
const { cell: next, direction } = neighbors[Math.floor(Math.random() * neighbors.length)]; | |
if (cell.walls[direction]) { // Only if wall exists | |
removeWalls(cell, next, direction); | |
addedPaths++; | |
} | |
} | |
} | |
} | |
function drawMaze() { | |
ctx.fillStyle = 'white'; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
ctx.strokeStyle = '#333'; | |
ctx.lineWidth = 2; | |
for (let y = 0; y < height; y++) { | |
for (let x = 0; x < width; x++) { | |
const cell = maze[y][x]; | |
const startX = x * cellSize; | |
const startY = y * cellSize; | |
ctx.beginPath(); | |
if (cell.walls.top) { | |
ctx.moveTo(startX, startY); | |
ctx.lineTo(startX + cellSize, startY); | |
} | |
if (cell.walls.right) { | |
ctx.moveTo(startX + cellSize, startY); | |
ctx.lineTo(startX + cellSize, startY + cellSize); | |
} | |
if (cell.walls.bottom) { | |
ctx.moveTo(startX, startY + cellSize); | |
ctx.lineTo(startX + cellSize, startY + cellSize); | |
} | |
if (cell.walls.left) { | |
ctx.moveTo(startX, startY); | |
ctx.lineTo(startX, startY + cellSize); | |
} | |
ctx.stroke(); | |
} | |
} | |
// Draw start position (green) | |
ctx.fillStyle = '#4CAF50'; | |
ctx.fillRect( | |
startPos.x * cellSize + 2, | |
startPos.y * cellSize + 2, | |
cellSize - 4, | |
cellSize - 4 | |
); | |
// Draw end position (red) | |
ctx.fillStyle = '#f44336'; | |
ctx.fillRect( | |
endPos.x * cellSize + 2, | |
endPos.y * cellSize + 2, | |
cellSize - 4, | |
cellSize - 4 | |
); | |
} | |
function generateNewMaze() { | |
updateCanvasSize(); | |
generateMaze(); | |
drawMaze(); | |
} | |
// Initial generation | |
generateNewMaze(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment