2048 html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>2048 Game</title>
<style>
  @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');

  body {
    margin: 0;
    background: #faf8ef;
    font-family: 'Roboto', sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    padding: 20px;
    user-select: none;
    box-sizing: border-box;
  }

  .game-container {
    margin-top: 40px;
    background: #bbada0;
    border-radius: 10px;
    padding: 20px;
    width: max-content;
    box-shadow: 0 8px 15px rgba(0,0,0,0.2);
    box-sizing: border-box;
  }

  .header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 15px;
  }

  .title {
    font-size: 48px;
    font-weight: 700;
    color: #776e65;
    user-select: none;
  }

  .scores-container {
    display: flex;
    gap: 10px;
  }

  .score-box {
    background: #eee4da;
    border-radius: 5px;
    padding: 10px 15px;
    text-align: center;
    min-width: 70px;
    box-shadow: inset 0 -3px 0 rgba(0,0,0,0.2);
  }

  .score-label {
    font-size: 12px;
    color: #776e65;
    font-weight: 700;
    margin-bottom: 5px;
  }

  .score-value {
    font-size: 20px;
    font-weight: 700;
    color: #776e65;
  }

  .grid {
    background: #cdc1b4;
    border-radius: 10px;
    padding: 15px;
    width: 355px;
    height: 355px;
    overflow: hidden;
    display: grid;
    grid-template-columns: repeat(4, 70px);
    grid-template-rows: repeat(4, 70px);
    gap: 15px;
    box-sizing: border-box;
  }

  .grid-cell {
    background: #eee4da;
    border-radius: 5px;
  }

  .tile {
    /* Remove absolute positioning to align with grid cells */
    width: 70px;
    height: 70px;
    border-radius: 5px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-weight: 700;
    font-size: 28px;
    color: #776e65;
    user-select: none;
    transition: transform 0.2s ease, background-color 0.2s ease, color 0.2s ease;
    box-shadow: 0 2px 5px rgba(0,0,0,0.15);
  }

  .tile-2    { background: #eee4da; color: #776e65; }
  .tile-4    { background: #ede0c8; color: #776e65; }
  .tile-8    { background: #f2b179; color: #f9f6f2; }
  .tile-16   { background: #f59563; color: #f9f6f2; }
  .tile-32   { background: #f67c5f; color: #f9f6f2; }
  .tile-64   { background: #f65e3b; color: #f9f6f2; }
  .tile-128  { background: #edcf72; color: #f9f6f2; font-size: 22px; }
  .tile-256  { background: #edcc61; color: #f9f6f2; font-size: 22px; }
  .tile-512  { background: #edc850; color: #f9f6f2; font-size: 22px; }
  .tile-1024 { background: #edc53f; color: #f9f6f2; font-size: 18px; }
  .tile-2048 { background: #edc22e; color: #f9f6f2; font-size: 18px; }

  .game-message {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 300px;
    height: 100px;
    margin-left: -150px;
    margin-top: -50px;
    background: rgba(238, 228, 218, 0.73);
    border-radius: 10px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 36px;
    font-weight: 700;
    color: #776e65;
    z-index: 100;
    user-select: none;
    display: none;
  }

  .button-container {
    margin-top: 15px;
    display: flex;
    justify-content: center;
  }

  button {
    background: #8f7a66;
    border: none;
    border-radius: 5px;
    color: #f9f6f2;
    font-weight: 700;
    font-size: 16px;
    padding: 10px 20px;
    cursor: pointer;
    box-shadow: 0 3px 0 #6f5e4e;
    transition: background-color 0.3s ease;
  }

  button:hover {
    background: #9f8b7a;
  }

  @media (max-width: 400px) {
    .game-container {
      width: 90vw;
      padding: 10px;
    }
    .grid {
      grid-template-columns: repeat(4, 20vw);
      grid-template-rows: repeat(4, 20vw);
      gap: 4vw;
      padding: 10px;
    }
    .grid-cell, .tile {
      width: 20vw;
      height: 20vw;
      font-size: 6vw;
    }
  }
</style>
</head>
<body>
  <div class="game-container" role="main" aria-label="2048 game">
    <div class="header">
      <div class="title">2048</div>
      <div class="scores-container">
        <div class="score-box">
          <div class="score-label">SCORE</div>
          <div id="score" class="score-value">0</div>
        </div>
        <div class="score-box">
          <div class="score-label">BEST</div>
          <div id="best-score" class="score-value">0</div>
        </div>
      </div>
    </div>
    <div class="grid" id="grid" aria-label="Game grid" role="grid">
      <!-- Grid cells -->
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
      <div class="grid-cell" role="gridcell"></div>
    </div>
    <div class="game-message" id="game-message" role="alert" aria-live="assertive"></div>
    <div class="button-container">
      <button id="restart-button" aria-label="Restart Game">New Game</button>
    </div>
  </div>

<script>
  (() => {
    const gridSize = 4;
    const tileSize = 70;
    const tileGap = 15;
    const animationDuration = 200;

    let grid = [];
    let tiles = [];
    let score = 0;
    let bestScore = 0;
    let gameOver = false;
    let gameWon = false;

    const gridElement = document.getElementById('grid');
    const scoreElement = document.getElementById('score');
    const bestScoreElement = document.getElementById('best-score');
    const gameMessage = document.getElementById('game-message');
    const restartButton = document.getElementById('restart-button');

    // 初始化游戏状态和界面
    function init() {
      score = 0;
      gameOver = false;
      gameWon = false;
      loadBestScore();
      updateScore(0);
      grid = createEmptyGrid();
      tiles = [];
      clearTiles();
      addRandomTile();
      addRandomTile();
      updateUI();
      gameMessage.style.display = 'none';
      window.addEventListener('keydown', handleKeyDown);
    }

    // 创建一个空的4x4网格,所有格子初始化为0
    function createEmptyGrid() {
      let arr = [];
      for (let i = 0; i < gridSize; i++) {
        arr[i] = [];
        for (let j = 0; j < gridSize; j++) {
          arr[i][j] = 0;
        }
      }
      return arr;
    }

    // 清除所有已有的数字方块元素
    function clearTiles() {
      const existingTiles = document.querySelectorAll('.tile');
      existingTiles.forEach(t => t.remove());
    }

    // 在随机空白格子中添加一个新的数字方块(2或4)
    function addRandomTile() {
      let emptyCells = [];
      for (let r = 0; r < gridSize; r++) {
        for (let c = 0; c < gridSize; c++) {
          if (grid[r][c] === 0) emptyCells.push({r, c});
        }
      }
      if (emptyCells.length === 0) return false;
      const {r, c} = emptyCells[Math.floor(Math.random() * emptyCells.length)];
      grid[r][c] = Math.random() < 0.9 ? 2 : 4;
      return true;
    }

    // 更新当前分数,并在需要时更新最高分
    function updateScore(points) {
      score += points;
      scoreElement.textContent = score;
      if (score > bestScore) {
        bestScore = score;
        saveBestScore();
      }
      bestScoreElement.textContent = bestScore;
    }

    // 将最高分保存到 localStorage
    function saveBestScore() {
      localStorage.setItem('2048-best-score', bestScore);
    }

    // 从 localStorage 加载最高分
    function loadBestScore() {
      const saved = localStorage.getItem('2048-best-score');
      bestScore = saved ? parseInt(saved, 10) : 0;
      bestScoreElement.textContent = bestScore;
    }

    // 根据当前网格状态更新界面显示
    function updateUI() {
      clearTiles();
      for (let r = 0; r < gridSize; r++) {
        for (let c = 0; c < gridSize; c++) {
          if (grid[r][c] !== 0) {
            createTile(r, c, grid[r][c]);
          }
        }
      }
    }

    // 创建并定位一个数字方块元素
    function createTile(row, col, value) {
    const tile = document.createElement('div');
    tile.classList.add('tile', `tile-${value}`);
    // Use CSS grid positioning instead of absolute positioning
    tile.style.gridRowStart = row + 1;
    tile.style.gridColumnStart = col + 1;
    tile.textContent = value;
    gridElement.appendChild(tile);
    }

    // 处理键盘按键事件,实现上下左右移动
    function handleKeyDown(e) {
      if (gameOver || gameWon) return; // 游戏结束或胜利时忽略输入
      let moved = false;
      switch (e.key) {
        case 'ArrowUp':
          e.preventDefault();
          moved = moveUp();
          break;
        case 'ArrowDown':
          e.preventDefault();
          moved = moveDown();
          break;
        case 'ArrowLeft':
          e.preventDefault();
          moved = moveLeft();
          break;
        case 'ArrowRight':
          e.preventDefault();
          moved = moveRight();
          break;
        default:
          return;
      }
      if (moved) {
        addRandomTile();
        updateUI();
        if (checkWin()) {
          gameWon = true;
          showGameMessage('You Win!');
        } else if (checkGameOver()) {
          gameOver = true;
          showGameMessage('Game Over!');
        }
      }
    }

    // 向上移动数字方块并合并相同数字
    function moveUp() {
      let moved = false;
      for (let c = 0; c < gridSize; c++) {
        let col = [];
        for (let r = 0; r < gridSize; r++) col.push(grid[r][c]);
        let {newLine, points, changed} = slideAndCombine(col);
        if (changed) moved = true;
        for (let r = 0; r < gridSize; r++) grid[r][c] = newLine[r];
        if (points > 0) updateScore(points);
      }
      return moved;
    }

    // 向下移动数字方块并合并相同数字
    function moveDown() {
      let moved = false;
      for (let c = 0; c < gridSize; c++) {
        let col = [];
        for (let r = gridSize - 1; r >= 0; r--) col.push(grid[r][c]);
        let {newLine, points, changed} = slideAndCombine(col);
        if (changed) moved = true;
        for (let r = gridSize - 1, i = 0; r >= 0; r--, i++) grid[r][c] = newLine[i];
        if (points > 0) updateScore(points);
      }
      return moved;
    }

    // 向左移动数字方块并合并相同数字
    function moveLeft() {
      let moved = false;
      for (let r = 0; r < gridSize; r++) {
        let row = grid[r].slice();
        let {newLine, points, changed} = slideAndCombine(row);
        if (changed) moved = true;
        grid[r] = newLine;
        if (points > 0) updateScore(points);
      }
      return moved;
    }

    // 向右移动数字方块并合并相同数字
    function moveRight() {
      let moved = false;
      for (let r = 0; r < gridSize; r++) {
        let row = grid[r].slice().reverse();
        let {newLine, points, changed} = slideAndCombine(row);
        if (changed) moved = true;
        grid[r] = newLine.reverse();
        if (points > 0) updateScore(points);
      }
      return moved;
    }

    // 滑动并合并一行或一列数字方块,返回新的行/列,得分和是否发生变化
    function slideAndCombine(line) {
      let arr = line.filter(v => v !== 0);
      let points = 0;
      let changed = false;
      for (let i = 0; i < arr.length - 1; i++) {
        if (arr[i] === arr[i + 1]) {
          arr[i] *= 2;
          points += arr[i];
          arr.splice(i + 1, 1);
          arr.push(0);
          changed = true;
        }
      }
      while (arr.length < gridSize) arr.push(0);
      if (!changed) {
        for (let i = 0; i < gridSize; i++) {
          if (arr[i] !== line[i]) {
            changed = true;
            break;
          }
        }
      }
      return {newLine: arr, points, changed};
    }

    // 检查游戏是否结束(无空格且无可合并方块)
    function checkGameOver() {
      for (let r = 0; r < gridSize; r++) {
        for (let c = 0; c < gridSize; c++) {
          if (grid[r][c] === 0) return false;
          if (c < gridSize - 1 && grid[r][c] === grid[r][c + 1]) return false;
          if (r < gridSize - 1 && grid[r][c] === grid[r + 1][c]) return false;
        }
      }
      return true;
    }

    // 检查是否达到2048,游戏胜利
    function checkWin() {
      for (let r = 0; r < gridSize; r++) {
        for (let c = 0; c < gridSize; c++) {
          if (grid[r][c] === 2048) return true;
        }
      }
      return false;
    }

    // 显示游戏结束或胜利信息
    function showGameMessage(message) {
      gameMessage.textContent = message;
      gameMessage.style.display = 'flex';
    }

    restartButton.addEventListener('click', () => {
      init();
    });

    init();
  })();
</script>
</body>
</html>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值