实现经典游戏“五子棋”详解

一、需要实现的功能

五子棋是一种策略棋型游戏,它是在棋盘上排列成连续的五个棋子(横向、纵向或对角线),即赢得比赛。

二、需求功能细化

1、五子棋棋盘

棋盘是一个15x15的方格网格,共有225个点。玩家可以在这些点上放置自己的棋子。

2、游戏目标

玩家需要先于对手,在棋盘上形成连续的五个棋子(横向、纵向或对角线),即获得胜利。

3、游戏流程

玩家与电脑(或其他玩家)轮流进行下棋,玩家先手。玩家可以通过点击棋盘上的点来放置自己的棋子。电脑(或其他玩家)也会在空闲的点上进行下棋操作。每一步棋之后,都会检查是否有玩家获胜或者游戏结束。

4、获胜条件

如果玩家(或电脑)在横向、纵向或对角线上形成连续的五个棋子(即五子连珠),即可获得胜利。如果棋盘填满且没有玩家获胜,则宣布游戏为平局。

5、再来一局

在游戏结束后,玩家可以选择重新开始一局,棋盘会被清空,双方重新开始对决。

三、H5页面设计

我们创建一个命名为 gobang.html 的文件通过编辑器(我用的EditPlus)打开。使用<!DOCTYPE>声明HTML文档类型,确保浏览器以正确的方式渲染网页内容。同时,设置UTF-8编码,以确保浏览器能够正确地解析和显示中文字符。

以下是五子棋游戏的DOM结构了。

<body>
  <h1>五子棋游戏</h1>
  <div class="board"></div>
  <div class="message"></div>
  <button class="reset-button">重新开始</button>
</body>

<h1>五子棋游戏</h1>:标题,使用h1标签,用于显示游戏的名称或标题,让玩家知道这是一个拼图游戏。

<div class="board">:一个具有board类的<div>元素,用于承载五子棋的棋盘。

<div class="message">:一个具有message类的<div>元素,用于显示游戏中的消息或提示。

<button class="reset-button">重新开始</button>:一个具有reset-button类的<button>元素,用于重新开始游戏。

以上是五子棋游戏的基本页面结构,其中棋盘、消息和重新开始按钮是游戏的主要组成部分。

四、CSS样式设置

上面html已经准备好了,下面我们简单的来配置一下样式吧。

<style>
 .board {
  display: grid;
  grid-template-columns: repeat(15, 40px);
  grid-template-rows: repeat(15, 40px);
  gap: 1px;
  background-color: #aaa;
  margin-bottom: 20px;
 }
 .cell {
  background-color: #eee;
  border: 1px solid #ccc;
 }
 .cell:hover {
  background-color: #ddd;
 }
 .piece {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  margin: 2px;
 }
 .black-piece {
  background-color: #000;
 }
 .white-piece {
  background-color: #fff;
 }
 .message {
  font-weight: bold;
 }
 .reset-button {
  margin-top: 10px;
 }
</style>

.board:棋盘容器的样式规则,使用网格布局,设置背景颜色和间距。

.cell:每个棋盘格子的样式规则,设置背景颜色和边框。

.cell:hover:鼠标悬停在格子上时的样式规则,设置背景颜色。

.piece:棋子的样式规则,设置宽度、高度、边框半径和间距。

.black-piece:黑色棋子的样式规则,设置背景颜色。

.white-piece:白色棋子的样式规则,设置背景颜色。

.message:消息文本的样式规则,设置字体粗细。

.reset-button:重新开始按钮的样式规则,设置上方间距。

以上样式规则通过类名的方式应用到HTML元素上,从而实现了五子棋游戏界面的样式和外观。

五、代码逻辑,游戏实现

<script>
  document.addEventListener('DOMContentLoaded', () => {
    const board = document.querySelector('.board');
    const message = document.querySelector('.message');
    const resetButton = document.querySelector('.reset-button');
    const PlayerType = {
      PLAYER: 'player',
      COMPUTER: 'computer',
    };
    let currentPlayer = PlayerType.PLAYER;
    let isGameOver = false;
    let boardState = [];
    function createBoard() {
      board.innerHTML = '';
      for (let row = 0; row < 15; row++) {
        boardState[row] = [];
        for (let col = 0; col < 15; col++) {
          const cell = document.createElement('div');
          cell.classList.add('cell');
          cell.dataset.row = row;
          cell.dataset.col = col;
          cell.addEventListener('click', handleCellClick);
          board.appendChild(cell);
          boardState[row][col] = '';
        }
      }
    }
    function handleCellClick(event) {
      if (isGameOver) {
        return;
      }
      const cell = event.target;
      const row = parseInt(cell.dataset.row);
      const col = parseInt(cell.dataset.col);
      if (boardState[row][col] !== '') {
        return;
      }
      placePiece(row, col, currentPlayer);
      if (checkWin(row, col)) {
        endGame(currentPlayer);
        return;
      }
      switchPlayer();
      if (currentPlayer === PlayerType.COMPUTER && !isGameOver) {
        makeComputerMove();
      }
    }
    function placePiece(row, col, player) {
      const piece = document.createElement('div');
      piece.classList.add('piece');
      piece.classList.add(player === PlayerType.PLAYER ? 'black-piece' : 'white-piece');
      const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
      cell.appendChild(piece);
      boardState[row][col] = player;
    }
    function switchPlayer() {
      currentPlayer = currentPlayer === PlayerType.PLAYER ? PlayerType.COMPUTER : PlayerType.PLAYER;
    }
    function checkWin(row, col) {
      const directions = [
        [1, 0],
        [0, 1],
        [1, 1],
        [1, -1]
      ];
      const player = boardState[row][col];
      for (const direction of directions) {
        const [dx, dy] = direction;
        let count = 1;
        // Check for consecutive pieces in both directions
        count += checkDirection(row, col, dx, dy, player);
        count += checkDirection(row, col, -dx, -dy, player);
        if (count >= 5) {
          return true;
        }
      }
      return false;
    }
    function checkDirection(row, col, dx, dy, player) {
      let count = 0;
      let newRow = row + dx;
      let newCol = col + dy;
      while (isValidPosition(newRow, newCol) && boardState[newRow][newCol] === player) {
        count++;
        newRow += dx;
        newCol += dy;
      }
      return count;
    }
    function isValidPosition(row, col) {
      return row >= 0 && row < 15 && col >= 0 && col < 15;
    }
    function makeComputerMove() {
      // Generate a random move for the computer
      let row, col;
      do {
        row = Math.floor(Math.random() * 15);
        col = Math.floor(Math.random() * 15);
      } while (boardState[row][col] !== '');
      placePiece(row, col, currentPlayer);
      if (checkWin(row, col)) {
        endGame(currentPlayer);
        return;
      }
      switchPlayer();
    }
    function endGame(winner) {
      isGameOver = true;
      message.textContent = winner === PlayerType.PLAYER ? 'You win!' : 'Computer wins!';
    }
    function resetGame() {
      currentPlayer = PlayerType.PLAYER;
      isGameOver = false;
      boardState = [];
      message.textContent = '';
      createBoard();
    }
    resetButton.addEventListener('click', resetGame);
    createBoard();
  });
</script>

现在解释一下关键部分的代码。

(1)createBoard():创建棋盘函数,用于动态生成一个15x15的棋盘,并为每个格子添加事件监听器。

(2)handleCellClick(event):处理格子点击事件函数,根据当前游戏状态和点击的格子位置判断是否可以下子,并进行相应的处理。

(3)placePiece(row, col, player):下子函数,根据给定的行、列和玩家类型,在指定的格子位置放置对应颜色的棋子,并更新棋盘状态。

(4)switchPlayer():切换玩家函数,用于在玩家下子后切换到另一个玩家。

(5)checkWin(row, col):检查胜利函数,根据当前下子的位置检查是否达到五子连珠的胜利条件。

(6)checkDirection(row, col, dx, dy, player):检查方向函数,根据指定的方向,在当前位置的左右两侧检查是否有连续的同色棋子。

(7)isValidPosition(row, col):验证位置有效性函数,用于判断指定的行和列是否在合法的范围内。

(8)makeComputerMove():电脑下子函数,随机生成电脑下子的位置,并执行下子操作。

(9)endGame(winner):结束游戏函数,当游戏达到胜利条件时,设置游戏结束标志,并显示胜利信息。

(10)resetGame():重新开始游戏函数,用于重置游戏状态,清空棋盘,重新创建棋盘。

(11)resetButton.addEventListener('click', resetGame):绑定重新开始按钮的点击事件监听器。

(12)createBoard():在页面加载完成后,执行创建棋盘的函数,初始化游戏。

每个方法都承担着不同的功能,这些代码一起实现了一个基于DOM操作的五子棋游戏。

六、完整源代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>五子棋游戏</title>
  <style>
    .board {
      display: grid;
      grid-template-columns: repeat(15, 40px);
      grid-template-rows: repeat(15, 40px);
      gap: 1px;
      background-color: #aaa;
      margin-bottom: 20px;
    }
    .cell {
      background-color: #eee;
      border: 1px solid #ccc;
    }
    .cell:hover {
      background-color: #ddd;
    }
    .piece {
      width: 36px;
      height: 36px;
      border-radius: 50%;
      margin: 2px;
    }
    .black-piece {
      background-color: #000;
    }
    .white-piece {
      background-color: #fff;
    }
    .message {
      font-weight: bold;
    }
    .reset-button {
      margin-top: 10px;
    }
  </style>
</head>
<body>
  <div class="board"></div>
  <div class="message"></div>
  <button class="reset-button">重新开始</button>
</body>
<script>
  document.addEventListener('DOMContentLoaded', () => {
    const board = document.querySelector('.board');
    const message = document.querySelector('.message');
    const resetButton = document.querySelector('.reset-button');
    const PlayerType = {
      PLAYER: 'player',
      COMPUTER: 'computer',
    };
    let currentPlayer = PlayerType.PLAYER;
    let isGameOver = false;
    let boardState = [];
    function createBoard() {
      board.innerHTML = '';
      for (let row = 0; row < 15; row++) {
        boardState[row] = [];
        for (let col = 0; col < 15; col++) {
          const cell = document.createElement('div');
          cell.classList.add('cell');
          cell.dataset.row = row;
          cell.dataset.col = col;
          cell.addEventListener('click', handleCellClick);
          board.appendChild(cell);
          boardState[row][col] = '';
        }
      }
    }
    function handleCellClick(event) {
      if (isGameOver) {
        return;
      }
      const cell = event.target;
      const row = parseInt(cell.dataset.row);
      const col = parseInt(cell.dataset.col);
      if (boardState[row][col] !== '') {
        return;
      }
      placePiece(row, col, currentPlayer);
      if (checkWin(row, col)) {
        endGame(currentPlayer);
        return;
      }
      switchPlayer();
      if (currentPlayer === PlayerType.COMPUTER && !isGameOver) {
        makeComputerMove();
      }
    }
    function placePiece(row, col, player) {
      const piece = document.createElement('div');
      piece.classList.add('piece');
      piece.classList.add(player === PlayerType.PLAYER ? 'black-piece' : 'white-piece');
      const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
      cell.appendChild(piece);
      boardState[row][col] = player;
    }
    function switchPlayer() {
      currentPlayer = currentPlayer === PlayerType.PLAYER ? PlayerType.COMPUTER : PlayerType.PLAYER;
    }
    function checkWin(row, col) {
      const directions = [
        [1, 0],
        [0, 1],
        [1, 1],
        [1, -1]
      ];
      const player = boardState[row][col];
      for (const direction of directions) {
        const [dx, dy] = direction;
        let count = 1;
        // Check for consecutive pieces in both directions
        count += checkDirection(row, col, dx, dy, player);
        count += checkDirection(row, col, -dx, -dy, player);
        if (count >= 5) {
          return true;
        }
      }
      return false;
    }
    function checkDirection(row, col, dx, dy, player) {
      let count = 0;
      let newRow = row + dx;
      let newCol = col + dy;
      while (isValidPosition(newRow, newCol) && boardState[newRow][newCol] === player) {
        count++;
        newRow += dx;
        newCol += dy;
      }
      return count;
    }
    function isValidPosition(row, col) {
      return row >= 0 && row < 15 && col >= 0 && col < 15;
    }
    function makeComputerMove() {
      // Generate a random move for the computer
      let row, col;
      do {
        row = Math.floor(Math.random() * 15);
        col = Math.floor(Math.random() * 15);
      } while (boardState[row][col] !== '');
      placePiece(row, col, currentPlayer);
      if (checkWin(row, col)) {
        endGame(currentPlayer);
        return;
      }
      switchPlayer();
    }
    function endGame(winner) {
      isGameOver = true;
      message.textContent = winner === PlayerType.PLAYER ? 'You win!' : 'Computer wins!';
    }
    function resetGame() {
      currentPlayer = PlayerType.PLAYER;
      isGameOver = false;
      boardState = [];
      message.textContent = '';
      createBoard();
    }
    resetButton.addEventListener('click', resetGame);
    createBoard();
  });
</script>
</html>

以上是五子棋游戏的完整代码,可以直接复制过去,或者点击五子棋游戏demo链接访问。

版权声明:本文为老张的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://www.webppp.com/view/gobang.html