增强版五子棋 - 双人/电脑对战edit icon

作者:
邓朝元
Fork(复制)
下载
嵌入
BUG反馈
index.html
现在支持上传本地图片了!
index.html
            
            <!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <title>🎮 增强版五子棋 - 双人/电脑对战</title>
  <style>
    body {
      background: #f4f4f4;
      font-family: 'Microsoft Yahei', sans-serif;
      text-align: center;
      margin-top: 30px;
    }

    h1 {
      color: #333;
    }

    canvas {
      border: 2px solid #333;
      background-color: #fdd;
      display: block;
      margin: 20px auto;
      box-shadow: 0 0 15px rgba(0,0,0,0.2);
    }

    #status {
      font-size: 18px;
      margin-top: 10px;
      color: green;
    }

    button {
      padding: 8px 16px;
      font-size: 16px;
      cursor: pointer;
      margin: 5px;
    }

    #score {
      margin-top: 15px;
      font-size: 16px;
    }

    select {
      font-size: 16px;
      padding: 5px;
    }
  </style>
</head>
<body>

<h1>🎮 增强版五子棋 - 玩家 vs 玩家 / 玩家 vs AI</h1>

<select id="gameMode">
  <option value="local">本地双人对战</option>
  <option value="ai">挑战电脑(黑先)</option>
</select>
<br/>
<canvas id="chessBoard" width="600" height="600"></canvas>
<div id="status">轮到黑棋落子</div>
<button onclick="restartGame()">重新开始</button>

<!-- 音效 -->
<audio id="placeSound">
  <source src="https://cdn.pixabay.com/audio/2022/03/15/audio_4b7f9a1ba6.mp3" type="audio/mp3">
</audio>
<audio id="winSound">
  <source src="https://cdn.pixabay.com/audio/2022/01/17/audio_c46dc4b0ac.mp3" type="audio/mp3">
</audio>

<div id="score">🎮 当前战绩:<br/>你(黑棋):0 胜 &nbsp;&nbsp; 电脑(白棋):0 胜</div>

<script>
  const canvas = document.getElementById("chessBoard");
  const ctx = canvas.getContext("2d");
  const statusDiv = document.getElementById("status");
  const scoreDiv = document.getElementById("score");

  const boardSize = 15;
  const cellSize = canvas.width / (boardSize + 1);
  let chessBoard = [];
  let gameOver = false;
  let currentPlayer = "black"; // 黑棋先手
  let gameMode = "local"; // 默认本地双人

  // 初始化胜负记录
  function initScores() {
    if (!localStorage.getItem("blackWins")) localStorage.setItem("blackWins", "0");
    if (!localStorage.getItem("whiteWins")) localStorage.setItem("whiteWins", "0");
    updateScoreDisplay();
  }

  function updateScoreDisplay() {
    const blackWins = localStorage.getItem("blackWins");
    const whiteWins = localStorage.getItem("whiteWins");
    scoreDiv.innerHTML = `🎮 当前战绩:<br/>你(黑棋):${blackWins} 胜 &nbsp;&nbsp; 电脑(白棋):${whiteWins} 胜`;
  }

  function playPlaceSound() {
    document.getElementById("placeSound").play();
  }

  function playWinSound() {
    document.getElementById("winSound").play();
  }

  function updateStatus(text) {
    statusDiv.textContent = text;
  }

  function init() {
    for (let i = 0; i < boardSize; i++) {
      chessBoard[i] = [];
      for (let j = 0; j < boardSize; j++) {
        chessBoard[i][j] = "";
      }
    }
    drawBoard();
    gameOver = false;
    currentPlayer = "black";
    gameMode = document.getElementById("gameMode").value;
    updateStatus(gameMode === "ai" ? "轮到你(黑棋)落子" : "轮到黑棋落子");
    if (gameMode === "ai" && currentPlayer === "white") aiMove();
  }

  function restartGame() {
    init();
  }

  function drawBoard() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 绘制网格线
    ctx.strokeStyle = "#333";
    for (let i = 1; i <= boardSize; i++) {
      ctx.beginPath();
      ctx.moveTo(cellSize, i * cellSize);
      ctx.lineTo(boardSize * cellSize, i * cellSize);
      ctx.stroke();

      ctx.beginPath();
      ctx.moveTo(i * cellSize, cellSize);
      ctx.lineTo(i * cellSize, boardSize * cellSize);
      ctx.stroke();
    }

    // 绘制星位
    ctx.fillStyle = "#333";
    const stars = [
      [3, 3], [11, 3],
      [3, 11], [11, 11],
      [7, 7]
    ];
    stars.forEach(([x, y]) => {
      ctx.beginPath();
      ctx.arc((x + 1) * cellSize, (y + 1) * cellSize, 5, 0, Math.PI * 2);
      ctx.fill();
    });

    // 绘制棋子
    for (let i = 0; i < boardSize; i++) {
      for (let j = 0; j < boardSize; j++) {
        if (chessBoard[i][j] === "black") {
          drawChess(i, j, "black");
        } else if (chessBoard[i][j] === "white") {
          drawChess(i, j, "white");
        }
      }
    }
  }

  function drawChess(x, y, color) {
    ctx.beginPath();
    ctx.arc((x + 1) * cellSize, (y + 1) * cellSize, cellSize / 2 - 2, 0, Math.PI * 2);
    ctx.fillStyle = color;
    ctx.fill();
    if (color === "white") {
      ctx.strokeStyle = "#999";
      ctx.stroke();
    }
  }

  canvas.addEventListener("click", function (e) {
    if (gameOver) return;

    const rect = canvas.getBoundingClientRect();
    const x = Math.round((e.clientX - rect.left) / cellSize) - 1;
    const y = Math.round((e.clientY - rect.top) / cellSize) - 1;

    if (
      x >= 0 && x < boardSize &&
      y >= 0 && y < boardSize &&
      chessBoard[x][y] === ""
    ) {
      chessBoard[x][y] = currentPlayer;
      drawBoard();
      playPlaceSound();

      if (checkWin(x, y)) {
        const winner = currentPlayer === "black" ? "黑棋" : "白棋";
        updateStatus(`${winner} 获胜!🎉`);
        playWinSound();
        gameOver = true;

        // 更新胜负记录
        if (gameMode === "ai") {
          let key = currentPlayer === "black" ? "blackWins" : "whiteWins";
          let count = parseInt(localStorage.getItem(key));
          localStorage.setItem(key, count + 1);
          updateScoreDisplay();
        }

        return;
      }

      // 切换玩家
      currentPlayer = currentPlayer === "black" ? "white" : "black";

      if (gameMode === "ai" && currentPlayer === "white") {
        updateStatus("电脑正在思考...");
        setTimeout(aiMove, 600);
      } else {
        updateStatus(`轮到${currentPlayer === "black" ? "黑棋" : "白棋"}落子`);
      }
    }
  });

  function aiMove() {
    if (gameOver) return;

    let bestScore = -Infinity;
    let bestMove = null;

    for (let i = 0; i < boardSize; i++) {
      for (let j = 0; j < boardSize; j++) {
        if (chessBoard[i][j] === "") {
          let score = evaluate(i, j, "white");
          if (score > bestScore) {
            bestScore = score;
            bestMove = { x: i, y: j };
          }
        }
      }
    }

    if (bestMove) {
      chessBoard[bestMove.x][bestMove.y] = "white";
      drawBoard();
      playPlaceSound();

      if (checkWin(bestMove.x, bestMove.y)) {
        updateStatus("电脑(白棋)获胜了 😢");
        playWinSound();
        gameOver = true;

        let key = "whiteWins";
        let count = parseInt(localStorage.getItem(key));
        localStorage.setItem(key, count + 1);
        updateScoreDisplay();
        return;
      }

      currentPlayer = "black";
      updateStatus("轮到你(黑棋)落子");
    }
  }

  // 棋型权重
  const patternScores = {
    live5: 100000,
    live4: 10000,
    doubleLive3: 5000,
    live3: 1000,
    live2: 100,
    block4: 800,
    block3: 50,
    dead: 0
  };

  function evaluate(x, y, player) {
    let score = 0;
    const opponent = player === "white" ? "black" : "white";

    const directions = [
      [[1, 0], [-1, 0]],
      [[0, 1], [0, -1]],
      [[1, 1], [-1, -1]],
      [[1, -1], [-1, 1]]
    ];

    for (const dir of directions) {
      let line = "";
      for (let step = -4; step <= 4; step++) {
        let tx = x + dir[0][0] * step;
        let ty = y + dir[0][1] * step;
        if (tx >= 0 && tx < boardSize && ty >= 0 && ty < boardSize) {
          line += chessBoard[tx][ty] || "_";
        } else {
          line += "#";
        }
      }

      line = line.replace(/_/i, "o"); // 替换当前点为中心

      if (line.includes("ooooo")) return patternScores.live5;

      if (/(_|^)oooo_/.test(line) || /_oooo($|_)/.test(line)) score += patternScores.live4;
      else if (/oo_o_o|o_oo_o|o_o_oo|_ooo_/.test(line)) score += patternScores.live3;
      else if (/oo_oo/.test(line)) score += patternScores.block4;
      else if (/oo_/.test(line)) score += patternScores.live2;
    }

    return score;
  }

  function checkWin(x, y) {
    const directions = [
      [[1, 0], [-1, 0]],
      [[0, 1], [0, -1]],
      [[1, 1], [-1, -1]],
      [[1, -1], [-1, 1]]
    ];

    const player = chessBoard[x][y];

    for (const dir of directions) {
      let count = 1;
      for (const d of dir) {
        let tx = x + d[0];
        let ty = y + d[1];
        while (
          tx >= 0 && tx < boardSize &&
          ty >= 0 && ty < boardSize &&
          chessBoard[tx][ty] === player
        ) {
          count++;
          tx += d[0];
          ty += d[1];
        }
      }
      if (count >= 5) return true;
    }

    return false;
  }

  // 页面加载初始化
  document.getElementById("gameMode").addEventListener("change", init);
  initScores();
  init();
</script>

</body>
</html>
        
编辑器加载中
预览
控制台