pacman / index.html
Andosky's picture
Add 1 files
533b773 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pac-Man Clone</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@keyframes pacman-animation {
0% { transform: rotate(0deg); }
50% { transform: rotate(-45deg); }
100% { transform: rotate(0deg); }
}
@keyframes ghost-animation {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-5px); }
}
.pacman {
animation: pacman-animation 0.5s infinite;
transform-origin: center;
}
.ghost {
animation: ghost-animation 1s infinite ease-in-out;
}
.blinky { background-color: #FF0000; }
.pinky { background-color: #FFB8FF; }
.inky { background-color: #00FFFF; }
.clyde { background-color: #FFB852; }
.eye {
background-color: white;
border-radius: 50%;
position: absolute;
width: 10px;
height: 10px;
}
.pupil {
background-color: #0000AA;
border-radius: 50%;
position: absolute;
width: 5px;
height: 5px;
}
.dot {
width: 8px;
height: 8px;
background-color: #FFF;
border-radius: 50%;
position: absolute;
}
.power-pellet {
width: 16px;
height: 16px;
background-color: #FFF;
border-radius: 50%;
position: absolute;
box-shadow: 0 0 10px 5px rgba(255, 255, 255, 0.7);
}
.wall {
background-color: #2121FF;
position: absolute;
}
#game-container {
position: relative;
width: 600px;
height: 600px;
background-color: #000;
margin: 0 auto;
overflow: hidden;
}
#score-display {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-family: 'Courier New', monospace;
font-size: 20px;
z-index: 100;
}
#lives-display {
position: absolute;
top: 10px;
right: 10px;
color: white;
font-family: 'Courier New', monospace;
font-size: 20px;
z-index: 100;
}
#game-over {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-family: 'Courier New', monospace;
font-size: 48px;
text-align: center;
display: none;
z-index: 100;
}
#start-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 200;
}
#start-screen h1 {
color: #FF0;
font-family: 'Courier New', monospace;
font-size: 48px;
margin-bottom: 30px;
text-shadow: 0 0 10px #FF0;
}
#start-button {
background-color: #FF0;
color: #000;
border: none;
padding: 15px 30px;
font-family: 'Courier New', monospace;
font-size: 24px;
cursor: pointer;
border-radius: 10px;
transition: all 0.3s;
}
#start-button:hover {
background-color: #FFF;
transform: scale(1.1);
}
.vulnerable-ghost {
background-color: #0000FF !important;
opacity: 0.7 !important;
}
</style>
</head>
<body class="bg-gray-900 flex flex-col items-center justify-center min-h-screen">
<h1 class="text-yellow-400 text-4xl font-bold mb-8">Pac-Man Clone</h1>
<div id="game-container">
<div id="score-display">Score: 0</div>
<div id="lives-display">Lives: 3</div>
<div id="game-over">GAME OVER<br><button id="restart-button" class="mt-4 bg-yellow-400 text-black px-4 py-2 rounded">Play Again</button></div>
<div id="start-screen">
<h1>PAC-MAN CLONE</h1>
<button id="start-button">START GAME</button>
<div class="mt-8 text-white text-center">
<p class="mb-2">Use arrow keys to move</p>
<p class="mb-2">Eat all dots to win</p>
<p>Avoid ghosts (except when powered up)</p>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const gameContainer = document.getElementById('game-container');
const scoreDisplay = document.getElementById('score-display');
const livesDisplay = document.getElementById('lives-display');
const gameOverDisplay = document.getElementById('game-over');
const startScreen = document.getElementById('start-screen');
const startButton = document.getElementById('start-button');
const restartButton = document.getElementById('restart-button');
const containerWidth = 600;
const containerHeight = 600;
const gridSize = 20;
const cellSize = containerWidth / gridSize;
let score = 0;
let lives = 3;
let gameRunning = false;
let dotsRemaining = 0;
let powerPelletActive = false;
let powerPelletTimeout;
let gameLoop;
// Game elements
let pacman = {
x: 9,
y: 15,
element: null,
direction: 'right',
nextDirection: 'right'
};
const ghosts = [
{ name: 'blinky', x: 9, y: 7, element: null, direction: 'left', color: 'red' },
{ name: 'pinky', x: 9, y: 9, element: null, direction: 'up', color: 'pink' },
{ name: 'inky', x: 8, y: 9, element: null, direction: 'right', color: 'cyan' },
{ name: 'clyde', x: 10, y: 9, element: null, direction: 'down', color: 'orange' }
];
// Maze layout (1 = wall, 0 = path, 2 = power pellet)
const maze = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,1,1,0,1,1,1,1,0,1,1,1,1,0,1,1,1,0,1],
[1,0,1,1,0,1,1,1,1,0,1,1,1,1,0,1,1,1,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1],
[1,0,0,0,0,1,0,0,0,1,1,0,0,1,0,0,0,0,0,1],
[1,1,1,1,0,1,1,1,0,1,1,0,1,1,0,1,1,1,1,1],
[0,0,0,1,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0],
[1,1,1,1,0,1,0,1,1,2,2,1,1,1,0,1,1,1,1,1],
[0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,0],
[1,1,1,1,0,1,0,1,1,1,1,1,1,0,1,0,1,1,1,1],
[1,0,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,0,1],
[1,0,1,1,0,1,0,1,0,1,1,0,1,0,1,0,1,1,0,1],
[1,0,0,1,0,0,0,1,0,0,0,0,1,0,0,0,1,0,0,1],
[1,1,0,1,0,1,0,1,1,1,1,1,1,0,1,0,1,0,1,1],
[1,0,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1],
[1,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];
// Initialize game
function initGame() {
// Clear previous game elements
gameContainer.querySelectorAll('.pacman, .ghost, .dot, .power-pellet, .wall').forEach(el => el.remove());
// Reset game state
score = 0;
lives = 3;
dotsRemaining = 0;
powerPelletActive = false;
clearTimeout(powerPelletTimeout);
clearInterval(gameLoop);
// Reset Pac-Man position
pacman.x = 9;
pacman.y = 15;
pacman.direction = 'right';
pacman.nextDirection = 'right';
// Reset ghosts positions
ghosts[0].x = 9; ghosts[0].y = 7; ghosts[0].direction = 'left';
ghosts[1].x = 9; ghosts[1].y = 9; ghosts[1].direction = 'up';
ghosts[2].x = 8; ghosts[2].y = 9; ghosts[2].direction = 'right';
ghosts[3].x = 10; ghosts[3].y = 9; ghosts[3].direction = 'down';
// Create maze walls
createMaze();
// Create dots and power pellets
createDots();
// Create Pac-Man
createPacman();
// Create ghosts
ghosts.forEach(ghost => createGhost(ghost));
// Update displays
updateScore();
updateLives();
// Hide game over screen
gameOverDisplay.style.display = 'none';
}
function createMaze() {
for (let y = 0; y < maze.length; y++) {
for (let x = 0; x < maze[y].length; x++) {
if (maze[y][x] === 1) {
const wall = document.createElement('div');
wall.className = 'wall';
wall.style.width = `${cellSize}px`;
wall.style.height = `${cellSize}px`;
wall.style.left = `${x * cellSize}px`;
wall.style.top = `${y * cellSize}px`;
gameContainer.appendChild(wall);
}
}
}
}
function createDots() {
dotsRemaining = 0;
for (let y = 0; y < maze.length; y++) {
for (let x = 0; x < maze[y].length; x++) {
if (maze[y][x] === 0) {
const dot = document.createElement('div');
dot.className = 'dot';
dot.style.left = `${x * cellSize + cellSize / 2 - 4}px`;
dot.style.top = `${y * cellSize + cellSize / 2 - 4}px`;
gameContainer.appendChild(dot);
dotsRemaining++;
} else if (maze[y][x] === 2) {
const powerPellet = document.createElement('div');
powerPellet.className = 'power-pellet';
powerPellet.style.left = `${x * cellSize + cellSize / 2 - 8}px`;
powerPellet.style.top = `${y * cellSize + cellSize / 2 - 8}px`;
gameContainer.appendChild(powerPellet);
}
}
}
}
function createPacman() {
if (pacman.element) {
gameContainer.removeChild(pacman.element);
}
pacman.element = document.createElement('div');
pacman.element.className = 'pacman absolute';
pacman.element.style.width = `${cellSize}px`;
pacman.element.style.height = `${cellSize}px`;
pacman.element.style.left = `${pacman.x * cellSize}px`;
pacman.element.style.top = `${pacman.y * cellSize}px`;
pacman.element.style.backgroundColor = '#FF0';
pacman.element.style.borderRadius = '50%';
pacman.element.style.zIndex = '10';
// Create Pac-Man's mouth
const mouth = document.createElement('div');
mouth.style.position = 'absolute';
mouth.style.width = `${cellSize}px`;
mouth.style.height = `${cellSize}px`;
mouth.style.backgroundColor = '#000';
mouth.style.clipPath = 'polygon(50% 50%, 100% 0%, 100% 100%)';
pacman.element.appendChild(mouth);
gameContainer.appendChild(pacman.element);
}
function createGhost(ghost) {
if (ghost.element) {
gameContainer.removeChild(ghost.element);
}
ghost.element = document.createElement('div');
ghost.element.className = `ghost absolute ${ghost.name}`;
ghost.element.style.width = `${cellSize}px`;
ghost.element.style.height = `${cellSize}px`;
ghost.element.style.left = `${ghost.x * cellSize}px`;
ghost.element.style.top = `${ghost.y * cellSize}px`;
ghost.element.style.borderRadius = '50% 50% 0 0';
ghost.element.style.zIndex = '5';
// Ghost body
const body = document.createElement('div');
body.style.position = 'absolute';
body.style.width = '100%';
body.style.height = '100%';
body.style.borderRadius = '50% 50% 0 0';
ghost.element.appendChild(body);
// Ghost eyes
const leftEye = document.createElement('div');
leftEye.className = 'eye';
leftEye.style.left = '25%';
leftEye.style.top = '25%';
ghost.element.appendChild(leftEye);
const rightEye = document.createElement('div');
rightEye.className = 'eye';
rightEye.style.right = '25%';
rightEye.style.top = '25%';
ghost.element.appendChild(rightEye);
// Ghost pupils
const leftPupil = document.createElement('div');
leftPupil.className = 'pupil';
leftPupil.style.left = '2px';
leftPupil.style.top = '2px';
leftEye.appendChild(leftPupil);
const rightPupil = document.createElement('div');
rightPupil.className = 'pupil';
rightPupil.style.right = '2px';
rightPupil.style.top = '2px';
rightEye.appendChild(rightPupil);
// Ghost feet
const feetContainer = document.createElement('div');
feetContainer.style.position = 'absolute';
feetContainer.style.bottom = '0';
feetContainer.style.width = '100%';
feetContainer.style.height = '10px';
feetContainer.style.display = 'flex';
feetContainer.style.justifyContent = 'space-between';
ghost.element.appendChild(feetContainer);
for (let i = 0; i < 3; i++) {
const foot = document.createElement('div');
foot.style.width = '10px';
foot.style.height = '10px';
foot.style.backgroundColor = 'inherit';
foot.style.borderRadius = '0 0 5px 5px';
feetContainer.appendChild(foot);
}
gameContainer.appendChild(ghost.element);
}
function updateScore() {
scoreDisplay.textContent = `Score: ${score}`;
}
function updateLives() {
livesDisplay.textContent = `Lives: ${lives}`;
}
function movePacman() {
let newX = pacman.x;
let newY = pacman.y;
// Try to move in the next direction first
if (pacman.nextDirection !== pacman.direction) {
switch (pacman.nextDirection) {
case 'up':
if (isValidMove(pacman.x, pacman.y - 1)) {
pacman.direction = pacman.nextDirection;
}
break;
case 'down':
if (isValidMove(pacman.x, pacman.y + 1)) {
pacman.direction = pacman.nextDirection;
}
break;
case 'left':
if (isValidMove(pacman.x - 1, pacman.y)) {
pacman.direction = pacman.nextDirection;
}
break;
case 'right':
if (isValidMove(pacman.x + 1, pacman.y)) {
pacman.direction = pacman.nextDirection;
}
break;
}
}
// Move in the current direction
switch (pacman.direction) {
case 'up':
if (isValidMove(pacman.x, pacman.y - 1)) {
newY--;
}
break;
case 'down':
if (isValidMove(pacman.x, pacman.y + 1)) {
newY++;
}
break;
case 'left':
if (isValidMove(pacman.x - 1, pacman.y)) {
newX--;
}
break;
case 'right':
if (isValidMove(pacman.x + 1, pacman.y)) {
newX++;
}
break;
}
// Handle tunnel
if (newX < 0) newX = gridSize - 1;
if (newX >= gridSize) newX = 0;
// Update position if valid
if (isValidMove(newX, newY) || (newX !== pacman.x || newY !== pacman.y)) {
pacman.x = newX;
pacman.y = newY;
}
// Update Pac-Man's position on screen
pacman.element.style.left = `${pacman.x * cellSize}px`;
pacman.element.style.top = `${pacman.y * cellSize}px`;
// Rotate Pac-Man based on direction
switch (pacman.direction) {
case 'up':
pacman.element.style.transform = 'rotate(-90deg)';
break;
case 'down':
pacman.element.style.transform = 'rotate(90deg)';
break;
case 'left':
pacman.element.style.transform = 'rotate(180deg)';
break;
case 'right':
pacman.element.style.transform = 'rotate(0deg)';
break;
}
// Check for dots or power pellets
checkCollisions();
}
function isValidMove(x, y) {
// Check if position is outside the grid (tunnel)
if (x < 0 || x >= gridSize || y < 0 || y >= gridSize) {
return true;
}
// Check if position is a wall
return maze[y][x] !== 1;
}
function checkCollisions() {
// Check for dots
const dots = document.querySelectorAll('.dot');
dots.forEach(dot => {
const dotX = parseInt(dot.style.left) / cellSize;
const dotY = parseInt(dot.style.top) / cellSize;
if (Math.abs(pacman.x - dotX) < 0.5 && Math.abs(pacman.y - dotY) < 0.5) {
dot.remove();
score += 10;
dotsRemaining--;
updateScore();
}
});
// Check for power pellets
const powerPellets = document.querySelectorAll('.power-pellet');
powerPellets.forEach(pellet => {
const pelletX = parseInt(pellet.style.left) / cellSize;
const pelletY = parseInt(pellet.style.top) / cellSize;
if (Math.abs(pacman.x - pelletX) < 0.5 && Math.abs(pacman.y - pelletY) < 0.5) {
pellet.remove();
score += 50;
updateScore();
activatePowerPellet();
}
});
// Check for ghosts
ghosts.forEach(ghost => {
if (Math.abs(pacman.x - ghost.x) < 0.8 && Math.abs(pacman.y - ghost.y) < 0.8) {
if (powerPelletActive) {
// Ghost is vulnerable - eat it
ghost.element.classList.add('vulnerable-ghost');
setTimeout(() => {
// Reset ghost position
ghost.x = 9;
ghost.y = 9;
ghost.element.style.left = `${ghost.x * cellSize}px`;
ghost.element.style.top = `${ghost.y * cellSize}px`;
ghost.element.classList.remove('vulnerable-ghost');
ghost.element.className = `ghost absolute ${ghost.name}`;
}, 1000);
score += 200;
updateScore();
} else {
// Pac-Man dies
lives--;
updateLives();
if (lives <= 0) {
gameOver();
} else {
// Reset positions
pacman.x = 9;
pacman.y = 15;
pacman.direction = 'right';
pacman.nextDirection = 'right';
pacman.element.style.left = `${pacman.x * cellSize}px`;
pacman.element.style.top = `${pacman.y * cellSize}px`;
pacman.element.style.transform = 'rotate(0deg)';
ghosts.forEach(g => {
g.x = [9, 9, 8, 10][ghosts.indexOf(g)];
g.y = [7, 9, 9, 9][ghosts.indexOf(g)];
g.direction = ['left', 'up', 'right', 'down'][ghosts.indexOf(g)];
g.element.style.left = `${g.x * cellSize}px`;
g.element.style.top = `${g.y * cellSize}px`;
});
}
}
}
});
// Check win condition
if (dotsRemaining <= 0) {
setTimeout(() => {
alert('Congratulations! You won!');
initGame();
}, 500);
}
}
function activatePowerPellet() {
powerPelletActive = true;
clearTimeout(powerPelletTimeout);
// Make ghosts vulnerable
ghosts.forEach(ghost => {
ghost.element.classList.add('vulnerable-ghost');
});
// Set timeout to deactivate power pellet
powerPelletTimeout = setTimeout(() => {
powerPelletActive = false;
ghosts.forEach(ghost => {
ghost.element.classList.remove('vulnerable-ghost');
ghost.element.className = `ghost absolute ${ghost.name}`;
});
}, 10000); // 10 seconds
}
function moveGhosts() {
ghosts.forEach(ghost => {
// Simple AI: move randomly but prefer directions that don't reverse
const directions = ['up', 'down', 'left', 'right'];
const oppositeDirections = {
'up': 'down',
'down': 'up',
'left': 'right',
'right': 'left'
};
// Filter out the opposite direction to prevent 180-degree turns
let possibleDirections = directions.filter(dir => dir !== oppositeDirections[ghost.direction]);
// Try to find a valid move
let newDirection = ghost.direction;
let newX = ghost.x;
let newY = ghost.y;
// First try to keep moving in the same direction
let moved = false;
switch (ghost.direction) {
case 'up':
if (isValidMove(ghost.x, ghost.y - 1)) {
newY--;
moved = true;
}
break;
case 'down':
if (isValidMove(ghost.x, ghost.y + 1)) {
newY++;
moved = true;
}
break;
case 'left':
if (isValidMove(ghost.x - 1, ghost.y)) {
newX--;
moved = true;
}
break;
case 'right':
if (isValidMove(ghost.x + 1, ghost.y)) {
newX++;
moved = true;
}
break;
}
// If current direction is blocked, try random other directions
if (!moved) {
// Shuffle possible directions
possibleDirections = possibleDirections.sort(() => Math.random() - 0.5);
for (const dir of possibleDirections) {
switch (dir) {
case 'up':
if (isValidMove(ghost.x, ghost.y - 1)) {
newDirection = 'up';
newY--;
moved = true;
break;
}
case 'down':
if (isValidMove(ghost.x, ghost.y + 1)) {
newDirection = 'down';
newY++;
moved = true;
break;
}
case 'left':
if (isValidMove(ghost.x - 1, ghost.y)) {
newDirection = 'left';
newX--;
moved = true;
break;
}
case 'right':
if (isValidMove(ghost.x + 1, ghost.y)) {
newDirection = 'right';
newX++;
moved = true;
break;
}
}
if (moved) break;
}
}
// Handle tunnel
if (newX < 0) newX = gridSize - 1;
if (newX >= gridSize) newX = 0;
// Update ghost position
ghost.x = newX;
ghost.y = newY;
ghost.direction = newDirection;
// Update ghost's position on screen
ghost.element.style.left = `${ghost.x * cellSize}px`;
ghost.element.style.top = `${ghost.y * cellSize}px`;
// Update ghost's eyes to look in the direction it's moving
const leftPupil = ghost.element.querySelector('.eye:first-child .pupil');
const rightPupil = ghost.element.querySelector('.eye:last-child .pupil');
leftPupil.style.left = '';
leftPupil.style.right = '';
leftPupil.style.top = '';
leftPupil.style.bottom = '';
rightPupil.style.left = '';
rightPupil.style.right = '';
rightPupil.style.top = '';
rightPupil.style.bottom = '';
switch (ghost.direction) {
case 'up':
leftPupil.style.left = '2px';
leftPupil.style.top = '0';
rightPupil.style.right = '2px';
rightPupil.style.top = '0';
break;
case 'down':
leftPupil.style.left = '2px';
leftPupil.style.bottom = '0';
rightPupil.style.right = '2px';
rightPupil.style.bottom = '0';
break;
case 'left':
leftPupil.style.left = '0';
leftPupil.style.top = '2px';
rightPupil.style.right = '4px';
rightPupil.style.top = '2px';
break;
case 'right':
leftPupil.style.left = '4px';
leftPupil.style.top = '2px';
rightPupil.style.right = '0';
rightPupil.style.top = '2px';
break;
}
});
}
function gameOver() {
gameRunning = false;
gameOverDisplay.style.display = 'block';
}
// Event listeners
document.addEventListener('keydown', (e) => {
switch (e.key) {
case 'ArrowUp':
pacman.nextDirection = 'up';
break;
case 'ArrowDown':
pacman.nextDirection = 'down';
break;
case 'ArrowLeft':
pacman.nextDirection = 'left';
break;
case 'ArrowRight':
pacman.nextDirection = 'right';
break;
}
});
startButton.addEventListener('click', () => {
startScreen.style.display = 'none';
initGame();
startGame();
});
restartButton.addEventListener('click', () => {
gameOverDisplay.style.display = 'none';
initGame();
startGame();
});
function startGame() {
gameRunning = true;
// Game loop
gameLoop = setInterval(() => {
if (!gameRunning) {
clearInterval(gameLoop);
return;
}
movePacman();
moveGhosts();
}, 150); // Adjust speed as needed
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=Andosky/andosky-game" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
</html>