<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Maze Navigator</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Dynamic Maze Navigator</h1>
<div id="maze-container">
<!-- Maze will be generated here by JavaScript -->
</div>
<p id="message">Use arrow keys to move. Find the goal (Yellow)!</p>
<script src="script.js"></script>
</body>
</html>
body {
font-family: sans-serif;
display: flex;
flex-direction: column;
align-items: center;
background-color: #f0f0f0;
}
#maze-container {
display: grid; /* We'll set columns/rows with JS */
border: 3px solid #333;
background-color: #fff; /* Floor color */
margin-top: 20px;
position: relative; /* For potential absolute positioning inside */
}
.cell {
width: 40px; /* Size of each cell */
height: 40px;
box-sizing: border-box; /* Include border in size */
display: flex;
justify-content: center;
align-items: center;
font-size: 1.5em;
transition: background-color 0.3s ease; /* Smooth transitions for dynamic walls */
}
/* Cell Types */
.floor {
background-color: #fff;
}
.wall {
background-color: #555; /* Static Wall */
border: 1px solid #444;
}
.dynamic-wall {
background-color: #888; /* Closed Dynamic Wall */
border: 1px solid #777;
}
.dynamic-wall.open {
background-color: #e0e0e0; /* Open Dynamic Wall - slightly different from floor */
border: 1px dashed #ccc; /* Visual cue that it can close */
}
.player {
background-color: blue;
border-radius: 50%;
width: 80%; /* Make player slightly smaller than cell */
height: 80%;
z-index: 10; /* Ensure player is visible */
box-shadow: 0 0 5px rgba(0, 0, 0, 0.5);
}
.goal {
background-color: gold;
border: 1px solid orange;
}
.pressure-plate {
background-color: lightcoral; /* Inactive */
border-radius: 20%;
width: 70%;
height: 70%;
border: 2px outset pink;
}
.pressure-plate.active {
background-color: darkred; /* Active */
border: 2px inset red;
}
#message {
margin-top: 15px;
font-weight: bold;
color: #333;
}
document.addEventListener('DOMContentLoaded', () => {
const mazeContainer = document.getElementById('maze-container');
const messageElement = document.getElementById('message');
// --- Maze Definition ---
// 0: Floor
// 1: Wall (Static)
// 2: Player Start
// 3: Goal
// 4: Pressure Plate (Inactive)
// 5: Pressure Plate (Active) - Will be set dynamically
// 6: Dynamic Wall (Closed)
// 7: Dynamic Wall (Open) - Represents the state, rendered differently
let mazeData = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 0, 0, 1, 6, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 1, 0, 1],
[1, 0, 0, 0, 1, 1, 0, 1, 3, 1],
[1, 1, 1, 4, 1, 6, 0, 1, 1, 1], // Plate at row 5, col 3
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
// --- Dynamic Element Links ---
// Map pressure plate coordinates (r,c) to the dynamic walls they control (array of {r, c})
const plateLinks = {
'5,3': [ { r: 1, c: 5 }, { r: 5, c: 5 } ] // Plate at 5,3 controls walls at 1,5 and 5,5
// Add more links here for other plates
};
// --- Game State ---
let playerPos = { r: 0, c: 0 };
let gameActive = true;
const numRows = mazeData.length;
const numCols = mazeData[0].length;
// --- Initialization ---
function initGame() {
// Find player start position
for (let r = 0; r < numRows; r++) {
for (let c = 0; c < numCols; c++) {
if (mazeData[r][c] === 2) {
playerPos = { r, c };
mazeData[r][c] = 0; // Change start position to floor after finding
break;
}
}
}
renderMaze();
document.addEventListener('keydown', handleKeyPress);
messageElement.textContent = "Use arrow keys to move. Find the goal (Yellow)!";
}
// --- Rendering ---
function renderMaze() {
mazeContainer.innerHTML = ''; // Clear previous state
mazeContainer.style.gridTemplateColumns = `repeat(${numCols}, 40px)`;
mazeContainer.style.gridTemplateRows = `repeat(${numRows}, 40px)`;
for (let r = 0; r < numRows; r++) {
for (let c = 0; c < numCols; c++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.r = r; // Store coordinates for potential interaction later
cell.dataset.c = c;
let cellType = mazeData[r][c];
let isPlayerOnCell = (playerPos.r === r && playerPos.c === c);
let cellContent = null; // Div for player, plate etc.
// Determine base cell class (wall, floor, dynamic states)
switch (cellType) {
case 1: cell.classList.add('wall'); break;
case 3: cell.classList.add('goal'); break;
case 4: // Inactive Plate
cell.classList.add('floor'); // Plate sits on floor
cellContent = document.createElement('div');
cellContent.classList.add('pressure-plate');
break;
case 5: // Active Plate
cell.classList.add('floor');
cellContent = document.createElement('div');
cellContent.classList.add('pressure-plate', 'active');
break;
case 6: // Closed Dynamic Wall
cell.classList.add('dynamic-wall');
break;
case 7: // Open Dynamic Wall
cell.classList.add('dynamic-wall', 'open');
break;
case 0: // Floor (or start pos after init)
default:
cell.classList.add('floor');
break;
}
// Add player div if player is on this cell
if (isPlayerOnCell) {
const playerDiv = document.createElement('div');
playerDiv.classList.add('player');
// If there's already content (like a plate), append player to it, otherwise append to cell
if (cellContent) {
cellContent.appendChild(playerDiv);
} else {
cell.appendChild(playerDiv);
}
}
// Add plate content if it exists and player isn't on it
else if(cellContent){
cell.appendChild(cellContent);
}
mazeContainer.appendChild(cell);
}
}
}
// --- Movement & Interaction ---
function handleKeyPress(event) {
if (!gameActive) return;
let dr = 0, dc = 0;
switch (event.key) {
case 'ArrowUp': dr = -1; break;
case 'ArrowDown': dr = 1; break;
case 'ArrowLeft': dc = -1; break;
case 'ArrowRight': dc = 1; break;
default: return; // Ignore other keys
}
event.preventDefault(); // Prevent page scrolling
const nextPos = { r: playerPos.r + dr, c: playerPos.c + dc };
// --- Collision & Boundary Check ---
if (nextPos.r < 0 || nextPos.r >= numRows || nextPos.c < 0 || nextPos.c >= numCols) {
return; // Out of bounds
}
const targetCellType = mazeData[nextPos.r][nextPos.c];
if (targetCellType === 1 || targetCellType === 6) { // Wall or Closed Dynamic Wall
return; // Collision
}
// --- Interaction Check (Before Move) ---
const currentCellType = mazeData[playerPos.r][playerPos.c];
// Stepping OFF a pressure plate
if (currentCellType === 5) { // Was on an active plate
togglePlateAndWalls(playerPos.r, playerPos.c, false); // Deactivate
}
// --- Move Player ---
playerPos = nextPos;
// --- Interaction Check (After Move) ---
const newCellType = mazeData[playerPos.r][playerPos.c];
// Stepping ON a pressure plate
if (newCellType === 4) { // Stepped on inactive plate
togglePlateAndWalls(playerPos.r, playerPos.c, true); // Activate
}
// --- Re-render ---
renderMaze();
// --- Win Condition Check ---
if (mazeData[playerPos.r][playerPos.c] === 3) {
messageElement.textContent = "Congratulations! You reached the goal!";
gameActive = false;
document.removeEventListener('keydown', handleKeyPress);
}
// Update message if stepping on/off plates for clarity (optional)
else if (newCellType === 5) {
messageElement.textContent = "Plate activated!";
} else if (currentCellType === 5 && newCellType !== 5){
messageElement.textContent = "Plate deactivated.";
}
}
// --- Dynamic Element Logic ---
function togglePlateAndWalls(plateR, plateC, activate) {
const plateKey = `${plateR},${plateC}`;
// Toggle plate state in mazeData
mazeData[plateR][plateC] = activate ? 5 : 4; // 5 = active, 4 = inactive
// Find linked walls and toggle their state
if (plateLinks[plateKey]) {
plateLinks[plateKey].forEach(wallCoord => {
const { r, c } = wallCoord;
if (mazeData[r] && mazeData[r][c] !== undefined) {
// Toggle between Closed (6) and Open (7)
if (mazeData[r][c] === 6) { // If closed, open it
mazeData[r][c] = 7;
} else if (mazeData[r][c] === 7) { // If open, close it
mazeData[r][c] = 6;
}
// Note: We could add safety checks here if needed
}
});
}
}
// --- Start the Game ---
initGame();
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.