JavaScript、jQueryでオセロを作る。その1

JavaScript、jQueryでオセロを作ってみるシリーズのその1です。

# 本記事の環境
※PCに環境構築を行う必要はありません。
WEBブラウザ(Google Chome)
テキストエディタ

# 成果物(画面)
![オセロ02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1009061/83d17039-640c-bdf2-f17b-1a5ba4f324f9.png)
![オセロ03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1009061/94ce1795-43a9-09b6-ec3d-c565c37bad1a.png)
![オセロ01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1009061/0502fa9d-a680-679a-c842-855e36ade765.png)

# 所感
* オセロ盤、石を表示できるようにするのが難しかった。
* どこに石を置けるかの判定処理が難しかった。
* COM側の「どこに置くのが最善手か」の思考処理は追って作ってみたい。

# 成果物(ソースコード)
* 参考程度にしてください。軽くは動作確認済みです。  

```html:othello.html
<!DOCTYPE html>
<html>
  <head>
    <title>オセロ</title>
    <style>
      body {
        background-color : #eee;
      }
      #board {
        position : relative;
        top : 50px;
        left : 50px;
        background-color : black;
      }
      #board td {
        width : 20px;
        height : 20px;
        background-color : green;
      }
      .whiteStone {
        width : 20px;
        height : 20px;
        border-radius : 50%;
        background-color : white;
        color : white;
        text-align : center;
      }
      .blackStone {
        width : 20px;
        height : 20px;
        border-radius : 50%;
        background-color : black;
        color : black;
        text-align : center;
      }
    </style>
  </head>
  <body>
    <template id="massImg">
      <td id="1_1"></td>
    </template>
    <div>
      <div>
        <table id='board'><tbody id='boardbody'></tbody></table>
      </div>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script language="JavaScript">
      // オセロ情報
      const MAX_NUM = 8;
      const MIN_NUM = 1;
      const VALUE0 = "0";
      const VALUE1 = "1";
      const VALUE2 = "2";
      const COLOR1 = "white";
      const COLOR2 = "black";
      
      // 反転判定パターン
      const CHECK_PATTERN = [
        [ 0,  1],  // 上
        [ 0, -1],  // 下
        [-1,  0],  // 左
        [ 1,  0],  // 右
        [ 1,  1],  // 右上
        [ 1, -1],  // 右下
        [-1,  1],  // 左上
        [-1, -1],  // 左下
      ];
      
      // オセロマス
      function Mass(x, y, stone) {
        // プロパティ
        this.x = x;
        this.y = y;
        this.stone = stone;
      }
      
      // オセロ盤
      function Board() {
        // プロパティ
        this.board = [];

        // 初期化処理
        this.init = function() {
          this.board = [];
          let mass;
          for (let y = MAX_NUM; y >= MIN_NUM; y--) {
            for (let x = MIN_NUM; x <= MAX_NUM; x++) {
              mass = new Mass(x, y, VALUE0);
              this.board.push(mass);
            }
          }
          return;
        };
      }
      
      // オセロ
      function Othello () {
        // プロパティ
        let board = new Board();
        
        // 初期化処理
        this.init = function() {
          // オセロ盤の初期化処理を呼び出す。
          board.init();
        }
        
        // オセロ盤表示処理
        this.outputBoard = function() {
          $("#boardbody").empty();
          let content_massImg = $("#massImg").contents();
          for (let y = MAX_NUM; y >= MIN_NUM; y--) {
            $("<tr id='y" + y + "'></tr>").appendTo("#boardbody");
            for (let x = MIN_NUM; x <= MAX_NUM; x++) {
              $(content_massImg).prop("id", x + "_" + y);
              $(content_massImg).clone().appendTo("#y" + y);
            }
          }
          $("#boardbody").show();
        }
        
        // 初期石設置処理
        this.setDefaultStone = function() {
          $("#" + (MAX_NUM / 2) + "_" + (MAX_NUM / 2)).html("<div class='blackStone'>B</div>");
          $("#" + (MAX_NUM / 2 + 1) + "_" + (MAX_NUM / 2)).html("<div class='whiteStone'>W</div>");
          $("#" + (MAX_NUM / 2) + "_" + (MAX_NUM / 2 + 1)).html("<div class='whiteStone'>W</div>");
          $("#" + (MAX_NUM / 2 + 1) + "_" + (MAX_NUM / 2 + 1)).html("<div class='blackStone'>B</div>");
        }
        
        // オセロマス選択イベント設定処理
        this.setMassEvent = function() {
          let stone_color = "black";
          for (let y = MAX_NUM; y >= MIN_NUM; y--) {
            for (let x = MIN_NUM; x <= MAX_NUM; x++) {
              $("#" + x + "_" + y).on("click", function () {
                // 反転判定処理を呼び出す。
                let reverseList = checkMain(x, y, stone_color);
                if (reverseList.length >= 1) {
                  // 反転できる石がある場合
                  
                  // 当該操作の石を置く。
                  if (stone_color == "black") {
                    $("#" + x + "_" + y).html("<div class='blackStone'>B</div>");
                  } else {
                    $("#" + x + "_" + y).html("<div class='whiteStone'>W</div>");
                  }
                  // 反転実行処理を呼び出す。
                  reverse(reverseList, stone_color);
                  if (stone_color == "black") {
                    stone_color = "white";
                  } else {
                    stone_color = "black";
                  }
                } else {
                  alert("x" + x + "y" + y + "には置けません。");
                }
              });
            }
          }
        }
        
        // 反転実行処理
        function reverse(reverseList, stone_color) {
          for (let target = 0; target < reverseList.length; target++) {
            let targetX = reverseList[target][0];
            let targetY = reverseList[target][1];
            if (stone_color == "black") {
              $("#" + targetX + "_" + targetY).html("<div class='blackStone'>B</div>");
            } else {
              $("#" + targetX + "_" + targetY).html("<div class='whiteStone'>W</div>");
            }
          }
        }
        
        // 反転判定処理
        function checkMain(x, y, stone_color) {
          let reverseList = checkCore(x, y, stone_color);
          return reverseList;
        }
        
        // 反転判定実処理
        function checkCore(x, y, stone_color) {
          let reverseList = [];
          let color_1 = "B";
          let color_2 = "W";
          if (stone_color == "white") {
            color_1 = "W";
            color_2 = "B";
          }
          for (let target = 0; target < CHECK_PATTERN.length; target++) {
            let targetX = x + CHECK_PATTERN[target][0];
            let targetY = y + CHECK_PATTERN[target][1];
            if ((targetX >= MIN_NUM) && (targetX <= MAX_NUM) && (targetY >= MIN_NUM) && (targetY <= MAX_NUM)) {
              if ($($("#" + targetX + "_" + targetY).contents()).html() == color_2) {
                let reverseList_work = [];
                let reverse_flg = false;
                reverseList_work.push([targetX, targetY]);
                //alert("stock1 : " + targetX + "_" + targetY);
                for (let shift = 2; shift <= MAX_NUM; shift++) {
                  let targetX2 = x + CHECK_PATTERN[target][0] * shift;
                  let targetY2 = y + CHECK_PATTERN[target][1] * shift;
                  //alert("TARGET : " + targetX2 + "_" + targetY2);
                  if ((targetX2 < MIN_NUM) || (targetX2 > MAX_NUM) || (targetY2 < MIN_NUM) || (targetY2 > MAX_NUM)) {
                    //alert("break1 : " + targetX2 + "_" + targetY2);
                    break;
                  }
                  if ($($("#" + targetX2 + "_" + targetY2).contents()).html() == color_2) {
                    reverseList_work.push([targetX2, targetY2]);
                    //alert("stock2 : " + targetX2 + "_" + targetY2);
                  } else if ($($("#" + targetX2 + "_" + targetY2).contents()).html() == color_1) {
                    //alert("break2 : " + targetX2 + "_" + targetY2);
                    reverse_flg = true;
                    break;
                  }
                }
                if (reverse_flg) {
                  reverseList = reverseList.concat(reverseList_work);
                }
              }
            }
          }
          return reverseList;
        }
      }
      
      // オセロ盤表示処理を呼び出す。
      let game = new Othello();
      game.outputBoard();
      
      // 初期石設置処理を呼び出す。
      game.setDefaultStone();
      
      // オセロマス選択イベント設定処理を呼び出す。
      game.setMassEvent();
    </script>
  </body>
</html>
```