この記事では、Ruby on Railsを使用して、Alexaによる音声認識で操作することができる3×3のスライドパズル作成についてかきました。
Ruby on Railsや、今回サーバーの実装として利用したngrokの導入方法についての説明は省いておりますのでご了承ください。
今回、技術的説明は極力省き、実装面のみの記事としているため、主な技術要件の参考記事は以下を参照ください。
-
Alexaコンソールについて
Alexaコンソール(Alexaスキル)では、どのような情報をサーバー(Ruby on Rails側)にPOSTするのか書き込みます。
クリスマスプレゼントに、Amazon Echoはいかが?〜Alexaスキルを自作してみよう〜 - Qiita -
ActionCableについて
ActionCableは、Railsに比較的新しく備え付けられた双方向通信を実装するための方法です。
最短で作るActionCableチャットアプリ - Qiita -
ngrokについて
こんなの説明いらんやろ!と言われそうですが一応。。
【3分で出来る】ngrokでデプロイをしてみよう! - Qiita
#ngrokを利用するための下準備
config.hosts << '.ngrok.io'
上記を追加し、ngrokによるホスティングを許可してあげればOK
#ActionCableを利用するための下準備
これは簡単です。
$ rails g channel talks
このコマンドで、WebSocketに関するファイルが生成されます。
今回はコントローラー名も一括してtalksにしています。
class TalksChannel < ApplicationCable::Channel
def subscribed
stream_from "some_channel"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def speak
end
end
some_channelというケーブル名にしましょう。
#Railsのルーティング設定
以下を記載します。
Rails.application.routes.draw do
root 'talks#index'
resources :talks, only: [:create, :index]
end
カレントディレクトリをプロジェクト内にし、以下のコマンドでルーティングを確認できます。
$ rails routes
POST /talks(.:format)
Alexaに話しかけた内容は、エンドポイントで指定されたURLにPOSTで送られます。/talksをAlexaコンソール上でエンドポイントに設定します。
続いて、コントローラーです。
class TalksController < ApplicationController
skip_before_action :verify_authenticity_token
# POST /talks
def create
request = AlexaRubykit::build_request(params)
#paramsはAlexaから送られるJSONデータ
request_word = request.slots[:message][:value]
response = AlexaRubykit::Response.new
response.add_speech("#{request.slots[:message][:value]}に移動します.")
message = @@board.slide(request_word)
ActionCable.server.broadcast("some_channel",message)
render json: response.build_response
end
def index
@@board = Board.new
end
end
#0は空白
class Board
def initialize
@@panels = [
[1,3,6],
[0,7,2],
[5,8,4]
]
end
#入れ替える要素の番号を引数として配列で受け取る index → [j,i]
def swap(index1,index2)
tmp = @@panels[index1[0]][index1[1]]
@@panels[index1[0]][index1[1]] = @@panels[index2[0]][index2[1]]
@@panels[index2[0]][index2[1]] = tmp
end
def slide(dir)
brank_index = Array.new(2)
@@panels.each_with_index do |panel,j|
i = panel.find_index { |value| value == 0}
if i != nil
brank_index = [j,i]
break
end
end
case dir
when '上', 'ウェイ'
if brank_index[0] != 2
self.swap(brank_index,[brank_index[0]+1,brank_index[1]])
end
when '下'
if brank_index[0] != 0
self.swap(brank_index,[brank_index[0]-1,brank_index[1]])
end
when '左'
if brank_index[1] != 2
self.swap(brank_index,[brank_index[0],brank_index[1]+1])
end
when '右'
if brank_index[1] != 0
self.swap(brank_index,[brank_index[0],brank_index[1]-1])
end
end
@@panels
end
end
gem 'alexa_rubykit', '1.3.1'
一気に長いコードが出てきたので少し説明します。
まず、Boardクラスは、初期化やAlexaからのメッセージによって盤面を移動させたりする関数(slide())から構成されています。
request = AlexaRubykit::build_request(params)
request_word = request.slots[:message][:value]
この部分では、Alexaから送られたJSONがparamsに入っており、AlexaRubyKitで受け取り、目的とする単語(今回の場合はスライドパズルなので「上」や「下」)をrequest_wordに渡します。
response = AlexaRubykit::Response.new
response = AlexaRubykit::Response.new
response.add_speech("#{request.slots[:message][:value]}に移動します.")
~~
render json: response.build_response
この部分では、Alexaに応答させる言葉をadd_speechに持たせて、renderでAlexaに送っています。
message = @@board.slide(request_word)
ActionCable.server.broadcast("some_channel",message)
Alexaから受け取った言葉をslide関数に渡して、盤面を更新していきます。そして更新された盤面をsome_channelというActionCableを使ってクライアント側にブチ飛ばしていますね。
#クライアント側での処理
先ほどブチ飛ばした内容は、talks_channel.jsで受け取ります。
import consumer from "./consumer"
consumer.subscriptions.create("TalksChannel", {
connected() {
// Called when the subscription is ready for use on the server
},
disconnected() {
// Called when the subscription has been terminated by the server
},
received(data) {
// Called when there's incoming data on the websocket for this channel
let txt = '';
data.forEach((value) => {
txt += '<div class="line">';
value.forEach((e) => {
if (e == 0){
txt += '<div class="empty-box" ></div>';
}else{
txt += '<div class="box" ><p>' + e + '</p></div>';
};
});
txt += '</div>'
});
document.querySelector("#display").innerHTML = txt;
console.log(txt);
},
speak: function() {
return this.perform('speak');
}
});
こんな感じで、クライアント側のjsをいじると、画面を更新することなくスライドパズルが動くというわけです。
あとはHTMLとCSSを適当にいじって完成です。
なんとも春らしい見た目。
#最後に
AlexaRubyKitってなかなか日本語のドキュメントもないしかなり苦戦したので、これからAlexaを使ってアプリケーションを作ろうとする方の手助けになればいいなと思います。
ActionCableは筆者的に今熱いのでどんどんプロダクトに使って行きたいですね。
これからも励んでいきますので、よろしくお願いします。