たくみん成長日記

開発したアプリやプログラミングの備忘録を不定期に上げていきます。

WebSocketでビンゴ!!

こんちか!!たくみんです!!SLP KBIT 2019 アドベントカレンダーの7日目の記事です。

adventar.org

今年もあと3週間ほどになり、各企業では忘年会が行われることではないでしょうか。よく忘年会で行われるのが景品をかけた抽選会やビンゴ大会です。これらの準備は忘年会の幹事や余興担当者が行うことになりますが、大抵は若者がやる羽目になるのではないでしょうか。ビンゴ大会だったら100均でビンゴカードを買ったり、抽選会だったら抽選カードを自作して、忘年会参加者に配布したり、会社の経費で落とすのであれば書類を書いたり...かなり手間がかかるし仕事との両立も難しいと思います。私もおそらく来年に幹事をすることになるため、どうにか楽をできないかと考えていました。そこで、エンジニアらしくITで楽をしようと思い、Web上で動くビンゴゲームのプロトタイプを作ってみました。

目次

方針

ビンゴ大会をWeb上でする場合は、ビンゴで出てきた値がリアルタイムでみんなのビンゴカードに反映される必要があります。そこで、リアルタイムの双方向通信を実現できるWebSocketを用いてビンゴゲームを作りたいと思います。イメージ的には以下のような感じです。 f:id:takuminv:20191205232842p:plain:w500

WebSocket

WebSocketサーバ

ビンゴガラガラ画面(主催者用)

送信ボタンを押下すると、WebSocketで1〜25の数字が送信されます。このビンゴは1〜25の数字しかありません。しかも、何回も同じ数字が出ます。普通のビンゴではブーイングの嵐ですが、プロトタイプなのでこれでいいのです。

<html>
    <head>
        <title>BingoGaraGara!</title>
        <script type="text/javascript" src="./bingoGaraGara.js"></script>
    </head>
    <body>
        <h1 id="GaraGararesult"></h1>
        <button type="button" id="button1">送信</button>
    </body>
</html>
const connection = new WebSocket("ws:localhost:8000");
window.onload = () =>{

    let button = document.getElementById("sendButton");
    button.addEventListener("click", () => {
        let GaraGara = Math.floor(Math.random() * 25 + 1);
        connection.send(GaraGara);
        result = document.getElementById("GaraGararesult");
        result.textContent = GaraGara;
    });
}

ビンゴカード画面(参加者用)

このビンゴカードは、1〜25が規則正しく並んだものしか作成されません。流石にプロトタイプといえどランダムしないといけないかなと思ったのですが、時間が足りませんでした。処理の流れは以下のようなものになります。

  1. 2次元配列を元にビンゴカードを作成
  2. WebSocketで数字を受け取ると、その数字をXに変更(ビンゴカードを開くイメージ)
  3. ビンゴになっているか判定し、ビンゴになっているとBINGO!と表示
<html>
    <head>
        <title>BingoTable!</title>
        <link rel="stylesheet" type="text/css" href="style.css">
        <script type="text/javascript" src="./bingoTable.js"></script>
    </head>
    <body>
        <h1 id="result"></h1>
        <table border="1" class="bingo" id="bingo-table"></table>
    </body>
</html>
let bingo = [["1","2","3","4","5"],["6","7","8","9","10"],["11","12","X","14","15"],["16","17","18","19","20"],["21","22","23","24","25"]];
 
// ビンゴボード作成
let createBingoTable = () => {
    let bingoTable = document.getElementById("bingo-table");
    for (i = 0; i < bingo.length; i++) {
        tr = document.createElement('tr');
        tr.className = "bingo-tr";
        for (j = 0; j < bingo[i].length; j++) {
            td = document.createElement('td');
            td.textContent = bingo[i][j];
            td.className = "bingo-td";
            td.id = "bingo-td-" + (i * 5 + j + 1);
            tr.appendChild(td);
        }
        bingoTable.appendChild(tr);
    }
}

// ビンゴボード更新
let updateBingoTable = (value) => {
    for (i = 0; i < bingo.length; i++) {
            for (j = 0; j < bingo[i].length; j++) {
                if (bingo[i][j] === value) {
                    let bingoTd = document.getElementById("bingo-td-" + (i * 5 + j + 1));
                    bingoTd.textContent = "X";
                    bingo[i][j] = "X";
                }
            }
        }
}

// ビンゴ判定
let checkBingo = () => {
    if (checkBingoCell(0,0,1,0)) {return true;}
    if (checkBingoCell(0,1,1,0)) {return true;}
    if (checkBingoCell(0,2,1,0)) {return true;}
    if (checkBingoCell(0,3,1,0)) {return true;}
    if (checkBingoCell(0,4,1,0)) {return true;}
    if (checkBingoCell(0,0,0,1)) {return true;}
    if (checkBingoCell(1,0,0,1)) {return true;}
    if (checkBingoCell(2,0,0,1)) {return true;}
    if (checkBingoCell(3,0,0,1)) {return true;}
    if (checkBingoCell(4,0,0,1)) {return true;}
    if (checkBingoCell(0,0,1,1)) {return true;}
    if (checkBingoCell(4,0,-1,1)) {return true;}
    return false;
}

let checkBingoCell = (startX, startY, dx, dy) => {
    count = 0;
    i = startY;
    j = startX;
    while(true) {
        if (i >= 5 || j >= 5) { break;}
        if (bingo[i][j] !== "X") {break;}
        count++;
        i += dy;
        j += dx;
    }

    if (count == 5) { return true;}
    return false;
}

window.onload = () => {

    createBingoTable();

    connection = new WebSocket("ws:localhost:8000");
    connection.onopen = function(e) {console.log("connected")}

    // メッセージを受け取ると該当するボードが開く
    connection.onmessage = function(e) {
        console.log(e.data);
        updateBingoTable(e.data);
        if(checkBingo()) {
            result = document.getElementById("result");
            result.textContent = "BINGO";
        }

    }
}

デモ

f:id:takuminv:20191207011232g:plain

おわりに

プロトタイプということでまだまだ未完成で考慮すべき点が色々あります。完全版にするためには以下のような点を考慮しないといけないのかなと思います。

  • 技術選定
  • そもそものシステム構成
  • Developer Toolでのビンゴカード書き換えの対策
  • ビンゴカードのランダム化
  • ビンゴ部屋的なやつ
  • 参加人数やリーチの人数、ビンゴの人数のリアルタイム表示
  • スマホでの使用を前提としたUI

今はただのJSのみを使用していますが、Reactとか使うのか、Railsといったフレームワークを使うのかなど色々決めないといけないなと思います。 そもそも、急ピッチで作ったため、WebSocketの理解が浅くまだまだわからない点が多いです。このビンゴゲーム作成を通してWebSocketについて理解できたらなと思います。そして、来年の忘年会までに完全版を作成し、ドヤ顔をしたいです。ではでは。

参考にしたサイト

qiita.com

ishiduca.hatenablog.com