【JavaScript】前回の三並べをコンピュータと対戦できるようにした

対NPC型三並べ

この記事を三行にまとめると

CSSで作った三並べをカスタムします
×ボタンをコンピュータに操作させます
ぼっちに優しい仕様
前回の記事はこちら
【CSS】JavaScriptを使わずに三並べを作ってみた

この記事は以下の動画の中に出てきたサンプルコードを載せたものです。コピペなどが必要なときに使ってください。





CSSは前回と同じなので今回はHTMLとJavaScriptだけ載せます(HTMLも前回とほぼ同じだけど)

HTML

<form>
  <input type="checkbox" id="maru1">
  <input type="checkbox" id="maru2">
  <input type="checkbox" id="maru3">
  <input type="checkbox" id="maru4">
  <input type="checkbox" id="maru5">
  <input type="checkbox" id="maru6">
  <input type="checkbox" id="maru7">
  <input type="checkbox" id="maru8">
  <input type="checkbox" id="maru9">
  <input type="checkbox" id="batsu1">
  <input type="checkbox" id="batsu2">
  <input type="checkbox" id="batsu3">
  <input type="checkbox" id="batsu4">
  <input type="checkbox" id="batsu5">
  <input type="checkbox" id="batsu6">
  <input type="checkbox" id="batsu7">
  <input type="checkbox" id="batsu8">
  <input type="checkbox" id="batsu9">

  <div id="board">
    <div id="square1" class="square" data-click="true" data-id="1">
      <label for="maru1" class="maru"></label>
      <label for="batsu1" class="batsu"></label>
    </div>
    <div id="square2" class="square" data-click="true" data-id="2">
      <label for="maru2" class="maru"></label>
      <label for="batsu2" class="batsu"></label>
    </div>
    <div id="square3" class="square" data-click="true" data-id="3">
      <label for="maru3" class="maru"></label>
      <label for="batsu3" class="batsu"></label>
    </div>
    <div id="square4" class="square" data-click="true" data-id="4">
      <label for="maru4" class="maru"></label>
      <label for="batsu4" class="batsu"></label>
    </div>
    <div id="square5" class="square" data-click="true" data-id="5">
      <label for="maru5" class="maru"></label>
      <label for="batsu5" class="batsu"></label>
    </div>
    <div id="square6" class="square" data-click="true" data-id="6">
      <label for="maru6" class="maru"></label>
      <label for="batsu6" class="batsu"></label>
    </div>
    <div id="square7" class="square" data-click="true" data-id="7">
      <label for="maru7" class="maru"></label>
      <label for="batsu7" class="batsu"></label>
    </div>
    <div id="square8" class="square" data-click="true" data-id="8">
      <label for="maru8" class="maru"></label>
      <label for="batsu8" class="batsu"></label>
    </div>
    <div id="square9" class="square" data-click="true" data-id="9">
      <label for="maru9" class="maru"></label>
      <label for="batsu9" class="batsu"></label>
    </div>
  </div>

  <div id="dialog">
    <div id="message"></div>
    <button id="reset" type="reset">RESET</button>
  </div>
</form>



JavaScript

const buttons = document.querySelectorAll('label');
const dialog = document.getElementById('dialog');
let patterns = ['123', '456', '789', '147', '258', '369', '159', '357'];

// ○×ボタンのクリックイベント
buttons.forEach((button) => {
  button.onclick = () => {
    // squqreのdata-clickをfalseにする
    parent = button.parentNode;
    parent.dataset.click = 'false';

    // 配列の各要素の数字を文字に置き換える
    patterns = patterns.map((pattern) => {
      // ○ボタンを押したときはm、×ボタンを押したときはbとする
      mark = button.classList.contains('maru') ? 'm' : 'b';
      return pattern.replace(parent.dataset.id, mark);
    });

    // 押したボタンが○だったら1秒後に×ボタンのクリックイベント発動 
    if(button.classList.contains('maru')) {
      setTimeout(() => {
        // 勝負がついていたら何もしない
        if(window.getComputedStyle(dialog).display == 'flex') {
          return;
        }

        // ×のリーチがあれば3つ目をそろえにいく
        if(NPC('b', 1)) {
          return;
        }

        // ○のリーチがあれば3つ目を阻止する
        if(NPC('m', 1)) {
          return;
        }

        // リーチが作れそうなところがあれば作る
        if(NPC('b', 2)) {
          return;
        }

        // ランダムに×を押す
        clickables = document.querySelectorAll('[data-click="true"] .batsu');
        i = Math.floor(Math.random() * clickables.length);
        clickables[i].click();
      }, 1000);
    }
  }
})

NPC = (rep, len) => {
  return patterns.some((pattern) => {
    // 配列の各要素から置き換えた文字を削除
    str = pattern.replaceAll(rep, '');
    check = rep == 'b' ? 'm' : 'b';

    // 条件に該当する箇所があれば×を押す
    if(str.length == len && !str.includes(check)) {
      selector = '[data-id="' + str[0] + '"] .batsu';
      clickable = document.querySelector(selector);
      clickable.click();
      return true;
    }
  });
}

// リセットボタンを押したら状態を初期に戻す
const reset = document.getElementById('reset');
const squares = document.querySelectorAll('.squares');

reset.onclick = () => {
  patterns = ['123', '456', '789', '147', '258', '369', '159', '357'];
  squares.forEach((square) => {
    square.dataset.click = 'true';
  });
}
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください