diff --git a/web-app/js/projects/snake.js b/web-app/js/projects/snake.js index 672623b..d268ea6 100644 --- a/web-app/js/projects/snake.js +++ b/web-app/js/projects/snake.js @@ -1,8 +1,19 @@ -function getsnakeGameHTML() { +function getSnakeGameHTML() { return `

🐍 Classic Snake Game

+
+
+ + +
+
+
@@ -19,6 +30,10 @@ function getsnakeGameHTML() { Score
0
+
+ Best +
0
+
@@ -40,6 +55,31 @@ function getsnakeGameHTML() { padding: 20px; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } + .controls-top { + margin-bottom: 15px; + margin-right: 140px; /* Aligns with canvas center offset */ + } + .difficulty-box select { + padding: 6px 10px; + border-radius: 6px; + border: 1px solid var(--accent-border, #ccc); + background-color: #ffffff !important; /* Explicitly forces white background */ + color: #222222 !important; /* Explicitly forces dark gray/black text */ + font-size: 14px; + font-family: inherit; + font-weight: 500; + cursor: pointer; + outline: none; + } + .difficulty-box select { + padding: 4px 8px; + border-radius: 4px; + border: 1px solid #aaa; + background-color: #ffffff; + color: #222222; + font-weight: normal; + cursor: pointer; + } .game-area { display: flex; align-items: flex-start; @@ -52,16 +92,16 @@ function getsnakeGameHTML() { } #canvas-wrapper { position: relative; - border: 4px solid var(--success-color); + border: 4px solid var(--success-color, #2ecc71); border-radius: 12px; overflow: hidden; box-shadow: 0 15px 35px rgba(0,0,0,0.3); } #snakeCanvas { - background-color: var(--panel-color); + background-color: var(--panel-color, #222); background-image: - linear-gradient(var(--border-color) 1px, transparent 1px), - linear-gradient(90deg, var(--border-color) 1px, transparent 1px); + linear-gradient(var(--border-color, rgba(255,255,255,0.05)) 1px, transparent 1px), + linear-gradient(90deg, var(--border-color, rgba(255,255,255,0.05)) 1px, transparent 1px); background-size: 20px 20px; display: block; } @@ -71,14 +111,18 @@ function getsnakeGameHTML() { gap: 15px; } .score-card { - background: var(--accent-soft); - border: 1px solid var(--accent-border); - color: var(--text-color); + background: var(--accent-soft, #fafafa); + border: 1px solid var(--accent-border, #ddd); + color: var(--text-color, #333); padding: 10px 25px; border-radius: 10px; text-align: center; min-width: 120px; - box-shadow: var(--shadow); + box-shadow: var(--shadow, 0 4px 6px rgba(0,0,0,0.1)); + } + .high-score-card { + border-color: #f1c40f; + background: rgba(241, 196, 15, 0.05); } .score-card span { font-size: 12px; @@ -92,15 +136,15 @@ function getsnakeGameHTML() { .button-group { display: flex; justify-content: center; - gap: 30px; /* Space between buttons fixed */ + gap: 30px; margin-top: 10px; - margin-right: 140px; /* Offset to align with canvas center */ + margin-right: 140px; } .instruction-box { margin-top: 20px; margin-right: 140px; text-align: center; - color: var(--text-secondary); + color: var(--text-secondary, #666); font-size: 15px; } #game-over-overlay { @@ -109,31 +153,40 @@ function getsnakeGameHTML() { left: 0; width: 100%; height: 100%; - background: var(--overlay-color); + background: var(--overlay-color, rgba(0, 0, 0, 0.75)); display: flex; flex-direction: column; justify-content: center; align-items: center; z-index: 10; - color: var(--success-color); + color: var(--success-color, #2ecc71); } #game-over-overlay h1 { font-size: 3rem; margin-bottom: 10px; } .hidden { display: none !important; } .btn-primary { padding: 12px 25px; + cursor: pointer; } `; } -// --- GAME LOGIC --- +// --- GAME LOGIC STATE --- let direction = { x: 0, y: 0 }; -let speed = 7; +let speed = 9; // System baseline operational configuration state +let scoreMultiplier = 2; // Baseline multiplier scalar state let score = 0; let lastPaintTime = 0; let snakeArr = [{ x: 13, y: 10 }]; let food = { x: 6, y: 7 }; +// Profile configuration mapping matrices +const CONFIG_DIFFICULTY = { + easy: { speed: 5, multiplier: 1 }, + medium: { speed: 9, multiplier: 2 }, + hard: { speed: 14, multiplier: 3 } +}; + function main(ctime) { window.requestAnimationFrame(main); if ((ctime - lastPaintTime) / 1000 < 1 / speed) { @@ -154,11 +207,44 @@ function isCollide(snake) { return false; } +function applyDifficultySettings() { + const selector = document.getElementById('difficultySelect'); + if (selector) { + const value = selector.value; + speed = CONFIG_DIFFICULTY[value].speed; + scoreMultiplier = CONFIG_DIFFICULTY[value].multiplier; + } +} + +function updateBestScoreUI() { + const bestScore = localStorage.getItem('snakeBestScore') || 0; + const bestScoreElement = document.getElementById('best-score'); + if (bestScoreElement) { + bestScoreElement.innerHTML = bestScore; + } +} + +function checkAndSaveHighScore() { + const currentBest = parseInt(localStorage.getItem('snakeBestScore') || '0', 10); + if (score > currentBest) { + localStorage.setItem('snakeBestScore', score); + updateBestScoreUI(); + } +} + function gameEngine() { if (isCollide(snakeArr)) { direction = { x: 0, y: 0 }; document.getElementById('final-score').innerHTML = score; document.getElementById('game-over-overlay').classList.remove('hidden'); + + // Execute persistent local evaluations + checkAndSaveHighScore(); + + // Restore controls visibility states + const selector = document.getElementById('difficultySelect'); + if (selector) selector.disabled = false; + snakeArr = [{ x: 13, y: 10 }]; score = 0; document.getElementById('score').innerHTML = score; @@ -167,7 +253,7 @@ function gameEngine() { // Eating food if (snakeArr[0].y === food.y && snakeArr[0].x === food.x) { - score += 1; + score += (1 * scoreMultiplier); // Scaled multiplier calculations document.getElementById('score').innerHTML = score; snakeArr.unshift({ x: snakeArr[0].x + direction.x, y: snakeArr[0].y + direction.y }); let a = 2, b = 16; @@ -199,11 +285,25 @@ function gameEngine() { ctx.fillRect(food.x * 20, food.y * 20, 18, 18); } -// IMPORTANT: Function to initialize listeners after HTML is loaded +// Initialize execution listeners function initSnakeGame() { window.requestAnimationFrame(main); + + // Set initial historic rendering metrics + updateBestScoreUI(); + + // Map difficulty listener parameters + const selector = document.getElementById('difficultySelect'); + if (selector) { + selector.addEventListener('change', applyDifficultySettings); + } + // Initialize standard values configuration parameters + applyDifficultySettings(); document.getElementById('startGameBtn').addEventListener('click', () => { + applyDifficultySettings(); + // Prevent changing parameter matrices on an active engine run + if (selector) selector.disabled = true; direction = { x: 1, y: 0 }; // Start moving right }); @@ -213,6 +313,7 @@ function initSnakeGame() { document.getElementById('overlayRestartBtn').addEventListener('click', () => { document.getElementById('game-over-overlay').classList.add('hidden'); + if (selector) selector.disabled = false; direction = { x: 0, y: 0 }; snakeArr = [{ x: 13, y: 10 }]; score = 0; @@ -220,6 +321,13 @@ function initSnakeGame() { }); window.addEventListener('keydown', e => { + // Change difficulty selection dynamic evaluations if arrow key registers + if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) { + if (selector && !selector.disabled) { + selector.disabled = true; + } + } + switch (e.key) { case "ArrowUp": if (direction.y !== 1) { direction.x = 0; direction.y = -1; } break; case "ArrowDown": if (direction.y !== -1) { direction.x = 0; direction.y = 1; } break; @@ -228,3 +336,7 @@ function initSnakeGame() { } }); } + +// Global scope assignments +window.getSnakeGameHTML = getSnakeGameHTML; +window.initSnakeGame = initSnakeGame; \ No newline at end of file