class Game2048 { constructor() { this.size = 4; this.grid = []; this.score = 0; this.gameWon = false; this.gameOver = false; this.moved = false; this.stats = { moves: 0, startTime: null, gameTime: 0, maxTile: 2, mergeCount: 0 }; this.initializeGrid(); this.updateDisplay(); this.addRandomTile(); this.addRandomTile(); this.updateDisplay(); this.bindEvents(); this.startTimer(); } initializeGrid() { this.grid = []; for (let i = 0; i < this.size; i++) { this.grid[i] = []; for (let j = 0; j < this.size; j++) { this.grid[i][j] = 0; } } } addRandomTile() { const emptyCells = []; for (let i = 0; i < this.size; i++) { for (let j = 0; j < this.size; j++) { if (this.grid[i][j] === 0) emptyCells.push({ x: i, y: j }); } } if (emptyCells.length > 0) { const cell = emptyCells[Math.floor(Math.random() * emptyCells.length)]; const value = Math.random() < 0.9 ? 2 : 4; this.grid[cell.x][cell.y] = value; this.createTileElement(cell.x, cell.y, value, true); } } createTileElement(x, y, value, isNew = false) { const container = document.getElementById('tile-container'); const tile = document.createElement('div'); tile.className = `tile tile-${value}`; if (isNew) tile.classList.add('tile-new'); tile.textContent = value; tile.style.left = `${y * 25}%`; tile.style.top = `${x * 25}%`; tile.dataset.x = x; tile.dataset.y = y; tile.dataset.value = value; container.appendChild(tile); setTimeout(() => tile.classList.remove('tile-new'), 200); } updateDisplay() { const container = document.getElementById('tile-container'); container.innerHTML = ''; for (let i = 0; i < this.size; i++) { for (let j = 0; j < this.size; j++) { if (this.grid[i][j] !== 0) this.createTileElement(i, j, this.grid[i][j]); } } document.getElementById('score').textContent = this.score; } move(direction) { if (this.gameOver) return; this.moved = false; switch (direction) { case 'up': this.moveUp(); break; case 'down': this.moveDown(); break; case 'left': this.moveLeft(); break; case 'right': this.moveRight(); break; } if (this.moved) { this.stats.moves++; this.addRandomTile(); this.updateDisplay(); if (this.isGameWon() && !this.gameWon) { this.gameWon = true; this.showEndScreen('你赢了!🎉'); } else if (this.isGameOver()) { this.gameOver = true; this.showEndScreen('游戏结束!'); } } } moveLeft() { for (let i = 0; i < this.size; i++) { const row = this.grid[i].filter(v => v !== 0); const merged = []; for (let j = 0; j < row.length - 1; j++) { if (row[j] === row[j + 1] && !merged[j] && !merged[j + 1]) { row[j] *= 2; this.score += row[j]; this.stats.mergeCount++; this.stats.maxTile = Math.max(this.stats.maxTile, row[j]); row[j + 1] = 0; merged[j] = true; } } const newRow = row.filter(v => v !== 0); while (newRow.length < this.size) newRow.push(0); for (let j = 0; j < this.size; j++) { if (this.grid[i][j] !== newRow[j]) this.moved = true; this.grid[i][j] = newRow[j]; } } } moveRight() { for (let i = 0; i < this.size; i++) { const row = this.grid[i].filter(v => v !== 0); const merged = []; for (let j = row.length - 1; j > 0; j--) { if (row[j] === row[j - 1] && !merged[j] && !merged[j - 1]) { row[j] *= 2; this.score += row[j]; this.stats.mergeCount++; this.stats.maxTile = Math.max(this.stats.maxTile, row[j]); row[j - 1] = 0; merged[j] = true; } } const newRow = row.filter(v => v !== 0); while (newRow.length < this.size) newRow.unshift(0); for (let j = 0; j < this.size; j++) { if (this.grid[i][j] !== newRow[j]) this.moved = true; this.grid[i][j] = newRow[j]; } } } moveUp() { for (let j = 0; j < this.size; j++) { const col = []; for (let i = 0; i < this.size; i++) { if (this.grid[i][j] !== 0) col.push(this.grid[i][j]); } const merged = []; for (let i = 0; i < col.length - 1; i++) { if (col[i] === col[i + 1] && !merged[i] && !merged[i + 1]) { col[i] *= 2; this.score += col[i]; this.stats.mergeCount++; this.stats.maxTile = Math.max(this.stats.maxTile, col[i]); col[i + 1] = 0; merged[i] = true; } } const newCol = col.filter(v => v !== 0); while (newCol.length < this.size) newCol.push(0); for (let i = 0; i < this.size; i++) { if (this.grid[i][j] !== newCol[i]) this.moved = true; this.grid[i][j] = newCol[i]; } } } moveDown() { for (let j = 0; j < this.size; j++) { const col = []; for (let i = 0; i < this.size; i++) { if (this.grid[i][j] !== 0) col.push(this.grid[i][j]); } const merged = []; for (let i = col.length - 1; i > 0; i--) { if (col[i] === col[i - 1] && !merged[i] && !merged[i - 1]) { col[i] *= 2; this.score += col[i]; this.stats.mergeCount++; this.stats.maxTile = Math.max(this.stats.maxTile, col[i]); col[i - 1] = 0; merged[i] = true; } } const newCol = col.filter(v => v !== 0); while (newCol.length < this.size) newCol.unshift(0); for (let i = 0; i < this.size; i++) { if (this.grid[i][j] !== newCol[i]) this.moved = true; this.grid[i][j] = newCol[i]; } } } isGameWon() { for (let i = 0; i < this.size; i++) for (let j = 0; j < this.size; j++) if (this.grid[i][j] === 2048) return true; return false; } isGameOver() { for (let i = 0; i < this.size; i++) for (let j = 0; j < this.size; j++) { if (this.grid[i][j] === 0) return false; const c = this.grid[i][j]; if ((i > 0 && this.grid[i-1][j] === c) || (i < this.size-1 && this.grid[i+1][j] === c) || (j > 0 && this.grid[i][j-1] === c) || (j < this.size-1 && this.grid[i][j+1] === c)) return false; } return true; } showEndScreen(text) { const message = document.getElementById('game-message'); message.className = 'game-message ' + (this.gameWon ? 'game-won' : 'game-over'); message.style.display = 'flex'; message.querySelector('p').innerHTML = `${text}
` + `得分 ${this.score} · 步数 ${this.stats.moves} · ` + `最大方块 ${this.stats.maxTile} · 用时 ${this.stats.gameTime}秒`; } restart() { this.score = 0; this.gameWon = false; this.gameOver = false; this.moved = false; this.stats = { moves: 0, startTime: null, gameTime: 0, maxTile: 2, mergeCount: 0 }; this.initializeGrid(); this.addRandomTile(); this.addRandomTile(); this.updateDisplay(); document.getElementById('game-message').style.display = 'none'; this.startTimer(); } startTimer() { this.stats.startTime = Date.now(); if (this.timerInterval) clearInterval(this.timerInterval); this.timerInterval = setInterval(() => { if (!this.gameOver && this.stats.startTime) { this.stats.gameTime = Math.floor((Date.now() - this.stats.startTime) / 1000); } }, 1000); } bindEvents() { document.getElementById('retry-btn').addEventListener('click', () => this.restart()); } } let game; document.addEventListener('DOMContentLoaded', () => { game = new Game2048(); window.game2048 = game; });