diff --git a/script.js b/script.js index 3cd983c..ff7e532 100644 --- a/script.js +++ b/script.js @@ -16,11 +16,20 @@ let bestScore = 0; let gameWon = false; // === Initialize Game === -document.addEventListener("DOMContentLoaded", startNewGame); +document.addEventListener("DOMContentLoaded", () => { + startNewGame(); + // Focus the game container for keyboard events + document.querySelector('.game-container').focus(); + // Set initial theme + document.body.classList.toggle('dark-theme', localStorage.getItem('theme') === 'dark'); + updateThemeButton(); +}); + newGameBtn.addEventListener("click", startNewGame); -tryAgainBtn.addEventListener("click", () => startNewGame()); +tryAgainBtn.addEventListener("click", startNewGame); continueBtn.addEventListener("click", () => { - messageBox.style.display = "none"; // Hide win message + messageBox.style.display = "none"; + gameWon = false; // Allow continuing after win }); // === Start / Reset Game === @@ -100,26 +109,85 @@ function updateScore() { } } -// === Arrow Key Handling (simplified placeholder) === -document.addEventListener("keydown", (e) => { - if (!["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].includes(e.key)) return; - - // FUTURE: Add move + merge logic here - // For now, just spawn a random tile after key press - const moved = spawnRandomTile(); - if (moved) renderGrid(); - - // Check win condition (2048) - if (!gameWon && grid.some(row => row.includes(2048))) { - gameWon = true; - messageText.textContent = "You Win!"; - messageBox.style.display = "flex"; +// === Move and Merge Functions === +function moveTiles(direction) { + let moved = false; + const oldGrid = JSON.stringify(grid); + + // Transpose the grid for vertical moves + if (direction === 'ArrowUp' || direction === 'ArrowDown') { + grid = grid[0].map((_, i) => grid.map(row => row[i])); + } + + // Process each row + for (let i = 0; i < GRID_SIZE; i++) { + let row = grid[i]; + + // Filter out zeros + let nonZeros = row.filter(cell => cell !== 0); + + // Merge tiles + if (direction === 'ArrowRight' || direction === 'ArrowDown') { + for (let j = nonZeros.length - 1; j > 0; j--) { + if (nonZeros[j] === nonZeros[j - 1]) { + nonZeros[j] *= 2; + score += nonZeros[j]; + nonZeros.splice(j - 1, 1); + j--; // Skip next to prevent double merging + } + } + // Pad with zeros at the beginning + while (nonZeros.length < GRID_SIZE) nonZeros.unshift(0); + } else { + for (let j = 0; j < nonZeros.length - 1; j++) { + if (nonZeros[j] === nonZeros[j + 1]) { + nonZeros[j] *= 2; + score += nonZeros[j]; + nonZeros.splice(j + 1, 1); + } + } + // Pad with zeros at the end + while (nonZeros.length < GRID_SIZE) nonZeros.push(0); + } + + // Update the row + grid[i] = nonZeros; + } + + // Transpose back for vertical moves + if (direction === 'ArrowUp' || direction === 'ArrowDown') { + grid = grid[0].map((_, i) => grid.map(row => row[i])); } + + // Check if grid changed + moved = oldGrid !== JSON.stringify(grid); + return moved; +} - // Check game over - if (isGameOver()) { - messageText.textContent = "Game Over!"; - messageBox.style.display = "flex"; +// === Arrow Key Handling === +document.addEventListener("keydown", (e) => { + if (["ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].includes(e.key)) { + e.preventDefault(); + const moved = moveTiles(e.key); + + if (moved) { + spawnRandomTile(); + renderGrid(); + updateScore(); + + // Check win condition (2048) + if (!gameWon && grid.some(row => row.includes(2048))) { + gameWon = true; + messageText.textContent = "You Win! Click 'New Game' to play again."; + messageBox.style.display = "flex"; + } + + // Check game over + if (isGameOver()) { + messageText.textContent = "Game Over! Click 'New Game' to play again."; + messageBox.style.display = "flex"; + } + } } }); @@ -140,8 +208,17 @@ function isGameOver() { return true; // No empty cells and no possible merges } - // === Theme toggle === + // === Theme Toggle === +function updateThemeButton() { + const isDark = document.body.classList.contains('dark-theme'); + toggleBtn.textContent = isDark ? '☀️' : '🌙'; + localStorage.setItem('theme', isDark ? 'dark' : 'light'); +} + toggleBtn.addEventListener('click', () => { document.body.classList.toggle('dark-theme'); + updateThemeButton(); +}); -}); \ No newline at end of file +// Initialize theme button +updateThemeButton(); \ No newline at end of file diff --git a/style.css b/style.css index 2a54bff..ffd2c96 100644 --- a/style.css +++ b/style.css @@ -1,3 +1,35 @@ +/* Base Styles */ +:root { + --bg-color: #faf8ef; + --text-color: #776e65; + --grid-bg-color: #bbada0; + --grid-cell-bg: #eee4da; + --grid-text-color: #776e65; + --button-bg: #8f7a66; + --button-text: #f9f6f2; + --button-hover: #9f8b77; + --button-active: #7f6a56; + --shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + --transition: all 0.2s ease-in-out; +} + +.dark-theme { + --bg-color: #2c2c2c; + --text-color: #f8f8f8; + --grid-bg-color: #4d443b; + --grid-cell-bg: #3a3329; + --grid-text-color: #f8f8f8; + --button-bg: #6d5c4d; + --button-hover: #7d6c5d; + --button-active: #5d4c3d; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + body { background-color: var(--bg-color); color: var(--text-color); @@ -6,90 +38,280 @@ body { display: flex; justify-content: center; align-items: center; - font-family: 'Clear Sans', 'Helvetica Neue', Arial, sans-serif; + font-family: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif; + line-height: 1.6; + transition: var(--transition); } -/* Center game container and add padding */ +/* Game Container */ .game-container { - padding: 2rem; - border-radius: 10px; - box-shadow: 0px 4px 20px rgba(0,0,0,0.1); + padding: 1.5rem; + border-radius: 12px; + box-shadow: var(--shadow); display: flex; flex-direction: column; align-items: center; width: 95vw; - max-width: 520px; + max-width: 500px; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.2); } -/* Header styling */ +/* Header */ +header { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1.5rem; + flex-wrap: wrap; + gap: 1rem; +} + +h1 { + font-size: 2.5rem; + font-weight: 800; + color: #776e65; + margin: 0; + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1); +} + +/* Score Containers */ .score-container { - display: inline-block; - margin: 0 8px; + background: var(--grid-bg-color); + border-radius: 6px; + padding: 0.5rem 1rem; + min-width: 80px; + text-align: center; + color: #eee4da; + font-weight: bold; + box-shadow: var(--shadow); + transition: var(--transition); } -/* Grid and cells */ +.score-container div:first-child { + font-size: 0.8rem; + text-transform: uppercase; + color: #eee4da; +} + +#score, +#best-score { + font-size: 1.2rem; + color: white; +} + +/* Game Grid */ .grid-wrapper { width: 100%; display: flex; justify-content: center; + margin-bottom: 1.5rem; } .grid-container { width: 100%; - max-width: 400px; + max-width: 450px; aspect-ratio: 1/1; position: relative; display: flex; justify-content: center; align-items: center; + padding: 0.5rem; } .grid { display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: repeat(4, 1fr); - gap: 12px; - background:var(--grid-bg-color); - padding: 14px; - border-radius: 6px; + gap: 1rem; + background: var(--grid-bg-color); + padding: 1rem; + border-radius: 8px; width: 100%; height: 100%; box-sizing: border-box; - aspect-ratio: 1/1; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); } .grid-cell { - background:var(--grid-cell-bg-color); - border-radius: 4px; + background: var(--grid-cell-bg); + border-radius: 6px; display: flex; justify-content: center; align-items: center; - font-size: 1.4rem; + font-size: 1.8rem; + font-weight: 700; color: var(--grid-text-color); user-select: none; aspect-ratio: 1/1; - /*squares update-2 */ - width: 100%; - height: 100%; + transition: var(--transition); + box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.1); + position: relative; + overflow: hidden; } -/* Light Theme Default */ -:root{ - --bg-color: #ffffff; - --text-color: #000000; - --grid-bg-color: #bbada0; - --grid-cell-bg-color:#eee4da; - --grid-text-color: #776e65; +/* Tile animations */ +@keyframes appear { + 0% { transform: scale(0.2); opacity: 0; } + 100% { transform: scale(1); opacity: 1; } +} +@keyframes pop { + 0% { transform: scale(0.8); } + 50% { transform: scale(1.1); } + 100% { transform: scale(1); } +} + +.tile { + position: absolute; + display: flex; + justify-content: center; + align-items: center; + width: calc(25% - 0.75rem); + height: calc(25% - 0.75rem); + border-radius: 6px; + font-size: 1.8rem; + font-weight: 700; + color: var(--grid-text-color); + transition: all 0.15s ease-in-out; + animation: appear 0.2s ease-in-out; + z-index: 2; } -/* Drak Theme */ -.dark-theme { - --bg-color: #776e65; - --text-color: #ffffff; - --grid-bg-color: #4d443b; - --grid-cell-bg-color:#eee4da; - --grid-text-color: #131110; +.tile.merged { + animation: pop 0.2s ease-in-out; + z-index: 3; +} + +/* Buttons */ +button { + background: var(--button-bg); + color: var(--button-text); + border: none; + border-radius: 6px; + padding: 0.6rem 1.2rem; + font-size: 1rem; + font-weight: 600; + cursor: pointer; + transition: var(--transition); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +button:hover { + background: var(--button-hover); + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); +} + +button:active { + transform: translateY(0); + background: var(--button-active); +} + +button:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +#theme-toggle { + width: 40px; + height: 40px; + padding: 0; + border-radius: 50%; + font-size: 1.2rem; + display: flex; + align-items: center; + justify-content: center; +} + +/* Game Message */ +.game-message { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(238, 228, 218, 0.9); + z-index: 100; + display: none; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 2rem; + animation: fadeIn 0.3s ease-in-out; +} + +.dark-theme .game-message { + background: rgba(58, 51, 41, 0.9); + color: #f8f8f8; +} + +.game-message p { + font-size: 2.5rem; + font-weight: 700; + margin-bottom: 2rem; + color: #776e65; +} + +.dark-theme .game-message p { + color: #f8f8f8; +} + +.game-message button { + font-size: 1.2rem; + padding: 0.8rem 2rem; + margin: 0.5rem; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +/* Responsive Design */ +@media (max-width: 520px) { + .game-container { + padding: 1rem; + max-width: 100%; + height: 100vh; + border-radius: 0; + justify-content: flex-start; + padding-top: 1rem; + } + + h1 { + font-size: 2rem; + } + + .score-container { + padding: 0.3rem 0.6rem; + min-width: 60px; + } + + .grid { + gap: 0.5rem; + padding: 0.5rem; + } + + .grid-cell { + font-size: 1.2rem; + } + + button { + padding: 0.5rem 1rem; + font-size: 0.9rem; + } } @media (max-width: 500px) { .game-container {