跳轉至內容

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>
華夏公益教科書