Javascriptで外国で有名なゲーム”CONNECT4”を作ってみた


こんにちは、Mottyです。
今回もJavascriptを使ったテーマでやっていきます。

はじめに

Connect4をご存知いらっしゃる方は多いかと思いますが、簡単に申し上げますと四目並べです。

先攻と後攻が交互にボード上に駒を打ち、縦横斜めのいづれかに4目以上並べれば勝ち・・・というシンプルなゲームです。
Javascriptの勉強の題材にはちょうど良いかなと思って、
自分の家にも4目並べがありますのでどなたか一緒に遊びませんか(急なお誘い)

開発の準備

・HTML/CSS/Javascript
・Node.js(デバッグ用)
・Bootstrap4(ボタン用)

描画ソフトはペイント、
開発環境はVisualStudioCodeで行いました。

全部解説すると冗長になるので、作ってみて勉強になった部分だけ載せていきたいと思います。
↓完成版はこんな感じになりました。

盤面の描画

ボードゲーム開発において悩んだのは、盤面をどう扱うか、ということでした。HTMLに直書きしても
よかったのですが、盤面情報を配列に持たせておいてそれを元に画面に描画していく、というような
やり方を行いました。(いわゆるModel-View分離型というやつですかね・・・?)

script.js

    var boardarray = [
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0],
            [0,0,0,0,0,0,0]]


        function load_board(array)
        {
            board.innerHTML = "";
            for (let i = 0; i < array.length; i++) {
                for (let j = 0; j < array[i].length; j++) {
                    if(array[i][j] == 0)
                    {
                        board.appendChild(blank.cloneNode(true))
                    }                    
                    else if(array[i][j] == 1)
                    {
                        board.appendChild(red.cloneNode(true))
                    }
                    else if(array[i][j] == -1)
                    {
                        board.appendChild(yellow.cloneNode(true))
                    }
                }
            board.innerHTML += "<br>";
            }
        }

load_board(array)では、先攻を1、後攻を−1としております。
ユーザーの入力を受け取って適宜盤面を更新し、それを読み込むというロジックでやってますね。

勝利判定

以下、勝利判定の関数です。ユーザーが石打ちを入力するたびに稼働します。
縦横ななめのいずれかが4つ以上並んでいれば良いので、打った位置から周り4方向(実質は8方向?)確認し、
いずれかの方向において自分と同じ石の数が連続して3つ以上あれば、1(先攻)/-1(後攻)、なければ0を返すfunctionにチェックしてもらってます。

Something went wrong

script.js

        function VictorysNumber(boardarr, Row, Col) //ボードと現在行・現在列を引数として代入
            //8方向を見て連続番号を管理する配列に格納していく。
            //格納した配列をもとに、勝利かどうかを判定。
            //→方向が0番、以降45°左回転するごとに番号を1番加算し、合計7番までの8方向を定義する。
            {
            var continuousNum = [0,0,0,0,0,0,0,0]//1,2,3,4,5,6,7,8番方向
            const directionList =[
            [0,1],//0番
            [-1,1],//1
            [-1,0],//2
            [-1,-1],//3
            [0,-1],//4
            [1,-1],//5
            [1,0],//6
            [1,1]] //7

            //スコープははじめ置かれた石と同じ
            var scopeRow = Row;
            var scopeColumn = Col;

        //スコープ開始

        for (let i = 0; i < 8; i++) {   
            //textlog.value += "勝利判定関数の開始。ScopeRow="+scopeRow+ "、scopeColumn = " +scopeColumn +"です。\n";
            //textlog.value += i+"番方向を見ます。";
            while(true)
            {
                //与えられた方向への移動操作
                scopeRow += directionList[i][0];
                scopeColumn += directionList[i][1];
                try
                {
                    if(0 <= scopeRow <= 5 && 0 <= scopeColumn <= 6 &&
                        boardarr[scopeRow][scopeColumn] == boardarr[Row][Col]) //起点と今見ている場所が同じであればカウントを増やす
                    {                       
                        continuousNum[i]++; 
                        //textlog.value += i +"番方向に" + continuousNum[i] + "個の連続した石が発見されました。\n";
                    }
                    else
                    {
                        //textlog.value += i +"番方向には同じ石は見つかりませんでした。\n";
                        break; //値が同じでなければ(連番でなければ)break
                    }
                }
                catch(e)
                {
                    break; //ボードを超えた値を参照するとエラーが出る。飛び越えてもbreak.
                }
            }
            //スコープを元の位置に戻す。
            scopeRow = Row;
            scopeColumn = Col;
        }
                //勝利判定
                if( continuousNum[0] + continuousNum[5] == 3 ||
                continuousNum[1] + continuousNum[6] == 3 ||
                continuousNum[2] + continuousNum[7] == 3 ||
                continuousNum[3] + continuousNum[8] == 3 )
                {
                    return turn; //勝利者決定
                }
                else
                {
                    return 0; //勝負がついていない場合は0を返す。
                }
        }

※これは以前のExcelで五目並べを作ったときとほぼ同じアルゴリズムを採用しております。

終わりに

やはりJavascriptを少しでもいいから勉強すると、View画面作成も怖じ気づかなくなりますね・・・!
今ならなんでもできそうな気がしますが、きっと気のせいで実は色々と奥が深い分野だと思います()

全ソースコードはgithubに載せております。
https://github.com/YukiYamamotty0713/connect4/

参考URL
今回は下記の動画を大変参考にさせていただきました。メモ帳を使って1時間でオセロゲームを
仕上げるという凄腕のプログラマーさんの動画です。
・ニコニコ動画・【プログラミング】オセロを1時間で作ってみた【実況解説】
https://www.nicovideo.jp/watch/sm8391299