CommanderLazarus's picture
There must be a logical path through the maze to get the main character successfully from the starting point to the finish point. The water pot should change color with each move made by the main character so that we know if it will be a win or a lose when contact is made at the center point.
d32863f verified
document.addEventListener('DOMContentLoaded', () => {
// Game constants
const GRID_SIZE = 41;
const CELL_COUNT = GRID_SIZE * GRID_SIZE;
const ENEMY_COUNT = 5;
const FOOD_COUNT = 5;
const MAX_HEALTH = 5;
const ENEMY_SCORE = 100;
const FOOD_SCORE = 50;
// Game state
let playerPosition = (2 * GRID_SIZE) + 2; // 2 rows down, 2 columns in
let health = MAX_HEALTH;
let score = 0;
let gameOver = false;
let maze = [];
let enemies = [];
let foodPositions = [];
let waterPosition = Math.floor((GRID_SIZE * GRID_SIZE) / 2); // Center
let goalPosition = (GRID_SIZE * (GRID_SIZE - 2)) + (GRID_SIZE - 2); // 2 from bottom, 2 from right
// DOM elements
const mazeGrid = document.querySelector('.maze-grid');
const scoreValue = document.getElementById('scoreValue');
const healthBars = document.querySelectorAll('.health-bars div');
const newMazeBtn = document.getElementById('newMazeBtn');
const resetBtn = document.getElementById('resetBtn');
// Initialize game
function initGame() {
generateMaze();
placePlayer();
placeEnemies();
placeFood();
placeWater();
updateUI();
addEventListeners();
}
// Generate maze with walls and paths ensuring a path exists
function generateMaze() {
mazeGrid.innerHTML = '';
maze = Array(CELL_COUNT).fill().map((_, i) => {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.index = i;
const row = Math.floor(i / GRID_SIZE);
const col = i % GRID_SIZE;
// Solid outer perimeter walls
let isWall = (row === 0 || row === GRID_SIZE-1 || col === 0 || col === GRID_SIZE-1);
// Create a guaranteed path from start to goal
if (!isWall) {
// Create a diagonal path from start to goal
const isOnPath =
(row === Math.floor(col * (GRID_SIZE-4)/(GRID_SIZE-4)) + 2) ||
(col === Math.floor(row * (GRID_SIZE-4)/(GRID_SIZE-4)) + 2);
// Add some randomness to the path
if (!isOnPath) {
isWall = Math.random() < 0.3;
}
}
// Ensure start, water and goal positions are accessible
if (i === playerPosition || i === waterPosition || i === goalPosition) {
isWall = false;
}
if (isWall) {
cell.classList.add('wall');
} else {
cell.classList.add('path');
}
mazeGrid.appendChild(cell);
return {
element: cell,
isWall: isWall
};
});
}
// Place player at start position
function placePlayer() {
playerPosition = (2 * GRID_SIZE) + 2;
const cell = maze[playerPosition].element;
cell.classList.add('player');
cell.innerHTML = '๐ŸŒฑ';
}
// Place enemies randomly
function placeEnemies() {
enemies = [];
while (enemies.length < ENEMY_COUNT) {
const pos = Math.floor(Math.random() * CELL_COUNT);
if (!maze[pos].isWall &&
pos !== playerPosition &&
pos !== waterPosition &&
pos !== goalPosition &&
!enemies.includes(pos)) {
enemies.push(pos);
const cell = maze[pos].element;
cell.classList.add('enemy');
cell.innerHTML = '๐ŸŒธ';
}
}
}
// Place food randomly
function placeFood() {
foodPositions = [];
while (foodPositions.length < FOOD_COUNT) {
const pos = Math.floor(Math.random() * CELL_COUNT);
if (!maze[pos].isWall &&
pos !== playerPosition &&
pos !== waterPosition &&
!enemies.includes(pos) &&
!foodPositions.includes(pos)) {
foodPositions.push(pos);
const cell = maze[pos].element;
cell.classList.add('food');
cell.innerHTML = '๐Ÿ•';
}
}
}
// Place water at center with random outcome
function placeWater() {
const cell = maze[waterPosition].element;
cell.classList.add('water');
cell.innerHTML = '๐Ÿ’ง';
updateWaterColor();
// Mark goal position
const goalCell = maze[goalPosition].element;
goalCell.classList.add('goal');
goalCell.innerHTML = '๐Ÿ';
}
// Update water color based on random outcome
function updateWaterColor() {
const waterCell = maze[waterPosition].element;
const willWin = Math.random() > 0.5;
waterCell.style.backgroundColor = willWin ? '#bbf7d0' : '#fecaca';
waterCell.style.border = willWin ? '1px solid #4ade80' : '1px solid #ef4444';
}
// Update UI elements
function updateUI() {
scoreValue.textContent = score;
// Update health display
healthBars.forEach((bar, i) => {
if (i < health) {
bar.classList.add('bg-green-500');
bar.classList.remove('bg-gray-300');
} else {
bar.classList.add('bg-gray-300');
bar.classList.remove('bg-green-500');
}
});
}
// Handle player movement
function movePlayer(direction) {
if (gameOver) return;
let newPosition = playerPosition;
const row = Math.floor(playerPosition / GRID_SIZE);
const col = playerPosition % GRID_SIZE;
switch (direction) {
case 'up':
if (row > 0) newPosition = playerPosition - GRID_SIZE;
break;
case 'down':
if (row < GRID_SIZE - 1) newPosition = playerPosition + GRID_SIZE;
break;
case 'left':
if (col > 0) newPosition = playerPosition - 1;
break;
case 'right':
if (col < GRID_SIZE - 1) newPosition = playerPosition + 1;
break;
}
// Check if new position is valid (not a wall)
if (newPosition !== playerPosition && !maze[newPosition].isWall) {
// Clear current player position
const currentCell = maze[playerPosition].element;
currentCell.classList.remove('player');
currentCell.innerHTML = '';
// Check what's at new position
const newCell = maze[newPosition].element;
// Check for enemy collision
if (enemies.includes(newPosition)) {
health--;
score += ENEMY_SCORE;
enemies = enemies.filter(pos => pos !== newPosition);
newCell.classList.remove('enemy');
if (health <= 0) {
endGame(false);
return;
}
}
// Check for food collection
if (foodPositions.includes(newPosition)) {
health = Math.min(health + 1, MAX_HEALTH);
score += FOOD_SCORE;
foodPositions = foodPositions.filter(pos => pos !== newPosition);
newCell.classList.remove('food');
}
// Check for water pot
if (newPosition === waterPosition) {
const isWin = Math.random() > 0.5;
endGame(isWin);
return;
}
// Check for win condition (2 from bottom, 2 from right)
if (newPosition === goalPosition) {
endGame(true);
return;
}
// Move player
playerPosition = newPosition;
newCell.classList.add('player');
newCell.innerHTML = '๐ŸŒฑ';
// Move enemies
moveEnemies();
// Update water color with each move
updateWaterColor();
// Update UI
updateUI();
}
}
// Move enemies towards player
function moveEnemies() {
enemies.forEach((enemyPos, index) => {
const enemyCell = maze[enemyPos].element;
enemyCell.classList.remove('enemy');
enemyCell.innerHTML = '';
const playerRow = Math.floor(playerPosition / GRID_SIZE);
const playerCol = playerPosition % GRID_SIZE;
const enemyRow = Math.floor(enemyPos / GRID_SIZE);
const enemyCol = enemyPos % GRID_SIZE;
// Simple AI: move towards player
let newRow = enemyRow;
let newCol = enemyCol;
if (enemyRow < playerRow) newRow++;
else if (enemyRow > playerRow) newRow--;
if (enemyCol < playerCol) newCol++;
else if (enemyCol > playerCol) newCol--;
// Randomize direction sometimes
if (Math.random() < 0.3) {
if (Math.random() < 0.5) {
newRow = enemyRow + (Math.random() < 0.5 ? 1 : -1);
} else {
newCol = enemyCol + (Math.random() < 0.5 ? 1 : -1);
}
}
// Ensure new position is within bounds
newRow = Math.max(0, Math.min(GRID_SIZE - 1, newRow));
newCol = Math.max(0, Math.min(GRID_SIZE - 1, newCol));
const newEnemyPos = newRow * GRID_SIZE + newCol;
// Only move if not a wall and not another enemy
if (!maze[newEnemyPos].isWall && !enemies.includes(newEnemyPos) && newEnemyPos !== playerPosition) {
enemies[index] = newEnemyPos;
}
// Update enemy display
const newEnemyCell = maze[enemies[index]].element;
newEnemyCell.classList.add('enemy');
newEnemyCell.innerHTML = '๐ŸŒธ';
});
}
// End game with win/lose
function endGame(isWin) {
gameOver = true;
const gameOverDiv = document.createElement('div');
gameOverDiv.className = 'game-over';
gameOverDiv.innerHTML = `
<h2>${isWin ? 'You Won! ๐ŸŽ‰' : 'Game Over! ๐Ÿ’€'}</h2>
<p>Final Score: ${score}</p>
<button onclick="location.reload()">Play Again</button>
`;
mazeGrid.appendChild(gameOverDiv);
}
// Add event listeners
function addEventListeners() {
// Keyboard controls
document.addEventListener('keydown', (e) => {
switch (e.key) {
case 'ArrowUp':
movePlayer('up');
break;
case 'ArrowDown':
movePlayer('down');
break;
case 'ArrowLeft':
movePlayer('left');
break;
case 'ArrowRight':
movePlayer('right');
break;
}
});
// New maze button
newMazeBtn.addEventListener('click', () => {
generateMaze();
placePlayer();
placeEnemies();
placeFood();
placeWater();
gameOver = false;
score = 0;
health = MAX_HEALTH;
updateUI();
});
// Reset button
resetBtn.addEventListener('click', () => {
placePlayer();
placeEnemies();
placeFood();
gameOver = false;
score = 0;
health = MAX_HEALTH;
updateUI();
});
}
// Start the game
initGame();
});