Spaces:
Running
Running
| class Game { | |
| constructor() { | |
| this.grid = Array(4).fill().map(() => Array(4).fill(0)); | |
| this.score = 0; | |
| this.init(); | |
| } | |
| init() { | |
| this.addNewTile(); | |
| this.addNewTile(); | |
| this.updateDisplay(); | |
| } | |
| addNewTile() { | |
| const emptyCells = []; | |
| for (let i = 0; i < 4; i++) { | |
| for (let j = 0; j < 4; j++) { | |
| if (this.grid[i][j] === 0) { | |
| emptyCells.push({x: i, y: j}); | |
| } | |
| } | |
| } | |
| if (emptyCells.length > 0) { | |
| const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)]; | |
| this.grid[randomCell.x][randomCell.y] = Math.random() < 0.9 ? 2 : 4; | |
| } | |
| } | |
| updateDisplay() { | |
| for (let i = 0; i < 4; i++) { | |
| for (let j = 0; j < 4; j++) { | |
| const cell = document.getElementById(`cell-${i}-${j}`); | |
| const value = this.grid[i][j]; | |
| cell.className = 'grid-cell' + (value ? ` tile tile-${value}` : ''); | |
| cell.textContent = value || ''; | |
| } | |
| } | |
| document.getElementById('score').textContent = this.score; | |
| } | |
| move(direction) { | |
| let moved = false; | |
| const oldGrid = JSON.stringify(this.grid); | |
| switch(direction) { | |
| case 'left': | |
| moved = this.moveLeft(); | |
| break; | |
| case 'right': | |
| moved = this.moveRight(); | |
| break; | |
| case 'up': | |
| moved = this.moveUp(); | |
| break; | |
| case 'down': | |
| moved = this.moveDown(); | |
| break; | |
| } | |
| if (moved) { | |
| this.addNewTile(); | |
| this.updateDisplay(); | |
| if (this.isGameOver()) { | |
| alert('Game Over!'); | |
| } | |
| } | |
| } | |
| moveLeft() { | |
| let moved = false; | |
| for (let i = 0; i < 4; i++) { | |
| let row = this.grid[i].filter(cell => cell !== 0); | |
| for (let j = 0; j < row.length - 1; j++) { | |
| if (row[j] === row[j + 1]) { | |
| row[j] *= 2; | |
| this.score += row[j]; | |
| row.splice(j + 1, 1); | |
| moved = true; | |
| } | |
| } | |
| while (row.length < 4) { | |
| row.push(0); | |
| } | |
| if (row.join(',') !== this.grid[i].join(',')) { | |
| moved = true; | |
| } | |
| this.grid[i] = row; | |
| } | |
| return moved; | |
| } | |
| moveRight() { | |
| let moved = false; | |
| for (let i = 0; i < 4; i++) { | |
| let row = this.grid[i].filter(cell => cell !== 0); | |
| for (let j = row.length - 1; j > 0; j--) { | |
| if (row[j] === row[j - 1]) { | |
| row[j] *= 2; | |
| this.score += row[j]; | |
| row.splice(j - 1, 1); | |
| row.unshift(0); | |
| moved = true; | |
| } | |
| } | |
| while (row.length < 4) { | |
| row.unshift(0); | |
| } | |
| if (row.join(',') !== this.grid[i].join(',')) { | |
| moved = true; | |
| } | |
| this.grid[i] = row; | |
| } | |
| return moved; | |
| } | |
| moveUp() { | |
| let moved = false; | |
| for (let j = 0; j < 4; j++) { | |
| let column = []; | |
| for (let i = 0; i < 4; i++) { | |
| column.push(this.grid[i][j]); | |
| } | |
| column = column.filter(cell => cell !== 0); | |
| for (let i = 0; i < column.length - 1; i++) { | |
| if (column[i] === column[i + 1]) { | |
| column[i] *= 2; | |
| this.score += column[i]; | |
| column.splice(i + 1, 1); | |
| moved = true; | |
| } | |
| } | |
| while (column.length < 4) { | |
| column.push(0); | |
| } | |
| for (let i = 0; i < 4; i++) { | |
| if (this.grid[i][j] !== column[i]) { | |
| moved = true; | |
| } | |
| this.grid[i][j] = column[i]; | |
| } | |
| } | |
| return moved; | |
| } | |
| moveDown() { | |
| let moved = false; | |
| for (let j = 0; j < 4; j++) { | |
| let column = []; | |
| for (let i = 0; i < 4; i++) { | |
| column.push(this.grid[i][j]); | |
| } | |
| column = column.filter(cell => cell !== 0); | |
| for (let i = column.length - 1; i > 0; i--) { | |
| if (column[i] === column[i - 1]) { | |
| column[i] *= 2; | |
| this.score += column[i]; | |
| column.splice(i - 1, 1); | |
| column.unshift(0); | |
| moved = true; | |
| } | |
| } | |
| while (column.length < 4) { | |
| column.unshift(0); | |
| } | |
| for (let i = 0; i < 4; i++) { | |
| if (this.grid[i][j] !== column[i]) { | |
| moved = true; | |
| } | |
| this.grid[i][j] = column[i]; | |
| } | |
| } | |
| return moved; | |
| } | |
| isGameOver() { | |
| // Check for empty cells | |
| for (let i = 0; i < 4; i++) { | |
| for (let j = 0; j < 4; j++) { | |
| if (this.grid[i][j] === 0) { | |
| return false; | |
| } | |
| } | |
| } | |
| // Check for possible merges | |
| for (let i = 0; i < 4; i++) { | |
| for (let j = 0; j < 4; j++) { | |
| if (j < 3 && this.grid[i][j] === this.grid[i][j + 1]) return false; | |
| if (i < 3 && this.grid[i][j] === this.grid[i + 1][j]) return false; | |
| } | |
| } | |
| return true; | |
| } | |
| } | |
| // Initialize game | |
| let game = new Game(); | |
| // Event listeners | |
| document.addEventListener('keydown', (event) => { | |
| switch(event.key) { | |
| case 'ArrowLeft': | |
| game.move('left'); | |
| break; | |
| case 'ArrowRight': | |
| game.move('right'); | |
| break; | |
| case 'ArrowUp': | |
| game.move('up'); | |
| break; | |
| case 'ArrowDown': | |
| game.move('down'); | |
| break; | |
| } | |
| }); | |
| document.getElementById('new-game').addEventListener('click', () => { | |
| game = new Game(); | |
| }); |