Google Homeと○✕ゲームしてみる

# はじめに
[前回記事](https://qiita.com/miso_develop/items/c2061519bb458e22f7d3)にてFirebaseとVue.jsでオンライン○✕ゲームを作ってみました。
今度はそれをActions on Google(Dialogflow)で実装し、Google Homeと○✕ゲームをできるようにしてみました。

DBに状態を持つGoogle Homeアプリのチュートリアル的な感覚で読んで頂ければと思います。



# 処理の流れ
![image](https://qiita-image-store.s3.amazonaws.com/0/207109/ae72e73e-8bab-804c-1619-57a00ea82b93.png)

Google Home、Actions on Googleはあくまでインターフェースなので、主にDialogflow、Firebaseを作り込みます。



# 実際の動作
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Google HomeとOXゲームしてみた <a href="https://t.co/6Dk5dBcjLj">pic.twitter.com/6Dk5dBcjLj</a></p>&mdash; 田中みそ (@miso_develop) <a href="https://twitter.com/miso_develop/status/928651063724425216?ref_src=twsrc%5Etfw">2017年11月9日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

確認のため前回記事で作成したWebアプリにDBの状態を表示させています。
もちろんWebアプリを開いてなくてもGoogle Homeと○✕ゲームできます。


# 設計
まずはどう実装するか考えてみます。
前述したように作り込みが発生するサービスはDialogflow、Firebaseとなり、これらの登場人物(サービス)間での情報のやりとりをシーケンス図にまとめて整理してみました。

アクション単位でまとめると以下の3パターンとなりました。

* ゲーム開始
* 置き場の指定(Put処理)
* ゲーム終了

ちなみにシーケンス図の書き方は以下の記事を参考にさせて頂きました。
[【SequenceDiagrams】シーケンス図の書き方](https://qiita.com/dodomasaki/items/31e2210c07cb58471ac4)

またシーケンス図の作成には以下のWebツールを使用しました。
マークダウンでシーケンス図がかけて、いい感じに出力してくれてとても便利です。
[WebSequenceDiagrams](https://www.websequencediagrams.com/)

## ゲーム開始シーケンス図
Google Homeアプリ開始時に実行されるwelcomeインテントよりFirebase Functionsを実行し、初期化(init)を行います。
初期化時に先攻後攻が決定し、Google Homeが先攻ならそのままGoogle HomeのPut処理も行います。

![start.png](https://qiita-image-store.s3.amazonaws.com/0/207109/680309b4-9223-1e69-3eb0-72cef55a6ea9.png)

## 置き場の指定(Put処理)シーケンス図
上下左右、斜め、真ん中のいずれかをマークする処理です。
呼び方はDialogflowのエンティティにまとめておきます。

userPut関数開始時にDatabaseより状態を取得します。
Put時にDatabase更新と勝敗判定を行い、続けてGoogle HomeのPut処理も行います。
応答としてはGoogle Homeがどこに置いたかを返します。

勝敗判定があれば初期化を行い、勝敗結果と「次のゲームを開始します」と応答します。

![put.png](https://qiita-image-store.s3.amazonaws.com/0/207109/e11ef5c9-c73c-93d2-c291-8ebf6b49b6c6.png)

## ゲーム終了シーケンス図
Google Homeアプリを終了します。
これはDialogflowからの単純な返しだけです。

![end.png](https://qiita-image-store.s3.amazonaws.com/0/207109/fd9898ba-b8d2-3b0a-5596-ecd013480cf2.png)

## 
シーケンス図にまとめた事でDialogflow、Firebaseでそれぞれ何をすべきか明確になりました。
上記の図に沿ってそれぞれの実装を行っていきたいと思います。



# Dialogflowの実装
Dialogflowの実装ではゲーム開始、置き場の指定、ゲーム終了の3つのアクションに対応するインテントの作成が主になります。

ここで行う操作は以下になります。

* Entityの作成
* Intentの作成
* Fulfillmentの設定
* IntegrationsからGoogle Assistant(Google Home)への反映

なお前段としてActions on Googleでのプロジェクト作成とDialogflowでのAgent(プロジェクト)作成があります。
これらは画面をポチポチしていくだけなので、以下の記事を参考にさくっと済ませてみて下さい。

[IFTTTを使わずActions on Google(Dialogflow)でGoogle Homeから家電を音声操作してみる](https://qiita.com/miso_develop/items/28cbfcdfb61d455ce346#actions-on-google%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90)

* Actions on Googleプロジェクトの作成
* Dialogflow Agentの作成

## Entityの作成
「direction」というエンティティを作成しました。
上下左右、斜め、真ん中それぞれの呼び方を定義します。
と言っても基本そのままなので、日本語からコードで読み取りやすい英語への変換が目的になります。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/207109/31f3540c-be76-0db8-08e4-dc46a4f825c9.png)

## Intentの作成
続いてインテントを作成します。
シーケンス図よりアクションは3パターンありました。
それぞれについて対応するインテントを作成します。

### welcomeインテントの作成
Google Homeアプリを起動したとき最初に呼ばれるインテントです。
デフォルトのインテントとして「Default Welcome Intent」があるのでそれをいじっちゃいましょう。

変更点は以下になります。

* Action: 「init」と入力
    * 後ほどFirebase Functionsで作成する初期化関数の名前です
* Text response: からっぽにします
    * responseはFirebase Functionsから返すのここでの指定は不要です
* Fulfillment: 「Use webhook」にチェック
    * Fulfillmentの項目がなかったら、左のメニューからFulfillmentを選びInline EditorをENABLEDにしてSAVEして下さい

![image.png](https://qiita-image-store.s3.amazonaws.com/0/207109/7eeb833a-5a0c-0e9e-ff60-31ef067d5965.png)

### putインテントの作成
置き場の指定時に呼ばれるインテントです。
先ほど作成したdirectionエンティティに反応して呼ばれます。

* User says: 「direction」と入力しエンティティ指定
    * 入力した文字をハイライトし、でてくるポップアップから「@direction」を選択します
        * Actionの下に自動的にパラメータとしても登録されます
* Action: 「userPut」と入力
    * 後ほどFirebase Functionsで作成する置き場指定関数の名前です
* Fulfillment: 「Use webhook」にチェック

![image.png](https://qiita-image-store.s3.amazonaws.com/0/207109/84963807-72e4-7fe1-a446-2a76aa629f79.png)

### endインテントの作成
Google Homeアプリ終了用のインテントを作成します。

* User says: 「ゲーム終了」と入力
* Text response: 「マルバツゲームを終了します」と入力
* Google Assistant: 「End Conversation」にチェック
    * ここにチェックをつけることでインテント呼び出し時にGoogle Homeアプリが終了されます

![image.png](https://qiita-image-store.s3.amazonaws.com/0/207109/9c4f0946-0ea0-8e0e-f0e0-850b1cf53d18.png)

### 
続いてFulfillmentの設定といきたいところですが、ここで指定するWebhookのURLとなるFirebase Functionsを先に作成します。



# Firebase Functions の実装
Firebase Functionsの実装ではDialogflowインテントのActionから呼ばれる関数をマッピングしたり、Dialogflowへレスポンスを返したり、Databaseとのやりとり等、裏側のごちゃごちゃした処理をコーディングします。

## Vue.jsからの移植
今回は、[前回記事](https://qiita.com/miso_develop/items/c2061519bb458e22f7d3)で作成したVue.jsのコードをFirebaseに移植します。
移植にあたり以下の変更を行います。

* Databaseへのアクセスは`firebase.database()`ではなく`require('firebase-admin').database`を使う
* Put処理の引数となる置き場所情報は、putインテントのdirectionパラメータより取得する
* Functionsではasync/awaitが使えなかったため、Database情報の取得は`ref.once()`にコールバックを渡し処理する
* 呼び出した関数の処理の最後にまとめて`response.send()`を返す
    * インテントからのFunctions呼び出しに対し、`response.send()`は一度しか返せないため
* インテントで指定したActionとFunctions関数名のマッピングを行う
* ViewはないのでVue.jsを使わない形に
* 対人戦からGoogle Homeとのコンピュータ戦への変更に伴い、ロビー画面やルーム作成機能を削除
* コンピュータ用の人工無能を作成（空いてる場所にランダムに置くだけ）

また実行環境がHTMLからNode.jsとなるため、以下の外部モジュールは`npm i <module> -save`しておく必要があります。

* firebase-admin
* moment

## ソース
ソースはGithubにプッシュしています。
[miso-develop/ox-game-googlehome/functions/index.js](https://github.com/miso-develop/ox-game-googlehome/blob/master/functions/index.js)

## 
なおローカルプロジェクト作成やデプロイの方法は以下の記事を参考にしてみて下さい。

[Firebase Funcitonsの作成](https://qiita.com/miso_develop/items/28cbfcdfb61d455ce346#firebase-funcitons%E3%81%AE%E4%BD%9C%E6%88%90)

* firebase-toolsの導入
* Firebaseへのデプロイ



# Dialogflow実装の続き
Firebase FunctionsをデプロイしURLが払い出されたらDialogflowに戻ります。
Fulfillment設定、Integrations設定を進めていきます。

## Fulfillmentの設定
Fulfillmentの設定画面にてWebhookをENABLEDに切り替え、Firebase Functionsのデプロイ結果に払い出されたURLを指定しSAVEします。

## Integrationsの設定
「Google Assistant」をクリックし、「Additional triggering intents」にputインテントとendインテントを追加しUPDATEすれば、○✕ゲームの完成です。

![Dialogflow_20171110_215300.png](https://qiita-image-store.s3.amazonaws.com/0/207109/28e31f4e-bba5-c4ca-77b9-2d3e13030011.png)



# 動作確認
DialgflowでのUPDATE後、Actions on GoogleにてTEST DRAFTしてみましょう。
これでActions on GoogleのエミュレータやGoogle Home、またはスマホのGoogle Assistantより実行できます。

同じFirebaseプロジェクトに[前回記事](https://qiita.com/miso_develop/items/c2061519bb458e22f7d3)のWebアプリソースをHostingにデプロイすることで、Google Homeとの○✕ゲームの状態をWeb画面から確認できるようになります。
ちなみに部屋番は「0000」です。

[実際の動作](#実際の動作)の動画はこうして確認を行っています。



# おわりに
本当は対人仕様にして「Google Homeアプリ vs Google Homeアプリ」や、「Google Homeアプリ vs Webアプリ」みたいな動きもさせてみたかったのですが、うまくいかず…

インテントの設定項目にEventsがあり[公式ドキュメント](https://dialogflow.com/docs/events)も試してみたのですが、外部からのキックができそうでできなくて…
GET/POSTでインテントに指定したEventにアクセスすれば起動できそうなのですが…

もし詳しい方いらっしゃいましたらご教示お願い致しますm(_ _)m