跳转至内容

JavaScript/练习/井字棋

来自维基教科书,自由的教学资源




井字棋

井字棋 是一款两人游戏。他们选择 3 x 3 棋盘中的方格。

首先,我们需要一个 HTML 文件加上 CSS 来实现用户界面。它应该包含:

  • 一个标题
  • 一个包含九个按钮的容器,以 3 x 3 棋盘的形式排列
  • 两个按钮“开始 X”和一个按钮“开始 O”,以决定哪位玩家先开始
  • 一个“重置”按钮
  • 一个文本字段,用于向用户反馈游戏的当前状态

HTML 代码可能如下所示

点击查看解决方案
<!DOCTYPE html>
<html>
<head>
  <title>TicTacToe</title>
  <script>
  // ...
  </script>

  <style>
    .container {
      display: grid;
      grid-template-columns: 32% 32% 32%;
      grid-template-rows:    6em 6em 6em;
      gap: 1%;
      background-color: aliceblue;
      margin: 2em;
      padding: 2em;
    }
    .cell {
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .button {
      height:2.4em;
      width: 2.4em;
      font-size: 2em;
      background-color: aqua;
    }
    .containerCmd {
      display: flex;
      justify-content: flex-end;
    }
    .buttonCmd {
      padding: 0.6em 2em 0.6em 2em;
      font-size: 1em;
      margin-right: 2em;
    }
    .feedback {
      padding: 0.8em 1em 0.8em 1em;
      margin: 1em;
      font-size: 1.4em;
      background-color: green;
    }
  </style>

</head>

<body>

  <h1 style="text-align: center;">TicTacToe</h1>

  <!--  The container with the nine clickable buttons  -->
  <div class="container">
    <div class="cell"><button id="b1" class="button" disabled /></div>
    <div class="cell"><button id="b2" class="button" disabled /></div>
    <div class="cell"><button id="b3" class="button" disabled /></div>
    <div class="cell"><button id="b4" class="button" disabled /></div>
    <div class="cell"><button id="b5" class="button" disabled /></div>
    <div class="cell"><button id="b6" class="button" disabled /></div>
    <div class="cell"><button id="b7" class="button" disabled /></div>
    <div class="cell"><button id="b8" class="button" disabled /></div>
    <div class="cell"><button id="b9" class="button" disabled /></div>
  </div>

  <!-- buttons for start and reset the game -->
  <div class="containerCmd">
    <button class="buttonCmd" id="startX">Start: X</button>
    <button class="buttonCmd" id="startO">Start: O</button>
    <p style="padding-right:3em"></p> <!-- a small spacer -->
    <button class="buttonCmd">Reset</button>
  </div>

  <!-- feedback from the script to the players -->
  <p id="feedback" class="feedback">Click to one of the 'Start' buttons</p>

</body>
</html>


接下来,通过向按钮添加事件和在脚本元素中添加函数,来开发应用程序的逻辑。

  • 当游戏开始或重置时,九个 X/O 按钮和反馈区域必须被清空。
  • 九个 X/O 按钮调用同一个事件处理程序会很有帮助。event.target.id 提供了按钮的 ID,通过 document.getElementById(event.target.id) 可以访问该按钮。
  • 每当点击九个 X/O 按钮中的一个时,必须阻止该按钮被再次点击 elem.disabled = true
  • 我们需要一个函数来判断一行、一列或一条对角线(共 8 种可能)是否包含 3 个 'X' 或 3 个 'O'。
  • 考虑没有人获胜的情况。

总的来说,应用程序可能看起来像这样

点击查看解决方案
<!DOCTYPE html>
<html>
<head>
  <title>TicTacToe</title>
  <script>
  "use strict";

  // global variable for X/O
  let user = "";

  // --------  start: decide which user has the first click  ----------------
  function startXO(userXO) {
    if (userXO === "X") {
      user = "X";
    } else {
      user = "O";
    }
    // disable / enable certain buttons
    document.getElementById("startX").disabled = true;
    document.getElementById("startO").disabled = true;
    ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9"]
      .forEach((button) => {
        document.getElementById(button).disabled = false;
      });
    document.getElementById("feedback").innerHTML = "";
  }

  // --------  regular action  ----------------------------------------------
  function buttonClicked(event) {
    const elem = document.getElementById(event.target.id);
    elem.innerHTML = user;
    elem.disabled = true;

    // check for end of game
    switch (isFinished()) {
    case "tie":
      document.getElementById("feedback").innerHTML = "No winner. Tie.";
      ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9"]
        .forEach((button) => {
          document.getElementById(button).disabled = true;
        });
      break;

    case true:
      document.getElementById("feedback").innerHTML = "The winner is: " + user;
      ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9"]
        .forEach((button) => {
          document.getElementById(button).disabled = true;
        });
      break;

    default:
      // toggle user and go on
      if (user === "X") {
        user = "O";
      } else {
        user = "X";
      }
    }
  }
  
  // --------  check for end of game ----------------------
  function isFinished() {
    const xo_b1 =  document.getElementById("b1").innerHTML;
    const xo_b2 =  document.getElementById("b2").innerHTML;
    const xo_b3 =  document.getElementById("b3").innerHTML;
    const xo_b4 =  document.getElementById("b4").innerHTML;
    const xo_b5 =  document.getElementById("b5").innerHTML;
    const xo_b6 =  document.getElementById("b6").innerHTML;
    const xo_b7 =  document.getElementById("b7").innerHTML;
    const xo_b8 =  document.getElementById("b8").innerHTML;
    const xo_b9 =  document.getElementById("b9").innerHTML;

    // check for 'tie' in a loop over all buttons
    let tmp = 0;
    [xo_b1, xo_b2, xo_b3, xo_b4, xo_b5, xo_b6, xo_b7, xo_b8, xo_b9]
      .forEach((elem) => {if (elem !== "") tmp++});
    if (tmp === 9) {
      return "tie";
    }

    // check for winner
    if (             // horizontal
        (xo_b1 === user && xo_b2 === user && xo_b3 === user) ||
        (xo_b4 === user && xo_b5 === user && xo_b6 === user) ||
        (xo_b7 === user && xo_b8 === user && xo_b9 === user) ||
                     // vertical
        (xo_b1 === user && xo_b4 === user && xo_b7 === user) ||
        (xo_b2 === user && xo_b5 === user && xo_b8 === user) ||
        (xo_b3 === user && xo_b6 === user && xo_b9 === user) ||
                     // diagonal
        (xo_b1 === user && xo_b5 === user && xo_b9 === user) ||
        (xo_b3 === user && xo_b5 === user && xo_b7 === user)
       )
    {
      return true;
    } else {
      return false;
    }
  }

  // --------  reset game  -------------------------------------
  function reset() {

    // disable / enable certain buttons
    ["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9"]
      .forEach((button) => {
        document.getElementById(button).innerHTML = "";
        document.getElementById(button).disabled = true;
      });
    document.getElementById("feedback").innerHTML = "Click to 'Start'";
    document.getElementById("startX").disabled = false;
    document.getElementById("startO").disabled = false;
  }
  </script>

  <style>
    .container {
      display: grid;
      grid-template-columns: 32% 32% 32%;
      grid-template-rows:    6em 6em 6em;
      gap: 1%;
      background-color: aliceblue;
      margin: 2em;
      padding: 2em;
    }
    .cell {
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .button {
      height:2.4em;
      width: 2.4em;
      font-size: 2em;
      background-color: aqua;
    }
    .containerCmd {
      display: flex;
      justify-content: flex-end;
    }
    .buttonCmd {
      padding: 0.6em 2em 0.6em 2em;
      font-size: 1em;
      margin-right: 2em;
    }
    .feedback {
      padding: 0.8em 1em 0.8em 1em;
      margin: 1em;
      font-size: 1.4em;
      background-color: green;
    }
  </style>

</head>

<body>

  <h1 style="text-align: center;">TicTacToe</h1>

  <!--  The container with the nine clickable buttons  -->
  <div class="container">
    <div class="cell"><button id="b1" class="button" onclick="buttonClicked(event)" disabled /></div>
    <div class="cell"><button id="b2" class="button" onclick="buttonClicked(event)" disabled /></div>
    <div class="cell"><button id="b3" class="button" onclick="buttonClicked(event)" disabled /></div>
    <div class="cell"><button id="b4" class="button" onclick="buttonClicked(event)" disabled /></div>
    <div class="cell"><button id="b5" class="button" onclick="buttonClicked(event)" disabled /></div>
    <div class="cell"><button id="b6" class="button" onclick="buttonClicked(event)" disabled /></div>
    <div class="cell"><button id="b7" class="button" onclick="buttonClicked(event)" disabled /></div>
    <div class="cell"><button id="b8" class="button" onclick="buttonClicked(event)" disabled /></div>
    <div class="cell"><button id="b9" class="button" onclick="buttonClicked(event)" disabled /></div>
  </div>

  <!-- buttons for start and reset the game -->
  <div class="containerCmd">
    <button class="buttonCmd" id="startX" onClick="startXO('X')">Start: X</button>
    <button class="buttonCmd" id="startO" onClick="startXO('O')">Start: O</button>
    <p style="padding-right:3em"></p> <!-- a small spacer -->
    <button class="buttonCmd" onClick="reset()">Reset</button>
  </div>

  <!-- feedback from the script to the players -->
  <p id="feedback" class="feedback">Click to one of the 'Start' buttons</p>

</body>
</html>
华夏公益教科书