<div class="rsg-game-container">
<h1 class="rsg-title">Retro Snake</h1>
<div class="rsg-info-bar">
<div class="rsg-score-display">Score: <span id="rsgScore">0</span></div>
<div class="rsg-high-score-display">High Score: <span id="rsgHighScore">0</span></div>
</div>
<canvas id="rsgGameCanvas"></canvas>
<button id="rsgStartButton" class="rsg-button">Start Game</button>
<p id="rsgInstructions" class="rsg-instructions">Use Arrow Keys or WASD to control the snake.</p>
<p id="rsgGameOverMessage" class="rsg-game-over-message" style="display:none;">Game Over!</p>
</div>
/* ==== Retro Snake Game Styles ==== */
body.rsg-active-page {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
font-family: "Press Start 2P", cursive, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background-color: #1a1a2e; /* Dark blue retro background */
color: #e0e0e0;
margin: 0;
text-align: center;
padding: 10px;
box-sizing: border-box;
image-rendering: pixelated;
}
/* Import Retro Font */
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
.rsg-game-container {
background-color: #0d0d1a;
padding: 20px;
border-radius: 8px;
border: 3px solid #303050;
box-shadow: 0 0 20px rgba(70, 70, 200, 0.4), inset 0 0 10px rgba(0,0,0,0.6);
display: flex;
flex-direction: column;
align-items: center;
}
.rsg-title {
font-size: 2.2rem;
color: #66ff66; /* Bright green */
margin-top: 0;
margin-bottom: 15px;
text-shadow: 3px 3px #008000;
}
.rsg-info-bar {
display: flex;
justify-content: space-between;
width: 100%;
max-width: 400px; /* Canvas width */
margin-bottom: 10px;
font-size: 0.9rem;
color: #dddddd;
}
.rsg-score-display, .rsg-high-score-display {
padding: 5px 10px;
background-color: rgba(40,40,70,0.5);
border-radius: 4px;
}
#rsgGameCanvas {
background-color: #000000;
border: 2px solid #66ff66;
/* Dimensions will be set by JavaScript */
}
.rsg-button {
background-color: #4CAF50; /* Green button */
color: #ffffff;
border: 2px solid #81C784;
padding: 12px 25px;
font-family: "Press Start 2P", cursive;
font-size: 1rem;
cursor: pointer;
margin-top: 20px;
text-transform: uppercase;
transition: background-color 0.2s, box-shadow 0.2s;
box-shadow: 0 0 8px rgba(76, 175, 80, 0.6);
}
.rsg-button:hover {
background-color: #66BB6A;
box-shadow: 0 0 12px rgba(102, 187, 106, 0.8);
}
.rsg-button:active {
background-color: #388E3C;
transform: translateY(1px);
}
.rsg-instructions {
font-size: 0.8rem;
color: #bbbbff;
margin-top: 15px;
}
.rsg-game-over-message {
font-size: 1.8rem;
color: #ff4444; /* Red for game over */
margin-top: 15px;
font-weight: bold;
text-shadow: 2px 2px #aa0000;
}
document.addEventListener('DOMContentLoaded', function() {
const canvas = document.getElementById('rsgGameCanvas');
const ctx = canvas.getContext('2d');
const startButton = document.getElementById('rsgStartButton');
const scoreEl = document.getElementById('rsgScore');
const highScoreEl = document.getElementById('rsgHighScore');
const gameOverMessageEl = document.getElementById('rsgGameOverMessage');
const instructionsEl = document.getElementById('rsgInstructions');
// Add class to body for specific page styling
if (document.querySelector('.rsg-game-container')) {
document.body.classList.add('rsg-active-page');
}
const gridSize = 20; // Size of each grid cell (and snake segment/food)
canvas.width = 400;
canvas.height = 400;
const tileCountX = canvas.width / gridSize;
const tileCountY = canvas.height / gridSize;
let snake, food, score, highScore, dx, dy, gameLoopTimeout, gameRunning;
const SNAKE_COLOR = '#33cc33';
const FOOD_COLOR = '#ff6347'; // Tomato red
const INITIAL_SPEED = 150; // Milliseconds per update, lower is faster
let currentSpeed = INITIAL_SPEED;
function initGame() {
snake = [{ x: Math.floor(tileCountX / 2), y: Math.floor(tileCountY / 2) }]; // Start in middle
dx = 1; // Initial direction: right
dy = 0;
score = 0;
currentSpeed = INITIAL_SPEED;
highScore = localStorage.getItem('rsgHighScore') || 0;
updateScoreDisplay();
spawnFood();
gameRunning = true;
gameOverMessageEl.style.display = 'none';
startButton.textContent = 'Restart Game';
startButton.style.display = 'none'; // Hide during gameplay
instructionsEl.style.display = 'block';
if (gameLoopTimeout) clearTimeout(gameLoopTimeout);
gameLoop();
}
function spawnFood() {
food = {
x: Math.floor(Math.random() * tileCountX),
y: Math.floor(Math.random() * tileCountY)
};
// Ensure food doesn't spawn on snake
for (let segment of snake) {
if (segment.x === food.x && segment.y === food.y) {
spawnFood(); // Recursively call if conflict
return;
}
}
}
function updateScoreDisplay() {
scoreEl.textContent = score;
highScoreEl.textContent = highScore;
}
function drawRect(x, y, w, h, color) {
ctx.fillStyle = color;
ctx.fillRect(x * gridSize, y * gridSize, w, h);
}
function drawGame() {
// Clear canvas
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw snake
if (snake !== undefined) snake.forEach(segment => drawRect(segment.x, segment.y, gridSize - 1, gridSize - 1, SNAKE_COLOR));
// Draw food
if (food !== undefined) drawRect(food.x, food.y, gridSize -1 , gridSize -1, FOOD_COLOR);
}
function moveSnake() {
const head = { x: snake[0].x + dx, y: snake[0].y + dy };
snake.unshift(head); // Add new head
// Check if snake ate food
if (head.x === food.x && head.y === food.y) {
score++;
if (score > highScore) {
highScore = score;
localStorage.setItem('rsgHighScore', highScore);
}
updateScoreDisplay();
spawnFood();
// Increase speed slightly
if (currentSpeed > 60) currentSpeed -= 3; // Speed up, but cap it
} else {
snake.pop(); // Remove tail if no food eaten
}
}
function checkCollision() {
const head = snake[0];
// Wall collision
if (head.x < 0 || head.x >= tileCountX || head.y < 0 || head.y >= tileCountY) {
return true;
}
// Self-collision
for (let i = 1; i < snake.length; i++) {
if (head.x === snake[i].x && head.y === snake[i].y) {
return true;
}
}
return false;
}
function gameOver() {
gameRunning = false;
gameOverMessageEl.style.display = 'block';
startButton.textContent = 'Play Again';
startButton.style.display = 'block';
instructionsEl.style.display = 'none';
}
function gameLoop() {
if (!gameRunning) return;
moveSnake();
if (checkCollision()) {
gameOver();
return;
}
drawGame();
gameLoopTimeout = setTimeout(gameLoop, currentSpeed);
}
document.addEventListener('keydown', (e) => {
const key = e.key.toLowerCase();
if ((key === 'arrowup' || key === 'w') && dy === 0) { dx = 0; dy = -1; }
else if ((key === 'arrowdown' || key === 's') && dy === 0) { dx = 0; dy = 1; }
else if ((key === 'arrowleft' || key === 'a') && dx === 0) { dx = -1; dy = 0; }
else if ((key === 'arrowright' || key === 'd') && dx === 0) { dx = 1; dy = 0; }
});
startButton.addEventListener('click', initGame);
// Initialize display of high score on page load
highScoreEl.textContent = localStorage.getItem('rsgHighScore') || 0;
drawGame(); // Draw initial empty board or placeholder state
});