kun432's blog

Alexaなどスマートスピーカーの話題中心に、Voiceflowの日本語情報を発信してます。たまにAWSやkubernetesなど。

〜スマートスピーカーやVoiceflowの記事は右メニューのカテゴリからどうぞ。〜

Voiceflow TIPS #5 重複しないランダムな数字を取得したい

引き続きVoiceflow TIPSです。

ランダムな数字を取得したい、かつ重複しないようにしたいことってありますよね?例えばクイズスキルとかでランダムな問題を重複しないように、というようなケースが考えられます。これをVoiceflowでやってみました。

全体像

全体はこんな感じです。 f:id:kun432:20190806010457p:plain

変数の作成

まず変数を作成します。ランダムな数字をセッション跨いで必要とするケースはそんなにない気がしたので、今回は TIPS #3で紹介したフロー変数を使ってみました。

f:id:kun432:20190806010449p:plain

変数名 説明
arrRndNum ランダムな数字郡を保持しておくための配列です
startNum ランダムな数字の最小値。ユーザが定義する。
endNum ランダムな数字の最大値。ユーザが定義する。
rndNum ランダムな数字が一つ入ります

例えば、0〜9からランダムな数字が取得したければ、startNumが0、endNumが9というような感じになります。

① Set Blockでユーザが定義する値を初期化

f:id:kun432:20190806012137p:plain

ここでstartNumとendNumを初期化します。別に後ろのCode Blockで初期化してもいいのですが、コードを直接編集せずに、ユーザが定義するところだけはわかりやすいSet Blockを使いました。例では1〜10でランダムな値を取得する感じですね。

② Code Blockで配列などの初期化

f:id:kun432:20190806012319p:plain

ここはコードそのものです。ちょっと見えにくいので以下にコードを貼ります。

var arrRndNum = [];
var rndNum = 0;

for (var i = startNum; i <= endNum; i++) {
  arrRndNum.push(i);
}

for (var j = arrRndNum.length -1; j > 0; j--) {
  var r = Math.floor(Math.random() * (j + 1));
  var tmp = arrRndNum[j];
  arrRndNum[j] = arrRndNum[r];
  arrRndNum[r] = tmp;
}

中身はこんな感じです。

  • 最初に、arrRndNumとrndNumを初期化しておきます。
  • 最初のforでユーザが定義したstartNumとendNumからarrRndNumに数字を入れていって配列をつくります。
  • 2番めのforでarrRndNumをシャッフルして配列内の数字をランダムに組み替えます。Fisher–Yatesアルゴリズムを使ってみました。

必要なときに都度ランダムを引いてもよいのですが、ここでは初期化の一環としてランダムな配列を作っておきました。ここまでが初期化です。

③ Code Blockで配列から数字を取得

f:id:kun432:20190806013240p:plain

ここからがメインのフローになります。arrRndNumから数字を一つ取り除いてrndNumに入れます。②でarrRndNumが既にランダムになっているので、単に一つpopするだけでランダムな数字が得られます。もちろん、ここでランダムにとるようにしてもよいと思います。

コードも貼っときます。

if(arrRndNum.length > 0){
  rndNum = arrRndNum.pop();
}else{
  throw new Error("配列が空です");
}

なお、pop()で取得した場合、配列が既に空になっていると0を返すので、ここでは最初に配列内の要素数をチェックして1以上でなければ、エラーをthrowするようにしました。こうしておくとCode Blockのfailに流れるのでわかりやすいです。

④ Speak Blockでランダムに取得した数字をAlexaに発話させる

f:id:kun432:20190806013950p:plain

ここでAlexaにランダムに取得した数字を発話させます。ついでにもう一度引く場合の質問も投げかけています。

⑤⑥⑦ Interaction Blockで繰り返しの確認

f:id:kun432:20190806014234p:plain

Interaction Blockで繰り返すかどうかを「はい」「いいえ」でユーザに答えさせます。Choice Block使ってもよいのですが、「はい」「いいえ」はYesIntent/NoIntentがありますので、ビルトインされているInteraction Blockを使ったほうが確実ですね。

ここで「はい」の場合は、1のフローに流れて、③に戻って再度ランダムな数字を引き直します。

f:id:kun432:20190806015249p:plain

「いいえ」の場合は、2のフローに流れて、終了のメッセージを発話して終わります。

f:id:kun432:20190806015536p:plain

「はい」「いいえ」以外の場合は、Elseに流れて、再度質問し直します。

f:id:kun432:20190806015550p:plain

⑧ 配列が空になった場合

f:id:kun432:20190806015816p:plain

③で書いたように、ランダムな数値を保持している配列arrRndNumが空っぽになった場合はエラーになりFailに流れるので、ここでもう配列が空だということを告げて終わります。ケースによってはここで再度②に流して、配列を初期化してもよいかもしれませんね。

これで一通り完了です。

テスト

2回実行した結果を以下に貼っときます。

  • 毎回、順番が違うこと
  • 同じ数字が1回のセッションで選ばれていないこと

を見ていただければわかるかと思います。

f:id:kun432:20190806021442p:plain


今回、以下の記事を参考に、少しコードの書き方などは修正させていただきました。Thanks! > Mark!

Markさんの他の記事にもいろいろVoiceflowのTIPSなどありますのでぜひ御覧ください(英語)

是非ご活用下さい〜