edit icon

创建者:
邓朝元
Fork(复制)
下载
嵌入
BUG反馈
index.html
md
README.md
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;
      transition: all 0.3s ease;
    }

    h1 {
      color: #333;
      transition: color 0.3s ease;
    }

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

    #status {
      font-size: 18px;
      margin-top: 10px;
      color: green;
      min-height: 27px;
      transition: color 0.3s ease;
    }

    button {
      padding: 8px 16px;
      font-size: 16px;
      cursor: pointer;
      margin: 5px;
      border-radius: 5px;
      border: 1px solid #999;
      background: #fff;
      transition: all 0.2s ease;
    }

    button:hover:not(:disabled) {
      background: #e8e8e8;
      transform: translateY(-2px);
    }

    button:disabled {
      opacity: 0.5;
      cursor: not-allowed;
    }

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

    select {
      font-size: 16px;
      padding: 5px;
      border-radius: 5px;
      border: 1px solid #999;
    }

    .controls {
      margin: 15px 0;
    }

    .theme-preview {
      display: inline-block;
      width: 20px;
      height: 20px;
      border-radius: 50%;
      margin-right: 8px;
      vertical-align: middle;
    }

    .footer-info {
      margin-top: 20px;
      font-size: 14px;
      color: #666;
      transition: color 0.3s ease;
    }
  </style>
</head>

<body>

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

  <div class="controls">
    <label>游戏模式:
      <select id="gameMode">
        <option value="local">本地双人对战</option>
        <option value="ai">挑战电脑(黑先)</option>
      </select>
    </label>
    <br />
    <label style="margin-top:10px; display:inline-block;">主题风格:
      <select id="themeSelect">
        <option value="classic">🟤 经典木纹</option>
        <option value="dark">🌙 暗夜模式</option>
        <option value="modern">💎 现代蓝</option>
        <option value="nature">🌿 自然绿</option>
        <option value="sunset">🌅 夕阳橙</option>
        <option value="purple">🌸 梦幻紫</option>
      </select>
    </label>
  </div>

  <canvas id="chessBoard" width="600" height="600"></canvas>
  <div id="status">轮到黑棋落子</div>

  <div class="controls">
    <button id="undoBtn" onclick="undoMove()" disabled>⬅️ 撤销上一步</button>
    <button onclick="restartGame()">🔄 重新开始</button>
    <button onclick="resetScores()">🗑️ 清除战绩</button>
  </div>

  <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>

  <div class="footer-info">
    💡 提示:在AI模式下撤销会同时撤回电脑的应对棋步
  </div>

  <script>
    // ========== 主题定义 ==========
    const themes = {
      classic: {
        name: "经典木纹",
        background: "#f4f4f4",
        canvasBg: "#fdd",
        canvasBorder: "#333",
        boardLines: "#333",
        textColor: "#333",
        statusColor: "green",
        blackPiece: "#1a1a1a",
        blackGradient: ["#333", "#000"],
        whitePiece: "#fff",
        whiteGradient: ["#fff", "#ddd"],
        whiteBorder: "#999",
        starColor: "#333",
        buttonBg: "#fff",
        buttonBorder: "#999",
        shadow: "0 0 15px rgba(0, 0, 0, 0.2)"
      },
      dark: {
        name: "暗夜模式",
        background: "#1a1a2e",
        canvasBg: "#2d2d44",
        canvasBorder: "#4a4a6a",
        boardLines: "#7a7a9a",
        textColor: "#e0e0e0",
        statusColor: "#4ade80",
        blackPiece: "#0a0a14",
        blackGradient: ["#222", "#000"],
        whitePiece: "#e8e8e8",
        whiteGradient: ["#f0f0f0", "#c0c0c0"],
        whiteBorder: "#888",
        starColor: "#7a7a9a",
        buttonBg: "#2d2d44",
        buttonBorder: "#4a4a6a",
        shadow: "0 0 20px rgba(100, 100, 255, 0.3)"
      },
      modern: {
        name: "现代蓝",
        background: "#e8f4f8",
        canvasBg: "#b8d8e8",
        canvasBorder: "#2c3e50",
        boardLines: "#2c3e50",
        textColor: "#2c3e50",
        statusColor: "#2980b9",
        blackPiece: "#2c3e50",
        blackGradient: ["#34495e", "#1a252f"],
        whitePiece: "#ecf0f1",
        whiteGradient: ["#fff", "#bdc3c7"],
        whiteBorder: "#95a5a6",
        starColor: "#2c3e50",
        buttonBg: "#ecf0f1",
        buttonBorder: "#bdc3c7",
        shadow: "0 0 15px rgba(44, 62, 80, 0.2)"
      },
      nature: {
        name: "自然绿",
        background: "#e8f5e9",
        canvasBg: "#c8e6c9",
        canvasBorder: "#1b5e20",
        boardLines: "#2e7d32",
        textColor: "#1b5e20",
        statusColor: "#2e7d32",
        blackPiece: "#212121",
        blackGradient: ["#333", "#111"],
        whitePiece: "#f5f5f5",
        whiteGradient: ["#fff", "#ddd"],
        whiteBorder: "#757575",
        starColor: "#1b5e20",
        buttonBg: "#e8f5e9",
        buttonBorder: "#81c784",
        shadow: "0 0 15px rgba(27, 94, 32, 0.2)"
      },
      sunset: {
        name: "夕阳橙",
        background: "#fff3e0",
        canvasBg: "#ffe0b2",
        canvasBorder: "#e65100",
        boardLines: "#bf360c",
        textColor: "#bf360c",
        statusColor: "#e65100",
        blackPiece: "#3e2723",
        blackGradient: ["#4e342e", "#1b0000"],
        whitePiece: "#fff8e1",
        whiteGradient: ["#fff", "#ffe082"],
        whiteBorder: "#f9a825",
        starColor: "#bf360c",
        buttonBg: "#fff3e0",
        buttonBorder: "#ff8f00",
        shadow: "0 0 15px rgba(230, 81, 0, 0.2)"
      },
      purple: {
        name: "梦幻紫",
        background: "#f3e5f5",
        canvasBg: "#e1bee7",
        canvasBorder: "#4a148c",
        boardLines: "#6a1b9a",
        textColor: "#4a148c",
        statusColor: "#7b1fa2",
        blackPiece: "#1a0033",
        blackGradient: ["#2d0052", "#0a0015"],
        whitePiece: "#fce4ec",
        whiteGradient: ["#fff", "#f8bbd0"],
        whiteBorder: "#ad1457",
        starColor: "#6a1b9a",
        buttonBg: "#f3e5f5",
        buttonBorder: "#ab47bc",
        shadow: "0 0 15px rgba(106, 27, 154, 0.2)"
      }
    };

    // ========== 全局变量 ==========
    const canvas = document.getElementById("chessBoard");
    const ctx = canvas.getContext("2d");
    const statusDiv = document.getElementById("status");
    const scoreDiv = document.getElementById("score");
    const undoBtn = document.getElementById("undoBtn");
    const boardSize = 15;
    const cellSize = canvas.width / (boardSize + 1);
    let chessBoard = [];
    let gameOver = false;
    let currentPlayer = "black";
    let gameMode = "local";
    let moveHistory = []; // 记录每一步棋
    let currentTheme = themes.classic;
    let aiThinking = false; // 防止AI思考时重复操作
    let aiTimeout = null; // 记录AI延迟

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

    function resetScores() {
      if (confirm("确定要清除所有战绩吗?")) {
        localStorage.setItem("blackWins", "0");
        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() {
      const sound = document.getElementById("placeSound");
      sound.currentTime = 0;
      sound.play().catch(() => {});
    }

    function playWinSound() {
      const sound = document.getElementById("winSound");
      sound.currentTime = 0;
      sound.play().catch(() => {});
    }

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

    function updateUndoButton() {
      undoBtn.disabled = gameOver || moveHistory.length === 0 || aiThinking;
    }

    // ========== 主题切换 ==========
    function applyTheme(themeName) {
      currentTheme = themes[themeName] || themes.classic;

      document.body.style.background = currentTheme.background;
      document.body.style.color = currentTheme.textColor;
      document.querySelector("h1").style.color = currentTheme.textColor;
      document.querySelector(".footer-info").style.color = currentTheme.textColor;
      statusDiv.style.color = currentTheme.statusColor;

      canvas.style.backgroundColor = currentTheme.canvasBg;
      canvas.style.borderColor = currentTheme.canvasBorder;
      canvas.style.boxShadow = currentTheme.shadow;

      // 按钮样式
      const buttons = document.querySelectorAll("button");
      buttons.forEach(btn => {
        btn.style.background = currentTheme.buttonBg;
        btn.style.borderColor = currentTheme.buttonBorder;
        btn.style.color = currentTheme.textColor;
      });

      const selects = document.querySelectorAll("select");
      selects.forEach(sel => {
        sel.style.background = currentTheme.buttonBg;
        sel.style.borderColor = currentTheme.buttonBorder;
        sel.style.color = currentTheme.textColor;
      });

      drawBoard();
    }

    document.getElementById("themeSelect").addEventListener("change", function () {
      applyTheme(this.value);
    });

    // ========== 游戏核心 ==========
    function init() {
      for (let i = 0; i < boardSize; i++) {
        chessBoard[i] = [];
        for (let j = 0; j < boardSize; j++) {
          chessBoard[i][j] = "";
        }
      }
      moveHistory = [];
      gameOver = false;
      aiThinking = false;
      if (aiTimeout) clearTimeout(aiTimeout);
      currentPlayer = "black";
      gameMode = document.getElementById("gameMode").value;
      updateStatus(gameMode === "ai" ? "轮到你(黑棋)落子" : "轮到黑棋落子");
      drawBoard();
      updateUndoButton();
    }

    function restartGame() {
      init();
    }

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

      // 绘制网格线
      ctx.strokeStyle = currentTheme.boardLines;
      ctx.lineWidth = 1;
      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 = currentTheme.starColor;
      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");
          }
        }
      }

      // 标记最后一步棋
      if (moveHistory.length > 0) {
        const lastMove = moveHistory[moveHistory.length - 1];
        const cx = (lastMove.x + 1) * cellSize;
        const cy = (lastMove.y + 1) * cellSize;
        ctx.strokeStyle = "#ff0000";
        ctx.lineWidth = 2;
        ctx.beginPath();
        ctx.arc(cx, cy, cellSize / 4, 0, Math.PI * 2);
        ctx.stroke();
      }
    }

    function drawChess(x, y, color) {
      const cx = (x + 1) * cellSize;
      const cy = (y + 1) * cellSize;
      const radius = cellSize / 2 - 2;

      ctx.beginPath();
      ctx.arc(cx, cy, radius, 0, Math.PI * 2);

      if (color === "black") {
        const gradient = ctx.createRadialGradient(cx - radius / 3, cy - radius / 3, 1, cx, cy, radius);
        gradient.addColorStop(0, currentTheme.blackGradient[0]);
        gradient.addColorStop(1, currentTheme.blackGradient[1]);
        ctx.fillStyle = gradient;
        ctx.fill();
      } else {
        const gradient = ctx.createRadialGradient(cx - radius / 3, cy - radius / 3, 1, cx, cy, radius);
        gradient.addColorStop(0, currentTheme.whiteGradient[0]);
        gradient.addColorStop(1, currentTheme.whiteGradient[1]);
        ctx.fillStyle = gradient;
        ctx.fill();
        ctx.strokeStyle = currentTheme.whiteBorder;
        ctx.lineWidth = 1;
        ctx.stroke();
      }
    }

    // ========== 撤销功能 ==========
    function undoMove() {
      if (gameOver || moveHistory.length === 0 || aiThinking) return;

      if (gameMode === "ai") {
        // AI模式:撤销AI的棋和玩家的前一步棋
        const lastMove = moveHistory[moveHistory.length - 1];

        if (lastMove.player === "white" && moveHistory.length >= 2) {
          // 最后一步是AI下的,撤回AI和玩家的两步
          moveHistory.pop(); // AI的棋
          moveHistory.pop(); // 玩家的棋
        } else {
          // 只有玩家下了一步(游戏刚开始时)
          moveHistory.pop();
        }

        resetBoardFromHistory();
        currentPlayer = "black";
        updateStatus("轮到你(黑棋)落子");
      } else {
        // 本地模式:只撤销最后一步
        moveHistory.pop();
        resetBoardFromHistory();
        currentPlayer = currentPlayer === "black" ? "white" : "black";
        updateStatus(`轮到${currentPlayer === "black" ? "黑棋" : "白棋"}落子`);
      }

      drawBoard();
      updateUndoButton();
    }

    function resetBoardFromHistory() {
      for (let i = 0; i < boardSize; i++) {
        for (let j = 0; j < boardSize; j++) {
          chessBoard[i][j] = "";
        }
      }
      for (const move of moveHistory) {
        chessBoard[move.x][move.y] = move.player;
      }
    }

    // ========== 落子事件 ==========
    canvas.addEventListener("click", function (e) {
      if (gameOver || aiThinking) 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;
        moveHistory.push({ x: x, y: y, player: 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();
          }
          updateUndoButton();
          return;
        }

        currentPlayer = currentPlayer === "black" ? "white" : "black";

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

    // ========== AI逻辑 ==========
    function aiMove() {
      if (gameOver) {
        aiThinking = false;
        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";
        moveHistory.push({ x: bestMove.x, y: bestMove.y, player: "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();
          aiThinking = false;
          updateUndoButton();
          return;
        }

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

      aiThinking = false;
      updateUndoButton();
    }

    // ========== 评估函数 ==========
    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 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;
      }

      // 进攻 + 防守综合评分
      let defenseScore = evaluateDefense(x, y);
      return score + defenseScore;
    }

    function evaluateDefense(x, y) {
      let score = 0;
      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, "x"); // 假设对手下这里
        if (line.includes("xxxxx")) score += patternScores.live5 * 0.9;
        else if (/(_|^)xxxx_/.test(line) || /_xxxx($|_)/.test(line)) score += patternScores.live4 * 0.9;
        else if (/xx_x_x|x_xx_x|x_x_xx|_xxx_/.test(line)) score += patternScores.live3 * 0.8;
      }
      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();
    applyTheme("classic");
    init();
  </script>

</body>

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