JavaScriptでオセロを作ってみた。

JavaScriptでオセロを作ってみた。:


作るときに意識すること

作るときに「これは絶対に守る!」を決めてから作成しました。以下の3つです。

  • 関数はできるだけ機能ごとに分ける
  • コメントはしっかり残す
  • 処理の追いにくい命名はしない
この決まりを決めた理由は2つあります。

まず、大前提にJavaScriptを「書けるようなる」ことが目的なので、自分で意味のわからないコピペソースは後に業務で活かせることが期待できないのでしっかり用途を理解したソースコードしか書かない。

2つ目、五月雨式に機能を追加していく個人ソースは関数の中で複数の機能を持ちがち。

実際の業務でこんなことしてしまった日には運用前提に作られているサイトでは秘伝のたれ状態になることは必至。

ということでその辺りを意識しながら書いていきます。

ちなみにオセロであることに大きな理由はありません。単純にさわれて遊べるものが良かったからです。

処理の汚さは一旦気にしないでやっていきます。


さっそく作ってみる

スタイルはのちにCSSファイルに分けることにして、一旦HTMLファイルに記述

マスは8×8の正方形で作りたいと思います。

真ん中4マスには「黒」と「白」2つずつをデフォルトで設置しておきます。

index.html
<!DOCTYPE html> 
<html lang="ja"> 
<head> 
  <meta charset="UTF-8"> 
  <meta name="viewport" content="width=device-width, initial-scale=1.0"> 
  <meta http-equiv="X-UA-Compatible" content="ie=edge"> 
  <title>JavaScript</title> 
  <style> 
 
    .table { 
      position:absolute; 
      top: 50%; 
      left: 50%; 
      margin-top: -257px; 
      margin-left: -257px; 
    } 
 
    .table td { 
      width: 60px; 
      height: 60px; 
      background-color: #ddd; 
      text-align: center; 
      font-size: 3.8vw; 
      cursor: pointer; 
    } 
 
    .list { 
      max-width: 750px; 
      margin: 0 auto; 
      list-style: none; 
      background-color: #fff; 
      padding: 0; 
    } 
 
    .list-item { 
      display: flex; 
      display: -webkit-flex; 
      -webkit-justify-content: flex-start; 
      justify-content: flex-start; 
      border-bottom: 2px solid #ddd; 
    } 
    .list-item > * { 
      width: 50px; 
      padding: 5px 10px; 
      text-align: center; 
    } 
    .list-item *:last-child { 
      -webkit-flex-grow: 1; 
      flex-grow: 1; 
    } 
  </style> 
  <script src="./index.js"></script> 
</head> 
<body> 
  <!--tableはこんな感じで8つ行を作ります--> 
  <table class="table"> 
    <!-- 省略 真ん中は下記のような感じで記述 --> 
    <tr> 
      <td></td> 
      <td></td> 
      <td></td> 
      <td>◯</td> 
      <td>●</td> 
      <td></td> 
      <td></td> 
      <td></td> 
    </tr> 
    <tr> 
      <td></td> 
      <td></td> 
      <td></td> 
      <td>●</td> 
      <td>◯</td> 
      <td></td> 
      <td></td> 
      <td></td> 
    </tr> 
    <!-- 省略 --> 
  </table> 
</body> 
</html> 
 
index.js
window.onload = function(){ 
 
  var $tableElements = document.getElementsByTagName('td'); 
  //順番を制御するための変数 
  let order = true; //trueは黒(先行) 
  let othelloWhte = '◯'; 
  let othelloBlack = '●'; 
  let othelloColor = othelloBlack; 
 
  //tableの全てにclickイベントを付与する 
  for (let $i=0; $i < $tableElements.length; $i++) { 
    $tableElements[$i].addEventListener('click', function(){ 
      //配列に変換する 
      let tableElements = [].slice.call($tableElements); 
      //クリックした位置の取得 
      let index = tableElements.indexOf(this); 
      putOthello(index); 
      changeOrder(); 
    }); 
  } 
 
  function putOthello(index) { 
    $tableElements[index].innerHTML = othelloColor; 
  } 
  //順番の判別する 
  function changeOrder() { 
    if (order) { 
      othelloColor = othelloWhte; 
      order = false; 
    } else { 
      othelloColor = othelloBlack; 
      order = true; 
    } 
  } 
} 
 
これでとりあえずそれっぽいものを完成。



68747470733a2f2f71696974612d696d6167652d


このままだと一度置いたオセロも隙を見て置き換えることができるファンタスティックな状態になります。

ただ、お互いがルールを絶対に守るのであれば十分遊べるので良いのでは?

…と思ってしまうけれどもプログラムとしては欠陥だらけなのでこれを基盤に機能を足していきます。


隣のオセロの色を変更する

index.js
changeOthello = (index) => { 
    //両隣とその隣のオセロの色(値)を取得 
    let prevLeftOthello = $tableElements[index - 2].innerHTML; 
    let prevOthello = $tableElements[index - 1].innerHTML; 
    let nextRightOthello = $tableElements[index + 2].innerHTML; 
    let nextOthello = $tableElements[index + 1].innerHTML; 
 
    //黒 
    //左隣の次のマスの色が置いたオセロと同じ色の場合隣のオセロの色を変える 
    if (prevLeftOthello.match(othelloBlack) && prevOthello.match(othelloWhte)) { 
      let targetIndex = index - 1; 
      putOthello(targetIndex, index); 
    } 
 
    //右隣の次のマスの色が置いたオセロと同じ色の場合隣のオセロの色を変える 
    if (nextRightOthello.match(othelloBlack) && nextOthello.match(othelloWhte)) { 
      let targetIndex = index + 1; 
      putOthello(targetIndex, index); 
    } 
 
    //白 
    //左隣の次のマスの色が置いたオセロと同じ色の場合隣のオセロの色を変える 
    if (prevLeftOthello.match(othelloWhte) && prevOthello.match(othelloBlack)) { 
      let targetIndex = index - 1; 
      putOthello(targetIndex, index); 
    } 
 
    //右隣の次のマスの色が置いたオセロと同じ色の場合隣のオセロの色を変える 
    if (nextRightOthello.match(othelloWhte) && nextOthello.match(othelloBlack)) { 
      let targetIndex = index + 1; 
      putOthello(targetIndex, index); 
    } 
  } 
同じ色のオセロで挟むと隣のオセロの色が変わるようになります。

ただ、下の図の赤い枠で囲った部分のように

  • 違う色のオセロを2つ以上挟んで黒を置く
  • 上の段の右端に「白」、下の段の左端に「黒」 その隣に白を置くと「黒→白」になる


68747470733a2f2f71696974612d696d6167652d


現状致命的なのは2つ目の段違いでも色が変更されてしまう点を解消したいと思います。

※オセロの色を変えるのは最悪2回クリックすれば置き換えられる

一番左の列は上からマスの右側、一番右の列の左側にオセロを置いた時に左隣もしくは右隣のオセロの色が変わらなければいいので。

index.js
  let unavailableListLeft = [9, 17, 25, 33, 41, 49, 57]; 
    const validLeftSide = []; 
    let number = 0; 
 
    //配列を使って選択したマスが該当の箇所かをチェックする(左側) 
    unavailableListLeft.forEach(function() { 
   //配列の中を比較、結果を配列に格納 
      if (index === unavailableListLeft[number]) { 
        validLeftSide.push(true); 
      } else { 
        validLeftSide.push(false); 
      } 
      number++; 
    }); 
 
    //配列の中に一つでもtrueがあるとtrueを返してくれる「some」メソッド 
    let result = validLeftSide.some((value) => { 
      return value === true; 
    }); 
これを右側分も作って該当の場所に置かれた時は隣のオセロの色が変わらないようにします。

もっと処理をまとめられるとは思いつつ先に進めます。

最終的に下記のような形に。

index.js
    let unavailableListLeft = [9, 17, 25, 33, 41, 49, 57]; 
    let unavailableListRight = [6, 14, 22, 30, 38, 46, 54]; 
    const validLeftSide = []; 
    const validRightSide = []; 
    let leftIndex = 0; 
    let rightIndex = 0; 
 
    //配列を使って選択したマスが該当の箇所かをチェックする(左側) 
    unavailableListLeft.forEach(function() { 
      if (index === unavailableListLeft[leftIndex]) { 
        validLeftSide.push(true); 
      } else { 
        validLeftSide.push(false); 
      } 
      leftIndex++; 
    }); 
 
    //配列の中に一つでもtrueがあるとtrueを返してくれる「some」メソッド 
    let resultLeftSide = validLeftSide.some((value) => { 
      return value === true; 
    }); 
 
    //配列を使って選択したマスが該当の箇所かをチェックする(右側) 
    unavailableListRight.forEach(function() { 
      if (index === unavailableListRight[rightIndex]) { 
        validRightSide.push(true); 
      } else { 
        validRightSide.push(false); 
      } 
      rightIndex++; 
    }); 
 
    //配列の中に一つでもtrueがあるとtrueを返してくれる「some」メソッド 
    let resultRightSide = validRightSide.some((value) => { 
      return value === true; 
    }); 
 
    //左隣の次のマスの色が置いたオセロと同じ色の場合隣のオセロの色を変える 黒色 
    if (!resultLeftSide) { 
      if (prevLeftOthello.match(othelloBlack) && prevOthello.match(othelloWhte)) { 
        let targetIndex = index - 1; 
        putOthello(targetIndex, index); 
      } 
 
      //左隣の次のマスの色が置いたオセロと同じ色の場合隣のオセロの色を変える 白色 
      if (prevLeftOthello.match(othelloWhte) && prevOthello.match(othelloBlack)) { 
        let targetIndex = index - 1; 
        putOthello(targetIndex, index); 
      } 
    } 
 
    //右隣の次のマスの色が置いたオセロと同じ色の場合隣のオセロの色を変える 黒色 
    if (!resultRightSide) { 
      if (nextRightOthello.match(othelloBlack) && nextOthello.match(othelloWhte)) { 
        let targetIndex = index + 1; 
        putOthello(targetIndex, index); 
      } 
 
      //右隣の次のマスの色が置いたオセロと同じ色の場合隣のオセロの色を変える 白色 
      if (nextRightOthello.match(othelloWhte) && nextOthello.match(othelloBlack)) { 
        let targetIndex = index + 1; 
        putOthello(targetIndex, index); 
      } 
    } 
  } 
 
とにかく動くものを・・・と思っていたら処理がカオスな状態に。

初めの取り決めが守れていないと思いつつ、リファクタは一度我慢します。

次に相手のオセロを上下で挟み込んだ場合の処理を加えます。

index.js
//変数の追加 
    let topOthello = $tableElements[index - 8].innerHTML; 
    let upperTopOthello = $tableElements[index - 16].innerHTML; 
    let bottomOthello = $tableElements[index + 8].innerHTML; 
    let lowerBottomOthello = $tableElements[index + 16].innerHTML; 
 
    //相手のオセロを上方向で挟み込んだ場合 黒色 
    if (upperTopOthello.match(othelloBlack) && topOthello.match(othelloWhte)) { 
      let targetIndex = index - 8; 
      putOthello(targetIndex, index); 
    } 
 
    //相手のオセロを上方向で挟み込んだ場合 白色 
    if (upperTopOthello.match(othelloWhte) && topOthello.match(othelloBlack)) { 
      let targetIndex = index - 8; 
      putOthello(targetIndex, index); 
    } 
 
    //相手のオセロを下方向で挟み込んだ場合 黒色 
    if (lowerBottomOthello.match(othelloBlack) && bottomOthello.match(othelloWhte)) { 
      let targetIndex = index + 8; 
      putOthello(targetIndex, index); 
    } 
 
    //相手のオセロを下方向で挟み込んだ場合 白色 
    if (lowerBottomOthello.match(othelloWhte) && bottomOthello.match(othelloBlack)) { 
      let targetIndex = index + 8; 
      putOthello(targetIndex, index); 
    } 
色の変更に関する処理があまりにも肥大化してきたので、あとで切り分けます。

ただ、ここで問題が発生。

上の2行、下2行のどこかをクリックした時、クリックした位置から2つ以上の上の行や下の行が存在しないため「upperTopOthello」や「lowerBottomOthello」それぞれの値が見つからないエラーがでました。

まあ当然ですよね…なので例外処理を付け加えて動くようにしてあげます。

上はindex(選択された場所)の値が二段目16以上、下は七段目48未満でそれぞれの値を取得、変更の処理を以下if文の間に記述していきます。

index.js
if (index > 15) { 
    //ここに相手のオセロを上方向で挟み込んだ場合の処理 
  } 
 
  if (index < 48) { 
    //ここに相手のオセロを下方向で挟み込んだ場合の処理 
  } 
 
最後に挟み込んだ間のオセロを全て色を変更させられるようにしたいと思います。

index.js
//基礎のロジック 
for (var i=0; i < 8; i++) { 
      let target = rowSpot[i][selectNumber]; 
      //選択した以外のマス目をチェック 
      if (target !== index) { 
        //黒か白かの判定 
        if (order) { 
          if ($tableElements[target].innerHTML.match(othelloBlack)) { 
            let othelloChangeNumber = (i - 1) - selectArray; 
            for (var n=0; n < othelloChangeNumber; n++) { 
              let changeTarget = rowSpot[n + othelloChangeNumber][selectNumber]; 
              $tableElements[changeTarget].innerHTML = othelloColor; 
            } 
          } 
        } else { 
          if ($tableElements[target].innerHTML.match(othelloWhte)) { 
 
          } 
        } 
      } 
    } 


おわりに

処理が複雑になりすぎて、命名も長くなってしまって見辛いソースになってしまいました。

リファクタはしたいけど、一旦出して更新していくスタイルにしようかなと思います。

まだまだES6の知識が不安定だったり、JavaScriptのみで書くことに慣れていないためもっと書かないといけないなと感じながらjQueryの偉大さも痛感した取り組みでした。

P.S.随時アップデートしてもっとスマートにしていきます。

コメント

このブログの人気の投稿

投稿時間:2021-06-17 22:08:45 RSSフィード2021-06-17 22:00 分まとめ(2089件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)