Reactのチュートリアルの三目並べをjQueryでやる

[前回](https://qiita.com/GodPhwng/items/1a4c2e0b784c7a4f8b54)作った三目並べをjQueryで書いてみようと思います。
どれくらい煩雑になるのだろう。

#数値の表示のみ
```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="css/index.css" />
    <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.4.1.min.js"></script>
    <script src="js/index.js"></script>
  </head>
  <body>
    <div id="root">
      <div class="game">
        <div class="gmae-board">
          <div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
          </div>
        </div>
        <div class="game-info">
          <div>次の手番: X</div>
          <div>
            <!--<li><button>Go to game start</button></li>-->
            <!-- <li><button>Go to move #1</button></li> -->
          </div>
        </div>
      </div>
    </div>
  </body>
</html>
```

```css:index.css
body {
  font: 14px 'Century Gothic', Futura, sans-serif;
  margin: 20px;
}

ol,
ul {
  padding-left: 30px;
}

.board-row:after {
  clear: both;
  content: '';
  display: table;
}

.status {
  margin-bottom: 10px;
}

.square {
  background: #fff;
  border: 1px solid #999;
  float: left;
  font-size: 24px;
  font-weight: bold;
  line-height: 34px;
  height: 34px;
  margin-right: -1px;
  margin-top: -1px;
  padding: 0;
  text-align: center;
  width: 34px;
}

.square:focus {
  outline: none;
}

.kbd-navigation .square:focus {
  background: #ddd;
}

.game {
  display: flex;
  flex-direction: row;
}

.game-info {
  margin-left: 20px;
}
```
以下処理だけで行けそうです。

```javascript:index.js
$(function() {
  $(".square").each((index, elem) => {
    $(elem).text(index);
  });
});
```

Live Serverの拡張をインストールして、以下からブラウザを起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/1b8c1f61-11eb-f088-4827-58f270f659e7.png)
出来てました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/636ade06-5be4-2a70-d0c9-21df51440b37.png)

#XとOを入力できるようにする
基本的な設計はチュートリアルを真似します。

```javascript:index.js
$(function() {
  let xIsNext = true;

  $(".square").click(function() {
    $(this).text(xIsNext ? "X" : "O");
    xIsNext = !xIsNext;
  });
});
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/825a6759-bf92-5a5c-393e-b94fc2cca6db.png)

#履歴なしの完成までもっていく

```javascript:index.js
//import $ from "jQuery";

$(function() {
  const squares = new Array(9).fill(null);
  let xIsNext = true;
  const status = $(".game-info div:first-child");

  $(".square").click(function() {
    // 押されたsquareのインデックス
    const index = $(".square").index(this);

    if (calculateWinner(squares) || squares[index]) {
      return;
    }

    squares[index] = xIsNext ? "X" : "O";
    $(this).text(squares[index]);
    status.text("次の手番: " + (xIsNext ? "O" : "X"));
    xIsNext = !xIsNext;
  });

  // 勝敗判定関数(公式チュートリアルから拝借)
  function calculateWinner(squares) {
    const lines = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6]
    ];
    for (let i = 0; i < lines.length; i++) {
      const [a, b, c] = lines[i];
      if (
        squares[a] &&
        squares[a] === squares[b] &&
        squares[a] === squares[c]
      ) {
        return squares[a];
      }
    }
    return null;
  }
});
```
![ダウンロード (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/611c5d6d-6caf-dcc2-377a-0d96d4cfdc93.gif)

**一旦完成**

ここまでだと、Reactよりソースコード短いかな？


#履歴機能を持たせる
履歴機能を持たせて、最後まで作ります。

```html:index.html
<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" href="css/index.css" />
    <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.4.1.min.js"></script>
    <script src="js/index.js"></script>
  </head>
  <body>
    <div id="root">
      <div class="game">
        <div class="gmae-board">
          <div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
            <div class="board-row">
              <button class="square"></button><button class="square"></button
              ><button class="square"></button>
            </div>
          </div>
        </div>
        <div class="game-info">
          <div>次の手番: X</div>
          <div>
            <li><button>Go to game start</button></li>
            <!-- <li><button>Go to move #1</button></li> -->
          </div>
        </div>
      </div>
    </div>
  </body>
</html>
```
jsは信じられないくらいワケわかめになりました。
もっとスマートにしたかった・・・。
(行数長かったんでコメント右に書いてます)

```javascript:index.js
//import $ from "jQuery";

$(function() {
  // 履歴
  const history = [{ squares: new Array(9).fill(null) }];

  let stepNumber = 0; // 現在表示している履歴のインデックスを表す
  const status = $(".game-info div:first-child"); // 手番
  const allJqSquare = $(".square"); // 全square jQueryオブジェクト

  // 最初の履歴ボタン
  const startButton = $(".game-info li")
    .find("button:first")
    .click(historyButtonClick.bind(null, 0)); // bindで引数を縛る

  // square押下処理
  allJqSquare.click(function() {
    const index = allJqSquare.index(this); // 押されたsquareのインデックス
    const current = history[stepNumber]; // 現在のsquares
    const squares = current.squares;

    if (calculateWinner(squares) || squares[index]) {
      return;
    }

    const newSquares = squares.concat(); // squaresのコピー
    newSquares[index] = stepNumber % 2 === 0 ? "X" : "O";
    $(this).text(newSquares[index]);
    status.text("次の手番: " + (stepNumber % 2 === 0 ? "O" : "X"));

    // カレントのstepNumberより後ろのボタンを削除する
    $(".game-info li").each((i, element) => {
      if (i > stepNumber) {
        $(element).remove();
      }
    });

    // 現在のステップまで履歴も削除
    for (; stepNumber + 1 < history.length; ) {
      history.pop();
    }

    history.push({ squares: newSquares }); // 新しい要素追加
    stepNumber++;

    // 新たに履歴ボタンを作る
    // 親のliごとクローンする
    startButton
      .parent()
      .clone()
      .appendTo(startButton.parent().parent()) // .game-infoに追加
      .children("button")
      .text("Go to move #" + stepNumber) // ボタン名変更
      .click(historyButtonClick.bind(null, stepNumber)); // bindで引数を縛る
  });

  // 履歴ボタン押下処理
  function historyButtonClick(sNumber) {
    stepNumber = sNumber;
    status.text("次の手番: " + (stepNumber % 2 === 0 ? "X" : "O")); // ここは上とは反対になる

    // 全squareのテキストを書き直す
    history[sNumber].squares.forEach((value, i) => {
      allJqSquare.eq(i).text(value);
    });
  }

  // 勝敗判定関数(公式チュートリアルから拝借)
  function calculateWinner(squares) {
    const lines = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [0, 3, 6],
      [1, 4, 7],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6]
    ];
    for (let i = 0; i < lines.length; i++) {
      const [a, b, c] = lines[i];
      if (
        squares[a] &&
        squares[a] === squares[b] &&
        squares[a] === squares[c]
      ) {
        return squares[a];
      }
    }
    return null;
  }
});

```

#感想
履歴がないバージョンまではスラスラできたんですが、履歴のとこで死にました。

私のやり方が悪いのもあるかもしれませんが、Reactのメリットを実感しました！tbn