- BC20270270's blog
2048(html)(终极优化版)
- 2025-6-14 19:28:38 @
使用说明
游戏操作:
使用键盘方向键(↑↓←→)移动方块
在触摸设备上可以通过滑动屏幕移动方块
点击"撤销"按钮可以回退到上一步操作
点击"新游戏"按钮重新开始游戏
回溯功能:
最多可以回溯5步操作
游戏结束或获胜后无法回溯
回溯会恢复分数和方块位置
游戏目标:
合并相同数字的方块
尝试创建2048方块
获得尽可能高的分数
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>炫酷2048游戏 - 终极优化版</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
user-select: none;
}
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #0c0b1d, #252348, #1a1a33);
color: #fff;
overflow: hidden;
position: relative;
}
/* 粒子背景 */
.particles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
overflow: hidden;
}
.particle {
position: absolute;
width: 4px;
height: 4px;
background: rgba(255, 255, 255, 0.5);
border-radius: 50%;
animation: float 15s infinite linear;
}
@keyframes float {
0% {
transform: translateY(100vh) translateX(0);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(-100px) translateX(100px);
opacity: 0;
}
}
.game-container {
max-width: 500px;
width: 100%;
padding: 20px;
position: relative;
z-index: 10;
}
.game-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.title {
font-size: 3rem;
font-weight: 800;
background: linear-gradient(to right, #ff9a9e, #fad0c4, #fad0c4, #a18cd1);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 15px rgba(255, 154, 158, 0.4);
letter-spacing: 2px;
}
.scores-container {
display: flex;
gap: 10px;
}
.score-box {
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 12px 18px;
text-align: center;
min-width: 110px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
}
.score-title {
font-size: 0.9rem;
font-weight: 600;
color: #ffca28;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 5px;
}
.score-value {
font-size: 1.8rem;
font-weight: 800;
color: #fff;
}
.game-intro {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.game-controls {
display: flex;
gap: 10px;
}
.game-button {
background: linear-gradient(to right, #4facfe, #00f2fe);
color: white;
border: none;
border-radius: 8px;
padding: 12px 20px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 4px 12px rgba(79, 172, 254, 0.4);
display: flex;
align-items: center;
gap: 8px;
}
.game-button.undo {
background: linear-gradient(to right, #ff9a9e, #fad0c4);
}
.game-button:hover {
transform: translateY(-3px);
box-shadow: 0 6px 18px rgba(79, 172, 254, 0.6);
}
.game-button:active {
transform: translateY(1px);
}
.game-message {
font-size: 1.2rem;
font-weight: 600;
height: 30px;
display: flex;
align-items: center;
padding: 0 15px;
border-radius: 6px;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(5px);
}
.game-win {
color: #4caf50;
text-shadow: 0 0 10px rgba(76, 175, 80, 0.5);
}
.game-over {
color: #f44336;
text-shadow: 0 0 10px rgba(244, 67, 54, 0.5);
}
.game-grid-container {
background: rgba(255, 255, 255, 0.05);
border-radius: 12px;
padding: 15px;
margin-bottom: 20px;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.4);
position: relative;
}
.grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-gap: 15px;
position: relative;
}
.grid-cell {
width: 100%;
height: 0;
padding-bottom: 100%;
background: rgba(255, 255, 255, 0.08);
border-radius: 8px;
position: relative;
box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.2);
}
.tile-container {
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
z-index: 10;
}
.tile {
position: absolute;
width: calc(25% - 11.25px);
height: calc(25% - 11.25px);
border-radius: 8px;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
font-weight: 800;
z-index: 10;
transition: transform 0.15s ease, opacity 0.15s ease;
animation-duration: 0.2s;
animation-fill-mode: both;
/* 添加平滑移动动画 */
transition: transform 0.15s ease, opacity 0.15s ease;
}
/* 添加移动动画效果 */
.tile-moving {
transition: transform 0.15s ease;
}
.tile-new {
animation: appear 0.3s;
}
.tile-merged {
animation: pop 0.4s;
}
/* 增强合并动画效果 */
@keyframes pop {
0% {
transform: scale(0.9);
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7);
}
50% {
transform: scale(1.2);
box-shadow: 0 0 0 10px rgba(255, 255, 255, 0);
}
100% {
transform: scale(1);
}
}
@keyframes appear {
0% {
transform: scale(0);
opacity: 0;
}
70% {
transform: scale(1.1);
opacity: 1;
}
100% {
transform: scale(1);
}
}
/* 方向指示器 */
.direction-indicator {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 4rem;
opacity: 0;
pointer-events: none;
z-index: 20;
text-shadow: 0 0 20px rgba(255, 255, 255, 0.8);
animation: fadeDirection 0.5s ease-out;
}
@keyframes fadeDirection {
0% {
opacity: 0.8;
transform: translate(-50%, -50%) scale(1.5);
}
100% {
opacity: 0;
transform: translate(-50%, -50%) scale(2.5);
}
}
/* 不同数值的方块颜色 */
.tile-2 { background: #3a3a5d; color: #eee; font-size: 1.8rem; }
.tile-4 { background: #2a2a4a; color: #eee; font-size: 1.8rem; }
.tile-8 { background: linear-gradient(135deg, #ff7043, #ff8c5a); color: #fff; font-size: 1.8rem; box-shadow: 0 5px 15px rgba(255, 112, 67, 0.4); }
.tile-16 { background: linear-gradient(135deg, #ff5722, #ff7043); color: #fff; font-size: 1.7rem; box-shadow: 0 5px 15px rgba(255, 87, 34, 0.4); }
.tile-32 { background: linear-gradient(135deg, #f44336, #ff5722); color: #fff; font-size: 1.7rem; box-shadow: 0 5px 15px rgba(244, 67, 54, 0.4); }
.tile-64 { background: linear-gradient(135deg, #d32f2f, #f44336); color: #fff; font-size: 1.7rem; box-shadow: 0 5px 15px rgba(211, 47, 47, 0.4); }
.tile-128 { background: linear-gradient(135deg, #ffca28, #ffd54f); color: #333; font-size: 1.6rem; box-shadow: 0 5px 20px rgba(255, 202, 40, 0.5); }
.tile-256 { background: linear-gradient(135deg, #ffc107, #ffca28); color: #333; font-size: 1.6rem; box-shadow: 0 5px 20px rgba(255, 193, 7, 0.5); }
.tile-512 { background: linear-gradient(135deg, #ffb300, #ffc107); color: #333; font-size: 1.5rem; box-shadow: 0 5px 20px rgba(255, 179, 0, 0.5); }
.tile-1024 { background: linear-gradient(135deg, #ffa000, #ffb300); color: #333; font-size: 1.4rem; box-shadow: 0 5px 20px rgba(255, 160, 0, 0.6); }
.tile-2048 { background: linear-gradient(135deg, #ff8f00, #ffa000); color: #333; font-size: 1.3rem; box-shadow: 0 5px 20px rgba(255, 143, 0, 0.6); }
.tile-super { background: linear-gradient(135deg, #4a148c, #7b1fa2); color: #fff; font-size: 1.2rem; box-shadow: 0 5px 20px rgba(74, 20, 140, 0.6); }
.game-explanation {
background: rgba(255, 255, 255, 0.08);
border-radius: 12px;
padding: 20px;
font-size: 1rem;
line-height: 1.6;
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
}
.game-explanation p {
margin-bottom: 10px;
}
.highlight {
color: #ffca28;
font-weight: 600;
}
.keys {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 15px;
}
.key {
background: rgba(255, 255, 255, 0.15);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
padding: 8px 15px;
font-size: 1rem;
min-width: 40px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
/* 响应式设计 */
@media (max-width: 520px) {
.grid {
grid-gap: 10px;
}
.tile {
width: calc(25% - 7.5px);
height: calc(25% - 7.5px);
font-size: 1.5rem;
}
.title {
font-size: 2.2rem;
}
.score-box {
min-width: 90px;
padding: 10px 12px;
}
.score-value {
font-size: 1.6rem;
}
.game-button {
padding: 10px 15px;
font-size: 0.9rem;
}
.tile-2, .tile-4, .tile-8 { font-size: 1.5rem; }
.tile-16, .tile-32, .tile-64 { font-size: 1.4rem; }
.tile-128, .tile-256 { font-size: 1.3rem; }
.tile-512, .tile-1024 { font-size: 1.2rem; }
.tile-2048, .tile-super { font-size: 1.1rem; }
}
</style>
</head>
<body>
<!-- 粒子背景 -->
<div class="particles" id="particles"></div>
<!-- 方向指示器 -->
<div class="direction-indicator" id="direction-indicator"></div>
<div class="game-container">
<div class="game-header">
<div class="title">2048</div>
<div class="scores-container">
<div class="score-box">
<div class="score-title">分数</div>
<div class="score-value" id="score">0</div>
</div>
<div class="score-box">
<div class="score-title">最高分</div>
<div class="score-value" id="best-score">0</div>
</div>
</div>
</div>
<div class="game-intro">
<div class="game-message" id="game-message"></div>
<div class="game-controls">
<button class="game-button undo" id="undo-button">
<i class="fas fa-undo"></i> 撤销
</button>
<button class="game-button" id="restart-button">
<i class="fas fa-redo"></i> 新游戏
</button>
</div>
</div>
<div class="game-grid-container">
<div class="grid">
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
<div class="grid-cell"></div>
</div>
<div class="tile-container" id="tile-container"></div>
</div>
<div class="game-explanation">
<p><span class="highlight">游戏规则:</span>使用键盘方向键或触摸滑动移动方块。当两个相同数字的方块碰撞时,它们会<span class="highlight">合并成一个新的方块</span>!</p>
<p>每次移动后,会在空白位置随机生成一个2或4的方块。当得到<span class="highlight">2048</span>方块时获胜,当没有可移动的方块时游戏结束。</p>
<div class="keys">
<div class="key">↑</div>
<div class="key">↓</div>
<div class="key">←</div>
<div class="key">→</div>
</div>
</div>
</div>
<script>
// 游戏状态变量
let grid = [];
let score = 0;
let bestScore = localStorage.getItem('bestScore') || 0;
let gameOver = false;
let gameWon = false;
let tileContainer;
let history = []; // 用于回溯功能
const MAX_HISTORY = 5; // 最多保存5步历史
let tiles = []; // 存储所有方块元素
// 初始化游戏
function initGame() {
grid = Array(4).fill().map(() => Array(4).fill(0));
score = 0;
gameOver = false;
gameWon = false;
history = [];
tiles = [];
document.getElementById('score').textContent = '0';
document.getElementById('best-score').textContent = bestScore;
document.getElementById('game-message').textContent = '';
document.getElementById('game-message').className = 'game-message';
tileContainer = document.getElementById('tile-container');
tileContainer.innerHTML = '';
// 添加两个初始方块
addRandomTile();
addRandomTile();
// 创建粒子背景
createParticles();
// 保存初始状态
saveState();
}
// 创建粒子背景
function createParticles() {
const particlesContainer = document.getElementById('particles');
particlesContainer.innerHTML = '';
for (let i = 0; i < 80; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
// 随机位置
particle.style.left = `${Math.random() * 100}%`;
particle.style.top = `${Math.random() * 100}%`;
// 随机动画延迟和持续时间
particle.style.animationDelay = `${Math.random() * 15}s`;
particle.style.animationDuration = `${10 + Math.random() * 15}s`;
particlesContainer.appendChild(particle);
}
}
// 添加随机方块
function addRandomTile() {
const emptyCells = [];
// 收集所有空格子
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
if (grid[r][c] === 0) {
emptyCells.push({ r, c });
}
}
}
if (emptyCells.length > 0) {
// 随机选择一个空格子
const randIndex = Math.floor(Math.random() * emptyCells.length);
const cell = emptyCells[randIndex];
const value = Math.random() < 0.9 ? 2 : 4;
// 更新网格
grid[cell.r][cell.c] = value;
// 创建方块元素
createTileElement(cell.r, cell.c, value, true);
}
}
// 创建方块元素
function createTileElement(row, col, value, isNew = false) {
const tile = document.createElement('div');
tile.className = `tile tile-${value}`;
if (isNew) tile.classList.add('tile-new');
tile.textContent = value;
tile.dataset.row = row;
tile.dataset.col = col;
tile.dataset.value = value;
// 计算位置
const cellSize = tileContainer.offsetWidth / 4;
tile.style.left = `${col * cellSize}px`;
tile.style.top = `${row * cellSize}px`;
tileContainer.appendChild(tile);
tiles.push(tile);
return tile;
}
// 保存当前状态用于回溯
function saveState() {
// 只保存最近几步
if (history.length >= MAX_HISTORY) {
history.shift();
}
history.push({
grid: JSON.parse(JSON.stringify(grid)),
score: score
});
}
// 显示方向指示器
function showDirectionIndicator(direction) {
const indicator = document.getElementById('direction-indicator');
let symbol = '';
switch(direction) {
case 'left': symbol = '←'; break;
case 'right': symbol = '→'; break;
case 'up': symbol = '↑'; break;
case 'down': symbol = '↓'; break;
}
indicator.textContent = symbol;
indicator.style.animation = 'none';
void indicator.offsetWidth; // 触发重绘
indicator.style.animation = 'fadeDirection 0.5s ease-out';
}
// 移动方块
function move(direction) {
if (gameOver) return;
// 显示方向指示器
showDirectionIndicator(direction);
// 保存当前状态
saveState();
let moved = false;
// 根据方向处理移动
if (direction === 'left') {
moved = moveLeft();
} else if (direction === 'right') {
moved = moveRight();
} else if (direction === 'up') {
moved = moveUp();
} else if (direction === 'down') {
moved = moveDown();
}
// 如果有移动,添加新方块并检查游戏状态
if (moved) {
// 添加新方块前先更新显示
updateTiles();
addRandomTile();
updateGameState();
}
}
// 向左移动
function moveLeft() {
let moved = false;
const moves = [];
for (let r = 0; r < 4; r++) {
// 移动方块到左侧
for (let c = 0; c < 4; c++) {
if (grid[r][c] !== 0) {
let targetCol = c;
while (targetCol > 0 && grid[r][targetCol - 1] === 0) {
targetCol--;
}
if (targetCol !== c) {
grid[r][targetCol] = grid[r][c];
grid[r][c] = 0;
moved = true;
moves.push({
from: { r, c },
to: { r, targetCol },
value: grid[r][targetCol]
});
}
}
}
// 合并相同数字
for (let c = 0; c < 3; c++) {
if (grid[r][c] !== 0 && grid[r][c] === grid[r][c + 1]) {
const newValue = grid[r][c] * 2;
grid[r][c] = newValue;
grid[r][c + 1] = 0;
score += newValue;
moved = true;
// 移动右侧的方块
for (let i = c + 1; i < 3; i++) {
grid[r][i] = grid[r][i + 1];
grid[r][i + 1] = 0;
}
// 记录合并
moves.push({
from: { r, c: c + 1 },
to: { r, c },
value: newValue,
merged: true
});
}
}
}
// 执行移动动画
animateMoves(moves);
return moved;
}
// 向右移动
function moveRight() {
let moved = false;
const moves = [];
for (let r = 0; r < 4; r++) {
// 移动方块到右侧
for (let c = 3; c >= 0; c--) {
if (grid[r][c] !== 0) {
let targetCol = c;
while (targetCol < 3 && grid[r][targetCol + 1] === 0) {
targetCol++;
}
if (targetCol !== c) {
grid[r][targetCol] = grid[r][c];
grid[r][c] = 0;
moved = true;
moves.push({
from: { r, c },
to: { r, targetCol },
value: grid[r][targetCol]
});
}
}
}
// 合并相同数字
for (let c = 3; c > 0; c--) {
if (grid[r][c] !== 0 && grid[r][c] === grid[r][c - 1]) {
const newValue = grid[r][c] * 2;
grid[r][c] = newValue;
grid[r][c - 1] = 0;
score += newValue;
moved = true;
// 移动左侧的方块
for (let i = c - 1; i > 0; i--) {
grid[r][i] = grid[r][i - 1];
grid[r][i - 1] = 0;
}
// 记录合并
moves.push({
from: { r, c: c - 1 },
to: { r, c },
value: newValue,
merged: true
});
}
}
}
// 执行移动动画
animateMoves(moves);
return moved;
}
// 向上移动
function moveUp() {
let moved = false;
const moves = [];
for (let c = 0; c < 4; c++) {
// 移动方块到上方
for (let r = 0; r < 4; r++) {
if (grid[r][c] !== 0) {
let targetRow = r;
while (targetRow > 0 && grid[targetRow - 1][c] === 0) {
targetRow--;
}
if (targetRow !== r) {
grid[targetRow][c] = grid[r][c];
grid[r][c] = 0;
moved = true;
moves.push({
from: { r, c },
to: { r: targetRow, c },
value: grid[targetRow][c]
});
}
}
}
// 合并相同数字
for (let r = 0; r < 3; r++) {
if (grid[r][c] !== 0 && grid[r][c] === grid[r + 1][c]) {
const newValue = grid[r][c] * 2;
grid[r][c] = newValue;
grid[r + 1][c] = 0;
score += newValue;
moved = true;
// 移动下方的方块
for (let i = r + 1; i < 3; i++) {
grid[i][c] = grid[i + 1][c];
grid[i + 1][c] = 0;
}
// 记录合并
moves.push({
from: { r: r + 1, c },
to: { r, c },
value: newValue,
merged: true
});
}
}
}
// 执行移动动画
animateMoves(moves);
return moved;
}
// 向下移动
function moveDown() {
let moved = false;
const moves = [];
for (let c = 0; c < 4; c++) {
// 移动方块到下方
for (let r = 3; r >= 0; r--) {
if (grid[r][c] !== 0) {
let targetRow = r;
while (targetRow < 3 && grid[targetRow + 1][c] === 0) {
targetRow++;
}
if (targetRow !== r) {
grid[targetRow][c] = grid[r][c];
grid[r][c] = 0;
moved = true;
moves.push({
from: { r, c },
to: { r: targetRow, c },
value: grid[targetRow][c]
});
}
}
}
// 合并相同数字
for (let r = 3; r > 0; r--) {
if (grid[r][c] !== 0 && grid[r][c] === grid[r - 1][c]) {
const newValue = grid[r][c] * 2;
grid[r][c] = newValue;
grid[r - 1][c] = 0;
score += newValue;
moved = true;
// 移动上方的方块
for (let i = r - 1; i > 0; i--) {
grid[i][c] = grid[i - 1][c];
grid[i - 1][c] = 0;
}
// 记录合并
moves.push({
from: { r: r - 1, c },
to: { r, c },
value: newValue,
merged: true
});
}
}
}
// 执行移动动画
animateMoves(moves);
return moved;
}
// 执行移动动画
function animateMoves(moves) {
const cellSize = tileContainer.offsetWidth / 4;
// 先移除所有方块的移动状态
tiles.forEach(tile => {
tile.classList.remove('tile-moving');
});
// 执行动画
moves.forEach(move => {
const tile = findTileAt(move.from.r, move.from.c);
if (tile) {
tile.classList.add('tile-moving');
// 更新位置
tile.style.left = `${move.to.c * cellSize}px`;
tile.style.top = `${move.to.r * cellSize}px`;
// 更新数据属性
tile.dataset.row = move.to.r;
tile.dataset.col = move.to.c;
// 如果是合并,添加合并动画
if (move.merged) {
setTimeout(() => {
tile.classList.add('tile-merged');
tile.textContent = move.value;
tile.className = `tile tile-${move.value} tile-merged`;
}, 150);
}
}
});
}
// 根据位置查找方块
function findTileAt(row, col) {
return tiles.find(tile =>
parseInt(tile.dataset.row) === row &&
parseInt(tile.dataset.col) === col
);
}
// 更新游戏状态
function updateGameState() {
document.getElementById('score').textContent = score;
// 更新最高分
if (score > bestScore) {
bestScore = score;
localStorage.setItem('bestScore', bestScore);
document.getElementById('best-score').textContent = bestScore;
}
// 检查是否获胜
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
if (grid[r][c] === 2048) {
gameWon = true;
}
}
}
if (gameWon) {
document.getElementById('game-message').textContent = '恭喜!你赢了!';
document.getElementById('game-message').classList.add('game-win');
return;
}
// 检查是否失败
if (!hasAvailableMoves()) {
gameOver = true;
document.getElementById('game-message').textContent = '游戏结束!';
document.getElementById('game-message').classList.add('game-over');
}
}
// 检查是否还有可移动的步数
function hasAvailableMoves() {
// 检查是否有空格子
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
if (grid[r][c] === 0) {
return true;
}
}
}
// 检查是否有可以合并的相邻方块
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 3; c++) {
if (grid[r][c] === grid[r][c + 1]) {
return true;
}
}
}
for (let r = 0; r < 3; r++) {
for (let c = 0; c < 4; c++) {
if (grid[r][c] === grid[r + 1][c]) {
return true;
}
}
}
return false;
}
// 更新方块显示
function updateTiles() {
// 移除已不存在的方块
tiles = tiles.filter(tile => {
const r = parseInt(tile.dataset.row);
const c = parseInt(tile.dataset.col);
if (grid[r][c] !== parseInt(tile.dataset.value)) {
tile.remove();
return false;
}
return true;
});
// 添加新方块
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
if (grid[r][c] !== 0 && !findTileAt(r, c)) {
createTileElement(r, c, grid[r][c]);
}
}
}
}
// 回溯功能
function undoMove() {
if (history.length < 2 || gameOver || gameWon) return;
// 移除当前状态
history.pop();
// 恢复上一个状态
const prevState = history[history.length - 1];
grid = JSON.parse(JSON.stringify(prevState.grid));
score = prevState.score;
// 更新显示
updateGameState();
updateTiles();
}
// 事件监听器
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') {
move('left');
} else if (e.key === 'ArrowRight') {
move('right');
} else if (e.key === 'ArrowUp') {
move('up');
} else if (e.key === 'ArrowDown') {
move('down');
}
});
// 触摸滑动支持
let touchStartX, touchStartY;
let touchStartTime;
document.addEventListener('touchstart', (e) => {
touchStartX = e.touches[0].clientX;
touchStartY = e.touches[0].clientY;
touchStartTime = new Date().getTime();
});
document.addEventListener('touchend', (e) => {
if (!touchStartX || !touchStartY) return;
const touchEndX = e.changedTouches[0].clientX;
const touchEndY = e.changedTouches[0].clientY;
const touchDuration = new Date().getTime() - touchStartTime;
const dx = touchEndX - touchStartX;
const dy = touchEndY - touchStartY;
// 确定滑动方向(仅在滑动距离足够大且时间短时)
if ((Math.abs(dx) > 20 || Math.abs(dy) > 20) && touchDuration < 500) {
if (Math.abs(dx) > Math.abs(dy)) {
if (dx > 0) {
move('right');
} else {
move('left');
}
} else {
if (dy > 0) {
move('down');
} else {
move('up');
}
}
}
// 重置触摸点
touchStartX = null;
touchStartY = null;
});
// 新游戏按钮
document.getElementById('restart-button').addEventListener('click', () => {
initGame();
});
// 撤销按钮
document.getElementById('undo-button').addEventListener('click', () => {
undoMove();
});
// 初始化游戏
window.onload = () => {
initGame();
};
</script>
</body>
</html>