趣味web在线小游戏-五子棋

Feng
7月23日发布

图片[1] - 趣味web在线小游戏-五子棋 - Feng的小屋

探索 HTML、CSS 和 JavaScript 实现的五子棋游戏

用 HTML、CSS 和 JavaScript 实现各种有趣的小游戏是一种既实用又有趣的学习方式。今天,我们就来深入探索一个用这三种技术打造的五子棋小游戏

整体项目概述

这个五子棋游戏是一个网页版应用,用户可以在浏览器中直接打开并进行对战。项目的整体结构清晰,由 HTML 构建页面结构,CSS 负责样式美化,JavaScript 实现游戏的核心逻辑。通过三者的协同工作,为玩家提供了一个流畅、有趣的游戏体验。

HTML:搭建游戏的骨架

HTML 文件为游戏搭建了基本的框架,包含了游戏的标题、控制按钮、棋盘区域、游戏信息展示和结果显示等部分。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>五子棋游戏</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <!-- 其他元信息和脚本引入 -->
</head>
<body>
  <div class="max-w-5xl w-full bg-white rounded-2xl shadow-xl overflow-hidden">
    <!-- 游戏标题 -->
    <header class="bg-primary text-white p-4 text-center">
      <h1 class="text-[clamp(1.8rem,4vw,2.5rem)] font-bold tracking-wide">五子棋</h1>
      <p class="text-amber-100 mt-1">落子连五获胜</p>
    </header>

    <!-- 游戏区域 -->
    <main class="p-6 md:p-8">
      <!-- 游戏控制区 -->
      <div class="flex flex-col lg:flex-row justify-between items-center mb-6 gap-4">
        <!-- 玩家信息和计时器 -->
        <div class="flex flex-col sm:flex-row items-center gap-3 w-full lg:w-auto">
          <div class="flex items-center gap-3">
            <div id="current-player" class="w-6 h-6 rounded-full piece-black piece-shadow"></div>
            <span class="text-lg font-medium">当前玩家: <span id="player-text" class="text-black font-bold">黑棋</span></span>
          </div>
          <div class="flex items-center gap-3 mt-2 sm:mt-0">
            <span class="text-lg font-medium">游戏时间: <span id="timer" class="text-primary font-bold">00:00</span></span>
          </div>
        </div>
        <!-- 控制按钮 -->
        <div class="flex gap-3 w-full lg:w-auto justify-center lg:justify-end">
          <button id="restart-btn" class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-lg transition-all flex items-center gap-2">
            <i class="fa fa-refresh"></i>
            <span>重新开始</span>
          </button>
          <button id="undo-btn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg transition-all flex items-center gap-2">
            <i class="fa fa-undo"></i>
            <span>悔棋</span>
          </button>
          <button id="hint-btn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg transition-all flex items-center gap-2">
            <i class="fa fa-lightbulb-o"></i>
            <span>提示</span>
          </button>
        </div>
      </div>

      <!-- 游戏主区域 -->
      <div class="flex flex-col lg:flex-row gap-6">
        <!-- 棋盘容器 -->
        <div class="relative mx-auto aspect-square max-w-md w-full bg-board rounded-lg board-shadow overflow-hidden grid-pattern" style="background-size: calc(100% / 14) calc(100% / 14);">
          <div id="board" class="absolute inset-0 cursor-pointer"></div>
          <!-- 棋盘标记点 -->
          <div class="absolute w-2 h-2 bg-dark rounded-full" style="top: calc(3/14 * 100%); left: calc(3/14 * 100%);"></div>
          <!-- 其他标记点 -->
        </div>
        <!-- 游戏信息和历史 -->
        <div class="w-full lg:w-80">
          <!-- 游戏统计 -->
          <div class="bg-gray-50 rounded-xl p-4 mb-4">
            <h3 class="text-lg font-semibold mb-3 text-gray-800">游戏统计</h3>
            <div class="grid grid-cols-2 gap-2">
              <div class="bg-white p-3 rounded-lg shadow-sm">
                <div class="text-sm text-gray-500">黑棋胜场</div>
                <div id="black-wins" class="text-xl font-bold text-black">0</div>
              </div>
              <!-- 其他统计信息 -->
            </div>
          </div>
          <!-- 走棋历史 -->
          <div class="bg-gray-50 rounded-xl p-4 h-64 overflow-hidden">
            <div class="flex justify-between items-center mb-3">
              <h3 class="text-lg font-semibold text-gray-800">走棋历史</h3>
              <button id="clear-history-btn" class="text-xs text-gray-500 hover:text-primary">
                <i class="fa fa-trash-o"></i> 清空
              </button>
            </div>
            <div id="move-history" class="overflow-y-auto h-48 text-sm space-y-1">
              <div class="text-gray-500 italic text-center">游戏尚未开始</div>
            </div>
          </div>
        </div>
      </div>

      <!-- 游戏结果 -->
      <div id="result" class="hidden mt-6 text-center p-4 rounded-lg bg-green-100 border border-green-300">
        <h2 id="result-text" class="text-xl font-bold text-green-800"></h2>
        <div class="mt-2 text-sm text-gray-600">
          用时: <span id="game-time">00:00</span> | 步数: <span id="move-count">0</span>
        </div>
        <button id="new-game-btn" class="mt-3 bg-primary hover:bg-primary/90 text-white px-6 py-2 rounded-lg transition-all">
          开始新游戏
        </button>
      </div>
    </main>

    <!-- 页脚 -->
    <footer class="bg-gray-100 text-gray-600 p-4 text-center text-sm">
      <p>© 2025 五子棋游戏 | 简约而不简单</p>
    </footer>
  </div>
  <script>
    // JavaScript 代码
  </script>
</body>
</html>

从这段代码中可以看出,HTML 页面使用了 Tailwind CSS 框架来快速实现样式布局,同时引入了 Font Awesome 图标库来增强视觉效果。页面结构层次分明,将游戏的各个部分清晰地展示出来,方便用户操作和查看信息。

CSS:美化游戏的外观

CSS 部分主要负责游戏界面的样式设计,通过自定义样式和 Tailwind CSS 的扩展,为游戏增添了丰富的视觉效果。

@layer utilities {
  .board-shadow {
    box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
  }
  .piece-shadow {
    box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
  }
  .piece-white {
    background: radial-gradient(circle at 35% 35%, #ffffff, #e0e0e0);
  }
  .piece-black {
    background: radial-gradient(circle at 35% 35%, #505050, #000000);
  }
  .grid-pattern {
    background-image: linear-gradient(#8B4513 1px, transparent 1px),
                      linear-gradient(90deg, #8B4513 1px, transparent 1px);
  }
  .animate-fadeIn {
    animation: fadeIn 0.5s ease-in-out;
  }
  .hover-indicator {
    position: absolute;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background-color: rgba(255, 215, 0, 0.7);
    opacity: 0;
    transition: opacity 0.2s ease;
  }
  .game-history-item {
    padding: 0.5rem;
    border-radius: 0.375rem;
    transition: background-color 0.2s;
  }
  .game-history-item:hover {
    background-color: rgba(0, 0, 0, 0.05);
  }
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

这里定义了棋盘的阴影效果、棋子的渐变背景、网格图案以及动画效果等。例如,piece-whitepiece-black 类分别为白棋和黑棋设置了不同的径向渐变背景,使其看起来更加立体。而 animate-fadeIn 动画则为游戏结果的显示添加了淡入效果,提升了用户体验。

JavaScript:实现游戏的核心逻辑

JavaScript 是这个五子棋游戏的核心,它负责处理游戏的各种交互和逻辑判断。

游戏状态管理

// 游戏常量
const BOARD_SIZE = 15;
const CELL_SIZE = 100 / (BOARD_SIZE - 1); // 百分比单位
const WINNING_LENGTH = 5;

// 游戏状态
let currentPlayer = 'black';
let gameBoard = Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(null));
let gameActive = true;
let moveHistory = [];
let gameStartTime = null;
let gameTimer = null;
let hintTimeout = null;
let hintPosition = null;

// 游戏统计
let stats = {
  blackWins: 0,
  whiteWins: 0,
  totalGames: 0,
  totalTime: 0
};

这里定义了游戏的常量和状态变量,包括棋盘大小、获胜条件、当前玩家、游戏棋盘状态、走棋历史等。同时,还使用 stats 对象来记录游戏的统计信息,如黑棋和白棋的胜场数、总对局数和总游戏时间。

初始化棋盘

function initializeBoard() {
  boardElement.innerHTML = '';
  
  // 创建交叉点
  for (let row = 0; row < BOARD_SIZE; row++) {
    for (let col = 0; col < BOARD_SIZE; col++) {
      const intersection = document.createElement('div');
      intersection.className = 'absolute w-8 h-8 rounded-full flex items-center justify-center transition-all duration-200 hover:bg-black/5 -translate-x-1/2 -translate-y-1/2';
      intersection.style.left = `${col * CELL_SIZE}%`;
      intersection.style.top = `${row * CELL_SIZE}%`;
      intersection.dataset.row = row;
      intersection.dataset.col = col;
      
      // 添加鼠标悬停指示器
      const hoverIndicator = document.createElement('div');
      hoverIndicator.className = 'hover-indicator';
      intersection.appendChild(hoverIndicator);
      
      // 添加点击事件
      intersection.addEventListener('click', () => handleIntersectionClick(row, col));
      
      // 添加鼠标进入事件
      intersection.addEventListener('mouseenter', () => {
        if (gameActive && gameBoard[row][col] === null) {
          hoverIndicator.style.opacity = '1';
        }
      });
      
      // 添加鼠标离开事件
      intersection.addEventListener('mouseleave', () => {
        hoverIndicator.style.opacity = '0';
      });
      
      boardElement.appendChild(intersection);
    }
  }
}

initializeBoard 函数用于创建棋盘的交叉点,并为每个交叉点添加鼠标悬停指示器和点击事件。当鼠标悬停在空闲的交叉点上时,指示器会显示出来,提示玩家可以落子。

处理落子事件

function handleIntersectionClick(row, col) {
  // 检查是否可以落子
  if (gameBoard[row][col] !== null || !gameActive) {
    return;
  }
  
  // 清除提示
  clearHint();
  
  // 记录历史
  moveHistory.push({row, col, player: currentPlayer});
  
  // 更新走棋历史显示
  addToMoveHistory(row, col, currentPlayer);
  
  // 落子
  placePiece(row, col, currentPlayer);
  
  // 检查是否获胜
  if (checkWin(row, col, currentPlayer)) {
    gameActive = false;
    stopTimer();
    
    // 更新统计
    if (currentPlayer === 'black') {
      stats.blackWins++;
    } else {
      stats.whiteWins++;
    }
    stats.totalGames++;
    stats.totalTime += getElapsedTime();
    saveStats();
    updateStatsDisplay();
    
    showResult(`${currentPlayer === 'black' ? '黑棋' : '白棋'}获胜!`);
    return;
  }
  
  // 检查是否平局
  if (checkDraw()) {
    gameActive = false;
    stopTimer();
    showResult('平局!');
    return;
  }
  
  // 切换玩家
  currentPlayer = currentPlayer === 'black' ? 'white' : 'black';
  updatePlayerIndicator();
}

handleIntersectionClick 函数处理玩家点击交叉点的事件。它会先检查该位置是否可以落子,然后记录走棋历史,放置棋子,并检查是否获胜或平局。如果游戏结束,会更新统计信息并显示结果;否则,切换玩家继续游戏。

检查获胜和平局

function checkWin(row, col, player) {
  const directions = [
    [0, 1],  // 水平
    [1, 0],  // 垂直
    [1, 1],  // 对角线
    [1, -1]  // 反对角线
  ];
  
  for (const [dx, dy] of directions) {
    let count = 1;  // 当前位置已经有一个棋子
    
    // 正向检查
    for (let i = 1; i < WINNING_LENGTH; i++) {
      const newRow = row + i * dx;
      const newCol = col + i * dy;
      
      if (
        newRow >= 0 && newRow < BOARD_SIZE &&
        newCol >= 0 && newCol < BOARD_SIZE &&
        gameBoard[newRow][newCol] === player
      ) {
        count++;
      } else {
        break;
      }
    }
    
    // 反向检查
    for (let i = 1; i < WINNING_LENGTH; i++) {
      const newRow = row - i * dx;
      const newCol = col - i * dy;
      
      if (
        newRow >= 0 && newRow < BOARD_SIZE &&
        newCol >= 0 && newCol < BOARD_SIZE &&
        gameBoard[newRow][newCol] === player
      ) {
        count++;
      } else {
        break;
      }
    }
    
    // 检查是否连成五子
    if (count >= WINNING_LENGTH) {
      // 高亮显示获胜的棋子
      highlightWinningPieces(row, col, dx, dy, count);
      return true;
    }
  }
  
  return false;
}

function checkDraw() {
  for (let row = 0; row < BOARD_SIZE; row++) {
    for (let col = 0; col < BOARD_SIZE; col++) {
      if (gameBoard[row][col] === null) {
        return false;  // 还有空位,不是平局
      }
    }
  }
  return true;  // 棋盘已满,平局
}

checkWin 函数通过检查四个方向(水平、垂直、对角线和反对角线)上是否有连续五个相同颜色的棋子来判断是否获胜。如果获胜,会调用 highlightWinningPieces 函数高亮显示获胜的棋子。checkDraw 函数则遍历整个棋盘,检查是否还有空位,如果没有则判定为平局。

总结

通过这个五子棋游戏项目,我们可以看到 HTML、CSS 和 JavaScript 是如何协同工作来创建一个完整的 Web 应用的。HTML 提供了页面的结构,CSS 美化了界面,而 JavaScript 实现了游戏的核心逻辑和交互功能。这个项目不仅适合初学者学习 Web 开发的基础知识,还可以作为一个基础,进一步扩展和优化,例如添加 AI 对手、在线对战等功能。

© 版权声明
THE END
喜欢就支持一下吧
点赞 3 分享 赞赏
评论 抢沙发
OωO
取消
SSL