読者です 読者をやめる 読者になる 読者になる

name_untitledのエッセイ

IT関係で知ったことなどを記事としてあげていきます。内容に不備や質問などあればぜひコメントをよせてください。

FirebaseでトランプのドボンのWebアプリを作る。(カードを配る)

色々と手を出し過ぎなんだけど、今FirebaseでWebアプリを作ろうとしている。トランプのドボンが出来るアプリ。

ステップはこんな感じと思う。

1.FirebaseのWebアプリのサンプルの確認

2.Firebaseから手札のデータを取得して表示する

3.選んだ手札をFirebaseDBに登録する

4.新しいゲームのボタンを押したら、カードをランダムに配る

5.順番に手札を台札に乗せる

6.手札が出せる札かチェックする

7.得点計算をする

 

ちょっとずつ更新する予定

1.FirebaseのWebアプリのサンプルの確認

 

Firebaseのチャットアプリのサンプルの動作や作りを一通り眺める。

Firebase Web Codelab

 

2.Firebaseから手札のデータを取得して表示する

これはコードを眺めて対応するところをコピペした。

Firebaseの方で手札データを登録して、それを表示させる。

 

3.選んだ手札をFirebaseに登録する

ここで、bindしないとつらそうな気がしたので、ちょっとググる

firebase.googleblog.com

これを参考にVueを導入してみた。まずは、クライアントでrequireを使うので、browserifyが必要らしい。

>npm install -g browserify

>npm install --save promise-polyfill

>browserify ./public/scripts/main.js -o ./public/scripts/bundle.js

>firebase serve

で、これ以降はmain.jsではなく、browserifyで生成したbundle.jsをindex.htmlで読み込んで使う。

arata.hatenadiary.com

動かしてみたがエラー。メッセージからすると、エレメントが見つからないからエラー。2行目は分かりにくいけどたぶん関係ない。

[Vue warn]: Cannot find element: #cards

[Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
(found in <Root>)

stackoverflow.com

確認して、bundle.jsを作り直して、もう一度表示。

bundle.js:2302 [Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build. (found in <Root>) 

やっぱりまだエラー。

これVueのバージョンアップに絡んでいそうなので、ちょっとここでいったんストップ。同じ感じで表示だけ行う処理を組み込んだ。

f:id:name_untitled:20170414151028p:plain

 

2017/04/23 更新

Vueはちょっとお預け。

データはまずは以下の形で作っている自分のカードを表示して、こんな感じにした。

root

┣messages
┣current

┃┣mark

┃┗num

┗cards

 ┗id

┣mark

 ┃┗num

 ┣id

 

手札を出す=cardsから1つ削除&currentを更新。

main.js →browserify → bundle.js

this.cardsRef.child(key).remove().then(function() {
    // Clear message text field and SEND button state.
    Dobon.resetMaterialTextfield(this.messageInput);
    this.toggleButton();
    }.bind(this)).catch(function(error) {
        console.error('Error writing new message to Firebase Database', error);
});

このコードでカードデータを削除。

Dobon.prototype.loadCards = function() {
    // // Reference to the /messages/ database path.
    this.cardsRef = this.database.ref('cards');

    // Make sure we remove all previous listeners.
    this.cardsRef.off();

    var remCard = function(data) {
        var val = data.val();
        var elm = document.getElementById(data.key);
        elm.parentNode.removeChild(elm);
    }.bind(this);
    // Loads the last 12 messages and listen for new ones.
    var setCard = function(data) {
        var val = data.val();
        this.displayCard(data.key, val.mark, val.num);
    }.bind(this);
    this.cardsRef.limitToLast(12).on('child_added', setCard);
    this.cardsRef.limitToLast(12).on('child_changed', setCard);
    this.cardsRef.limitToLast(12).on('child_removed', remCard);
};

上記はDB変更時のイベントの設定。元々のサンプルはchild_added, child_changedだけだけど、child_removedを追加。

 

次に、FirebaseのFunctionを追加する。

index.js

exports.makeUppercase = functions.database.ref('/cards/{pushId}')
    .onWrite(event => {

    // Only edit data when it is deleted
    if *1
        || event.data.exists()) {
        console.log('end trigger');
        return;
    }
    // Grab the current value of what was written to the Realtime Database.
    var mark = event.data.previous.val().mark;
    var num = event.data.previous.val().num;
    event.data.ref.parent.parent.child('current').child('mark').set(mark);
    return event.data.ref.parent.parent.child('current').child('num').set(num);
});

previousにあって、今のデータにない=削除されたデータ。

で、上記の処理でノードを更新。

Realtime Database Triggers  |  Firebase

このサイトがとても詳しく書かれていてありがたい。

Read and Write Data on the Web  |  Firebase

 

2017/04/24 追記

4.新しいゲームのボタンを押したら、カードをランダムに配る

カードデータを作成して、ランダムにシャッフルする処理は以下のような感じ。

ランダムに並べ替えるアルゴリズムがあるそうで助かりました。

h2ham.net

このサイトが見ててきれい。

Fisher–Yates Shuffle

function getCards(){
    var marks = ["spade","clover","heart","diamond"];
    var cards = [];
    for(var i = 0; i < marks.length; i++){
        for(var j = 1; j <= 13; j++){
            var node = {"mark":marks[i], "num": j};
            cards.push(node);
        }
    }
    return cards;
}
function shuffle(array) {
    var n = array.length, t, i;

    while (n) {
        i = Math.floor(Math.random() * n--);
        t = array[n];
        array[n] = array[i];
        array[i] = t;
    }

    return array;
}

var test_array = getCards();
var cards =shuffle(test_array);
console.log(cards.length);
console.log(cards);

 2017/04/25 追記

4.新しいゲームのボタンを押したら、カードをランダムに配る

の続き。

Firebaseで手札を配る際に、RESTでGETしたら手札を配るようにしたいため、FirebaseのFunctionsという機能を利用する。これはREST APIっぽいことが出来るみたい。index.jsでの書き方はこんな感じ。

exports.date = functions.https.onRequest((req, res) => {
 
// ...
});

ここで、dataがHTTPのTriggerの名前。reqからJSONデータを取ることもできるらしい。詳しくは公式をご覧ください。

HTTP Triggersの書き方はこんな感じ。

HTTP Triggers  |  Firebase

Databaseへの登録の書き方はこんな感じ。

Read and Write Data on the Web  |  Firebase

var functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

 

exports.newgame = functions.https.onRequest((req, res) => {
    var getCards = function(){
        var marks = ["spade","clover","heart","diamond"];
        var cards = [];
        for(var i = 0; i < marks.length; i++){
            for(var j = 1; j <= 13; j++){
                var node = {"mark":marks[i], "num": j};
                cards.push(node);
            }
        }
        return cards;
    };
    var shuffle = function(array) {
        var n = array.length, t, i;

        while (n) {
            i = Math.floor(Math.random() * n--);
            t = array[n];
            array[n] = array[i];
            array[i] = t;
        }
        return array;
    };
    var newGame = function(cards){
        for(var i = 0; i < cards.length; i++){
            // A card entry.
            var cardData = {
                mark: cards[i].mark,
                num: cards[i].num
            };

            var newCardKey = admin.database().ref().child('cards').push().key;

            var updates = {};
            updates['/cards/' + newCardKey] = cardData;
            admin.database().ref().update(updates);
        }
    };
    newGame(shuffle(getCards()));
    res.send(200);
});

JSっぽくない書き方になってるのでどう直したらいいか教えてください!

これをdeployして、GETリクエストを投げたらこんな感じでデータが出来ました。

f:id:name_untitled:20170426004141p:plain

うれしい、、、。

 

2017/04/27 追記

カードを配る前に今のカードを全部削除する処理を追加した。

上の処理を1行追加してこんな感じ。

admin.database().ref().child('cards').remove();
newGame(shuffle(getCards()));
res.send(200);

removeで配下のものを全部削除してくれるらしい。

*1:!event.data.previous.exists(