【初心者向け】第六回 メモ帳だけでゲーム作ってみた〈オセロ編〉

今回も、前回までと同様にメモ帳だけを使ってオセロを作っていきたいと思います!

前回の記事をまだ読んでない方は是非そちらを先に読んでみて下さい!


今回作るもの

前回は、キーボード入力で駒を置けるようなコードを書きましたね!

  1. 準備編
  2. オセロの盤を表示させよう!
  3. オセロの駒を表示させよう!
  4. キー入力で動かせるようにしよう!
  5. キー入力で駒を置けるようにしよう! ←前回
  6. 駒が裏返るようにしよう! 今回

そして、最終回としておいた駒によって他の駒が裏返るようにして、オセロとして遊べるようにしていきたいと思います!

完成イメージは以下の通りです!

是非最後まで読んで、完成させましょう!


駒を裏返すコードを考える

今回は、実際にコードを書いていく前に、駒を裏返す方法を少し考えてからコードに入っていきたいと思います。

まずは駒を置いたときにチェックしないといけない範囲なんですが、これは簡単で周りの8方です。

オセロでは縦横斜めでひっくりかえすことが出来るので、おいた駒の8方をチェックすればいいですね。

では次に駒をひっくりかえす方法ですが、ここではまず下だけを考えましょう。

駒があるかチェックしなければいけない範囲として、盤面の端っこまでか、何もないマスが出てくるか、おいた駒と同じ色が出てくるの3パターンです。

まず画面の一番左側の場合を考えると、黒を置いたとき下に白い駒があるので、さらにその下を確認すると、空白になっているのでひっくりかえすことが出来ません。

つまり置いても何も起きませんね。

また真ん中の場合もほとんど同様で、駒を置いたら、その下が白なので、さらにその下のマスを確認しますが、そこも白なので、その下のマスを調べて、空白なので何も起きません。

ですが、一番右側の場合は、黒を置いたときに、下が白ですが、さらにその下の駒が黒であることを確認出来たら、一つ上の前のマスに戻って白をひっくりかえすという動作が必要ですね。

これをコードで実現するには、再帰処理というものを用います。

再帰処理というのは、関数が自分の関数を呼び出す処理のことを指します。
(少しわかりやすく説明しているので、少し本来の意味とはずれてきますが、大体はあっています。)

少し下のコードを見ましょう。

let a = 0;
function test() {
  a++;
  test();
}

test();

このコードでは、関数のtest()というのが実行されると、その中でtest()というのが実行されるようになっています。

そしてこれを実行すると、aが無限に増え続けます。

止めない限り一生、aが増えて行ってしまいダメなコードなんですが、再帰とはこういうものです。

そして、今回では、黒の駒が置いたときに、白の駒をひっくりかえすときに再帰をうまく使います。

詳しくは実際にコードを書いていくときに説明します。


コードを書いていこう!

ということで、今回書くコードの難しさが分かりましたでしょうか?

今回のコードは、前回までとは比べ物にならないほど難しいものです。

ですが、ここですべてを理解する必要はありません。

コードを書きながら、「大体こういうものなのかな~」と考えるだけで本当に十分です。

ゆっくり進めていきましょう!


関数を作る

ではまず初めに、今回使う関数を作っていきます。

関数は8つ作ります。

最初に説明した通り、今回は駒を置いたときに8方向をチェックしなければいけないので、その8つの関数がそれにあたります。

また、今回書く関数は、putPiece()という関数の中に書いていきます!

(putPiece()の中に書かなくても正常に動作しますが、関数の中に関数が書くこともできることを知っておいてもいいでしょう!)

function putPiece() {
  if (field[y][x] == "") {
    if (turn == "●") {
      field[y][x] = "●";
    }else if (turn == "〇") {
      field[y][x] = "〇";
    }
  }

  if (turn == "●") {
    turn = "〇"
  }else if (turn == "〇") {
    turn = "●"
  }

  function checkDown(tmp_x, tmp_y) {

  }

  function checkUp(tmp_x, tmp_y) {

  }

  function checkRight(tmp_x, tmp_y) {

  }

  function checkLeft(tmp_x, tmp_y) {

  }

  function checkDownRight(tmp_x, tmp_y) {

  }

  function checkDownLeft(tmp_x, tmp_y) {

  }

  function checkUpRight(tmp_x, tmp_y) {

  }

  function checkUpLeft(tmp_x, tmp_y) {

  }
}

このように8つ作ります。

そして引数が2つあることもチェックですね!

「tmp」というのは「仮の」という意味です。

なので、引数で仮のxとyを使うよという意味で大丈夫です。


駒を置くコードをターンを変えるコードをくっつける

まず初めに、前回書いたコードに修正と追加をします。

function putPiece() {
  if (field[y][x] == "") {
    if (turn == "●") {
      if (checkDown(x, y + 1) == true) {
        field[y][x] = "●";
        turn = "〇";
      }
   }else if (turn == "〇") {
      
    }
  }

  function checkDown(tmp_x, tmp_y) {

  }

  …省略…
}

まず黒の駒のターンを考えます。
(白のターンもほとんど同様ですが、またあとで書きます)

初めに、前回は駒を置くコードと、ターンを変えるif文を分けていましたが、駒を置いたときに同時にターンを変えればいいので、くっつけた感じになっています。

そして、checkDown(x, y + 1)で、下のマスをチェックするようになっていて、checkDownが「true」となるとき(駒が置けるとき)に駒が置かれ、ターンを変えるようにしました。

checkDown次に書いていきますので、まだ少し分かりづらいかもしれませんので、次に進めましょう。


checkDownの処理を書いていく

では、次にcheckDownの処理を書いていきましょう。

下をチェックするときには、

もしチェックする場所が範囲外なら、ひっくりかえされないので「false」を返す
もしチェックする場所に何も置かれていないのなら、ひっくりかえされないので「false」を返す
もしチェックする場所が黒ならば、ひっくりかえすことが可能なので「true」を返す
もしチェックする場所が白ならば、さらにその下を調べて、「true」か「false」を返す

といったように4つに分けなければなりません。

また、最後のチェックする場所が白ならば、さらにその下を調べないといけないので、ここで再帰処理の出番です!

実際にコードを書いてみますので、どこがどの様に対応しているのかを考えてみましょう。

function checkDown(tmp_x, tmp_y) {
  if (tmp_y >= 8) {
    return false;
  }else if (field[tmp_y][tmp_x] == "") {
    return false;
  }else if (field[tmp_y][tmp_x] == "●") {
    return true;
  }else {
    if (checkDown(tmp_x, tmp_y + 1) == true) {
      field[tmp_y][tmp_x] = "●";
      return true;
    }else {
      return false;
    }
  }
}

まず初めのif文では、下をチェックしているのでy座標が画面外であるかだけを調べています。

そして二つ目では空欄の場合、三つ目では黒がすでに置かれている場合の処理になっています。

そして最後のelseでは、それ以外の場合、つまり白が置かれている場合の処理で、再帰処理が使われています。

難しいですが、まずはそれぞれがどの様な意味があるのかを考えてみるとわかるかもしれません。


白黒どっちの色にも対応させる

では次に、先ほどのコードでは黒だけだったのを、引数を一つ追加して白にも対応させます。

function putPiece() {
  if (field[y][x] == "") {
    if (turn == "●") {
      if (checkDown(x, y + 1, "●") == true) {
        field[y][x] = "●";
        turn = "〇";
      }
   }else if (turn == "〇") {
      if (checkDown(x, y + 1, "◯") == true) {
        field[y][x] = "◯";
        turn = "●";
      } 
    }
  }

  function checkDown(tmp_x, tmp_y, color) {
    if (tmp_y >= 8) {
      return false;
    }else if (field[tmp_y][tmp_x] == "") {
      return false;
    }else if (field[tmp_y][tmp_x] == color) {
      return true;
    }else {
      if (checkDown(tmp_x, tmp_y + 1, color) == true) {
        field[tmp_y][tmp_x] = color;
        return true;
      }else {
        return false;
      }
    }
  }

  …省略…
}

このように、引数に駒を追加することで両方に対応させることが出来ますね!


他の7方向にも対応させる

では最後に他の7方向にも対応させましょう。

基本的にはif文を追加して、対応するxとyでチェックします。

function putPiece() {
  if (field[y][x] == "") {
    if (turn == "●") {
      if (checkDown(x, y + 1, "●") == true) {
        field[y][x] = "●";
        turn = "〇";
      }
      if (checkUp(x, y - 1, "●") == true) {
        field[y][x] = "●";
        turn = "〇";
      }
      if (checkRight(x + 1, y, "●") == true) {
        field[y][x] = "●";
        turn = "〇";
      }
      if (checkLeft(x - 1, y, "●") == true) {
        field[y][x] = "●";
        turn = "〇";
      }
      if (checkDownRight(x + 1, y + 1, "●") == true) {
        field[y][x] = "●";
        turn = "〇";
      }
      if (checkDownLeft(x - 1, y + 1, "●") == true) {
        field[y][x] = "●";
        turn = "〇";
      }
      if (checkUpRight(x + 1, y - 1, "●") == true) {
        field[y][x] = "●";
        turn = "〇";
      }
      if (checkUpLeft(x - 1, y - 1, "●") == true) {
        field[y][x] = "●";
        turn = "〇";
      }
    }else if (turn == "〇") {
      if (checkDown(x, y + 1, "〇") == true) {
        field[y][x] = "〇";
        turn = "●";
      }
      if (checkUp(x, y - 1, "〇") == true) {
        field[y][x] = "〇";
        turn = "●";
      }
      if (checkRight(x + 1, y, "〇") == true) {
        field[y][x] = "〇";
        turn = "●";
      }
      if (checkLeft(x - 1, y, "〇") == true) {
        field[y][x] = "〇";
        turn = "●";
      }
      if (checkDownRight(x + 1, y + 1, "〇") == true) {
        field[y][x] = "〇";
        turn = "●";
      }
      if (checkDownLeft(x - 1, y + 1, "〇") == true) {
        field[y][x] = "〇";
        turn = "●";
      }
      if (checkUpRight(x + 1, y - 1, "〇") == true) {
        field[y][x] = "〇";
        turn = "●";
      }
      if (checkUpLeft(x - 1, y - 1, "〇") == true) {
        field[y][x] = "〇";
        turn = "●";
      }
    }
  }

  function checkDown(tmp_x, tmp_y, color) {
    if (tmp_y >= 8) {
      return false;
    }else if (field[tmp_y][tmp_x] == "") {
      return false;
    }else if (field[tmp_y][tmp_x] == color) {
      return true;
    }else {
      if (checkDown(tmp_x, tmp_y + 1, color) == true) {
        field[tmp_y][tmp_x] = color;
        return true;
      }else {
        return false;
      }
    }
  }

  function checkUp(tmp_x, tmp_y, color) {
    if (tmp_y < 0) {
      return false;
    }else if (field[tmp_y][tmp_x] == "") {
      return false;
    }else if (field[tmp_y][tmp_x] == color) {
      return true;
    }else {
      if (checkUp(tmp_x, tmp_y - 1, color) == true) {
        field[tmp_y][tmp_x] = color;
        return true;
      }else {
        return false;
      }
    }
  }

  function checkRight(tmp_x, tmp_y, color) {
    if (tmp_x >= 8) {
      return false;
    }else if (field[tmp_y][tmp_x] == "") {
      return false;
    }else if (field[tmp_y][tmp_x] == color) {
      return true;
    }else {
      if (checkRight(tmp_x + 1, tmp_y, color) == true) {
        field[tmp_y][tmp_x] = color;
        return true;
      }else {
        return false;
      }
    }
  }

  function checkLeft(tmp_x, tmp_y, color) {
    if (tmp_x < 0) {
      return false;
    }else if (field[tmp_y][tmp_x] == "") {
      return false;
    }else if (field[tmp_y][tmp_x] == color) {
      return true;
    }else {
      if (checkLeft(tmp_x - 1, tmp_y, color) == true) {
        field[tmp_y][tmp_x] = color;
        return true;
      }else {
        return false;
      }
    }
  }

  function checkDownRight(tmp_x, tmp_y, color) {
    if (tmp_y >= 8 || tmp_x >= 8) {
      return false;
    }else if (field[tmp_y][tmp_x] == "") {
      return false;
    }else if (field[tmp_y][tmp_x] == color) {
      return true;
    }else {
      if (checkDownRight(tmp_x + 1, tmp_y + 1, color) == true) {
        field[tmp_y][tmp_x] = color;
        return true;
      }else {
        return false;
      }
    }
  }

  function checkDownLeft(tmp_x, tmp_y, color) {
    if (tmp_y >= 8 || tmp_x < 0) {
      return false;
    }else if (field[tmp_y][tmp_x] == "") {
      return false;
    }else if (field[tmp_y][tmp_x] == color) {
      return true;
    }else {
      if (checkDownLeft(tmp_x - 1, tmp_y + 1, color) == true) {
        field[tmp_y][tmp_x] = color;
        return true;
      }else {
        return false;
      }
    }
  }

  function checkUpRight(tmp_x, tmp_y, color) {
    if (tmp_y < 0 || tmp_x >= 8) {
      return false;
    }else if (field[tmp_y][tmp_x] == "") {
      return false;
    }else if (field[tmp_y][tmp_x] == color) {
      return true;
    }else {
      if (checkUpRight(tmp_x + 1, tmp_y - 1, color) == true) {
        field[tmp_y][tmp_x] = color;
        return true;
      }else {
        return false;
      }
    }
  }

  function checkUpLeft(tmp_x, tmp_y, color) {
    if (tmp_y < 0 || tmp_x < 0) {
      return false;
    }else if (field[tmp_y][tmp_x] == "") {
      return false;
    }else if (field[tmp_y][tmp_x] == color) {
      return true;
    }else {
      if (checkUpLeft(tmp_x - 1, tmp_y - 1, color) == true) {
        field[tmp_y][tmp_x] = color;
        return true;
      }else {
        return false;
      }
    }
  }
}

非常に長いですが、{}の位置や数字を間違えないようにしましょう!

これで完成です!

実際にオセロで遊んでみましょう!


うまくいかなかった方向け

うまくいかなかった方は、まずエラーが出ていないか確認しましょう!

エラーの確認方法については、別の記事で詳しくまとめたので、ぜひそちらを参考にしてください!

それでもうまくいかなければ、下のサンプルコードをダウンロードして、自分のコードを比較してみましょう!


完成!

ということで、これで「オセロ」の完成です!

オセロとして十分に遊べるゲームが出来ましたね!

お友達と是非遊んでみて下さい!

また、オセロとしてもっと遊びやすく自分で修正しても面白いかもしれませんね!

是非ご感想などあればTwitterのほうでお願いいたします!

というわけで長くなってしまいましたが、

最後まで読んでいただきありがとうございました!

このブログでは、他にも初心者向けのプログラミングの記事やゲーム紹介の記事を書いていますので、良ければ是非読んでみて下さい!

おすすめ記事: