見出し画像

【JavaScript】ワンピースのオセロゲーム(赤犬vs青雉)

今回はJavaScriptでオセロゲームを作成しました。

赤犬と青雉との戦いを再現してみました。オセロで笑

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>オセロゲーム</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="stylesheet" media="screen" href="style.css">
  </head>
  <body>
    <p>
      <span id="current-turn">赤犬の番です</span>
      <button id="pass">パスする</button>
    </p>
    <div id="stage" class="stage"></div>
    <div id="square-template" class="square">
      <div class="stone"></div>
    </div>
    <script src="index.js"></script>
  </body>
</html>
* { /*要素の幅と高さにpaddingとborderを含める*/
    box-sizing: border-box;
}

html, body {
    margin: 0;
    text-align: center; /*pタグを中央寄せ*/
    background-color: #bcf0bc;
}

.stage {
    display: flex;  /*<div id="stage" class="stage"></div>の中の要素を*/
    flex-wrap: wrap;/*折り返して表示する*/
    margin: 0 auto; /*中央寄せ*/
    width: 644px;   /* 84(一番左)+80(それ以外の四角)×7(個) */
    height: 644px;
}

.square {
    position: relative;
    width: 80px; /* 一番左以外の四角形に適用 */
    height: 80px;/* 一番上以外の四角形に適用 */
    border: solid black;
    border-width: 0 4px 4px 0; /*右と下の枠*/
    cursor: pointer;
    background-color: rgb(6, 136, 26);
}

.square:nth-child(-n + 8) {     /*一番上の四角形8個*/
    border-width: 4px 4px 4px 0;/*左以外の枠*/
    height: 84px;
}

.square:nth-child(8n + 1) {     /*一番左の四角形8個*/
    border-width: 0 4px 4px 4px;/*上以外の枠*/
    width: 84px;
}

.square:first-child { /*先頭の四角形(左隅)*/
    border-width: 4px;/*上下左右*/
    width: 84px;
    height: 84px;
}

.stone {
    position: absolute;
    width: 76px;
    height: 76px;
    background-size: contain;     /*背景画像全体が見えるように*/
    background-repeat: no-repeat; /*背景画像の繰り返しをしない*/
    background-position: center;  /*背景画像を中央にする*/
}

.stone[data-state="0"] {
    display: none;
}

.stone[data-state="1"] {
    background-color: red; /*背景画像を指定*/
    background-image: url('img/akainu.png');
}

.stone[data-state="2"] {
    background-color: blue; /*背景画像を指定*/
    background-image: url('img/aokiji.png');
}

#square-template {
    display: none;
}
const stage = document.getElementById("stage");
const squareTemplate = document.getElementById("square-template");
const stoneStateList = []; //配列で石の状態を管理
let currentColor = 1; //1は赤犬 2は青雉
const currentTurnText = document.getElementById("current-turn");
const passButton = document.getElementById("pass");

const changeTurn = () => {
  currentColor = 3 - currentColor; //「1なら2」「2なら1」にする
  if (currentColor === 1) {
    currentTurnText.textContent = "赤犬の番です";
  } else {
    currentTurnText.textContent = "青雉の番です";
    setTimeout(CPU, 1000);
  }
}

const CPU = () => { //コンピューター
  let clickNums = []; //クリックできるマスのindex番号を入れる配列
  for (let i = 0; i < 64; i++) {  //これ以降の処理を実行させない
   if (stoneStateList[i] !== 0 || !getReversibleStones(i).length) continue;
   clickNums.push(i);
  }
  let n = Math.floor(Math.random() * clickNums.length);
  onClickSquare(clickNums[n]);
}

const getReversibleStones = (idx) => { //ひっくり返せる石の番号をゲットする
  const squareNums = [ //各方向にマスがいくつあるか
    7 - (idx % 8),  //右に何マスあるか
    Math.min(7 - (idx % 8), (56 + (idx % 8) - idx) / 8),//右下に何マス
               //右のマス数          下のマス数
    (56 + (idx % 8) - idx) / 8, //下に何マス
    Math.min(idx % 8, (56 + (idx % 8) - idx) / 8),//左下に何マス
    idx % 8,  //左に何マス
    Math.min(idx % 8, (idx - (idx % 8)) / 8),  //左上に何マス
    (idx - (idx % 8)) / 8, //真上に何マス
    Math.min(7 - (idx % 8), (idx - (idx % 8)) / 8), //右上に何マス
  ];
  const parameters = [1, 9, 8, 7, -1, -9, -8, -7]; //隣のマスに行くため
                  //右 右下 真下 左下 左 左上 真上 右上
  let results = [];//ひっくり返せると確定した石の番号を入れる配列
  for (let i = 0; i < 8; i++) { //8方向で調査
    const box = [];  //ひっくり返せる可能性のある石の情報を入れる配列
    const squareNum = squareNums[i];  //現在調べている方向にいくつマスがあるか
    const param = parameters[i];
    const nextStoneState = stoneStateList[idx + param];//ひとつ隣の石の状態
          //隣に石がない       or          自分の色            -> 次のループへ
    if (nextStoneState === 0 || nextStoneState === currentColor) continue;
    box.push(idx + param); //ひとつ隣の石のindex番号

    for (let j = 0; j < squareNum - 1; j++) { //さらにその延長線
      const targetIdx = idx + param * 2 + param * j;
                               //2回目      次から足される
      const targetColor = stoneStateList[targetIdx];
      if (targetColor === 0) break; //その方向は石がないので終了
      if (targetColor === currentColor) {  //自分の色なら仮ボックスの石がひっくり返せることが確定
        results = results.concat(box);     //新しい配列を追加
        break;
      } else { //相手の色なら仮ボックスにその石の番号を格納
        box.push(targetIdx);
      }
    }
  }
  return results; //ひっくり返せると確定した石の番号を戻り値にする
}

const onClickSquare = (index) => {
  const reversibleStones = getReversibleStones(index); //ひっくり返せる石の数を取得
       //他の石(1 or 2)
  if (stoneStateList[index] !== 0 || !reversibleStones.length) {
    return;//これ以降の処理を実行させない
  }
  //自分の石を置く
  stoneStateList[index] = currentColor; 
  document.querySelector(`[data-index='${index}']`).setAttribute("data-state", currentColor);
  //相手の石をひっくり返す
  reversibleStones.forEach((key) => {
    stoneStateList[key] = currentColor;
    document.querySelector(`[data-index='${key}']`).setAttribute("data-state", currentColor);
  });
  //もし盤面がいっぱいだったら、集計してゲームを終了する
  if (stoneStateList.every((state) => state !== 0)) { //すべて0でない(1or2)なら真
    const redNum = stoneStateList.filter(state => state === 1).length;
    const blueNum = 64 - redNum;
    let winnerText = "";
    if (redNum > blueNum) {
      winnerText = "赤犬の勝ちです!";
    } else if (redNum < blueNum) {
      winnerText = "青雉の勝ちです!";
    } else {
      winnerText = "引き分けです";
    }
    setTimeout(
      () => { currentTurnText.textContent = `青${blueNum}、赤${redNum}で、${winnerText}`},
      2000
    );
  }

  changeTurn(); //ゲーム続行なら相手のターンにする
}

const createSquares = () => {
  for (let i = 0; i < 64; i++) {
    const square = squareTemplate.cloneNode(true);//クローンを作成
    square.removeAttribute("id"); //idが重複しないように
    stage.appendChild(square);    //盤に要素を追加
    const stone = square.querySelector('.stone');
    let defaultState; // 0:何もなし 1:赤犬 2:青雉
    if (i == 27 || i == 36) { //iの値によってデフォルトの石の状態を分岐する
      defaultState = 1; //赤犬
    } else if (i == 28 || i == 35) {
      defaultState = 2; //青雉
    } else {
      defaultState = 0;
    }
    stone.setAttribute("data-state", defaultState);
    stone.setAttribute("data-index", i); //インデックス番号をHTML要素に保持させる
    stoneStateList.push(defaultState); //初期値を配列に格納
    square.addEventListener('click', () => {
      onClickSquare(i);  //すべてにクリックイベントを登録
    });
  }
}

window.onload = () => {
  createSquares();
  passButton.addEventListener("click", changeTurn);
}

この記事が気に入ったらサポートをしてみませんか?