Power Virtual Agetns と Power Automate で神経衰弱ゲームを作る③

#はじめに

Teams版のPower Virtual AgetnsとPower Automateを利用して、作成した神経衰弱ゲームの解説その２になります。
[「その１（初期処理）はこちら」](https://qiita.com/MiyakeMito/items/fee22871ffecb287f5b5)
[「その２（ループトピック）はこちら」](https://qiita.com/MiyakeMito/items/f93dd970f40e2ecca0f8)

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Power Platform 2022 release wave1 で Power Automate と Power Virtual Agetns がより便利で面白くなりそうで嬉しい！<br>嬉しさ余って、娘にも遊んで欲しくて神経衰弱を可愛くしてみました。<a href="https://twitter.com/hashtag/PowerApps?src=hash&amp;ref_src=twsrc%5Etfw">#PowerApps</a> <a href="https://twitter.com/hashtag/PowerAutomate?src=hash&amp;ref_src=twsrc%5Etfw">#PowerAutomate</a> <a href="https://twitter.com/hashtag/PowerVirtualAgents?src=hash&amp;ref_src=twsrc%5Etfw">#PowerVirtualAgents</a> <a href="https://t.co/a80RnwfaCa">pic.twitter.com/a80RnwfaCa</a></p>&mdash; Miyake Mito (@MiTo60448639) <a href="https://twitter.com/MiTo60448639/status/1486277992095485953?ref_src=twsrc%5Etfw">January 26, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#フロー「神経衰弱処理」

今回は神経衰弱のメイン処理となる、神経衰弱処理フローについて説明します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/9be324e6-cd31-163f-2942-936da062007a.png)

##全体図

このフローは、神経衰弱の1ターンである1手目と2手目のそれぞれで、共通で呼び出されるフローとなります。
よって、2手目の処理では、最後にめくったカードが1手目と同じか判断を行います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/069192ed-19ff-f3d5-6207-472c3e163cab.png)


###１．フローのパラメータ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/03ea713a-cf35-d221-7a42-4926b5dd7f69.png)
① めくったカードの値。1手目と2手目で異なります。

② 初期処理にて生成されたランダムな数字配列

③ 場のカード。1手目と2手目で異なります。

④ 1手目でめくったカードの値。1手目処理のときは"null"を設定します。その後、当フローでアウトプットされた値を、2手目でインプットとします。

⑤ 1手目をめくる前の場のカード。当フローを呼び出すトピックA、およびトピックBのインプットである場のカードの値です。ハズレだった場合にめくったカードを戻すためにインプットとします。

###２．場のカードの機種依存文字を通常文字に変換
チャットボットでの見栄えをよくするために、チャットボットでの表示には機種依存文字を多用します。
機種依存文字は、Power Automate にて substring() で文字長が2となる場合があります。（サロゲートペアは2と予想しています）
以後の文字位置の算出処理等を複雑化しないために、フローの先頭で文字長が1の文字に置換し、フローの後半で戻します。
当初はトランプをイメージして作成していたため、1~13 の文字を Ａ,②~⑩,Ｊ,Ｑ,Ｋ の文字に置き換えています。

変換前
🔳🐭🐞🐯🐔🐻🐼🐟🐨🐶🐷🐸🐹🐛

変換後
 ■ Ａ ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ Ｊ Ｑ Ｋ 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/3f0d8942-75aa-cf2a-2300-f4bb46e6306e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/8799d784-96e3-da75-60f8-f043f556a61e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/714c53de-bc6e-f0d6-507f-b86c58334954.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/ace0c5ee-cc43-4f79-9515-682f619f15e0.png)

最後に、1行目（列番号を表示）もまるっと置換します。

```
replace(outputs('特殊文字を変換_14'), '🔲 ❶ ❷ ❸ ❹ ❺ ❻ ❼ ❽ ❾', '▯１２３４５６７８９')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/0b9e6973-e454-a72d-32ad-f10b162b5ccd.png)

###３．ランダムな数字配列を、数字のarrayに変換
次にランダムな数字配列を、カンマ区切りの横並びの状態からJSON配列に変換します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/a428d23d-a1a5-913e-e0af-dc180bb349e1.png)

split() で一発です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/78e03e2c-fefe-ef86-f8d0-8b6bff8bbe3e.png)

###４．場のカードにおける、Inputの「めくったカード」の位置を特定

場のカードは文字列として扱います。

今回めくったカードは、行⇒列の番号で指定します。
(下図の黄色いセルは「3の6」）

指定された行列に対する、場のカードの位置を取得します。
(下図の黄色いセルは「43」）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/ead89ab3-f06b-7e4e-d625-f27daa0bdea7.png)


めくったカードの列を X とします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/bdb1d548-117b-37d8-4830-486ac999b77c.png)

めくったカードの行を Y とします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/399b66b9-abd5-4435-c423-78ca74eca5b5.png)

行番号や改行を加味し、場のカードにおけるめくったカードの位置を求めます。

　　X+1+Y×12

で取得可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/95d1e93a-aa1e-2ba0-6110-b096216e0d29.png)


###５．ランダムな数字配列における、「めくったカード」に該当する数字を特定

ランダムな数字配列は、上記でJSON配列に変換しました。そのJSON配列の添え字と、めくったカードの関係は下図になります。
（めくったカードが「36」ならば、JSON配列の添え字は「24」）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/016f5994-520d-2cf3-9a97-d53716dcbe72.png)

上記のJSON配列の添え字は、

　　X+(Y-1)×9-1

で求められます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/3675df12-01df-f9df-511c-0d7823e4d0f7.png)

この添え字を、JSON配列化したランダムな数字配列に指定すると、ランダムな数字配列における数字が取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/7e15af5e-47b0-8156-5c34-3ff4435e8a15.png)

ランダムな数字は1~54で構成されています。
13で除算した剰余が、トランプのA~Kに相当します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/9a2f0620-03cf-7f2c-fcbb-e0af0f9e9e25.png)

剰余は10~12の2桁数字も含むため、そのまま場のカードに挿入してしまうと桁ズレが起きます。
一桁の数字
　　■■■■■　⇒　■■■8■
は問題ありませんが、二桁の数字
　　■■■■■　⇒　■■■12■
は桁ズレが発生します。

よって、ここでも Ａ,②~⑩,Ｊ,Ｑ,Ｋ への置換を行います。

文字の置換は、スイッチアクションで可能ですが、フローが横に広がり見難くなるため、

[Hiroさん](https://twitter.com/mofumofu_dance)の[「横長なSwitchアクション・条件分岐はJSON Dictionaryですっきり短く」](https://qiita.com/h-nagao/items/116555a996144cbc175c)を利用するとすっきりします。すごいテクニックです。

マッピング表をJSONで作成し、
（これもExcelとテキストエディタを駆使すれば簡単に作成可能です）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/e90d9ea6-3e34-49ea-9f9b-91b14f7cae8b.png)

上記マッピングのキーに、取得したランダムな数字配列における数字が一致したら値を返す if() を記載します。
（今回は一致しないことはあり得ないので、if() のtrue条件は null とかでも問題ありません）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/626f5195-ef85-067e-a7e3-116c775aa11e.png)

###６．場のカードにめくったカードを情報を付与

「２．場のカードの機種依存文字を通常文字に変換」の結果に対し、1文字ずつのJSON配列を作成します。
その際、indexとして文字位置を付与していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/e2a041f1-25db-b5e2-2f4e-eb111140359e.png)

[データ操作]－[選択]アクションで、開始には、２．場のカードの機種依存文字を通常文字に変換の結果をセット。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/63a807a0-2ee8-a76d-8bd1-41343c736d57.png)
マップには、

```
index ： item()
value ： substring(outputs('Card'), item(), 1)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/b35af0ec-5707-4b5a-a117-e945416775dd.png)


上記[選択]の出力に対し、上記の「４．場のカードにおける、Inputの「めくったカード」の位置を特定」で取得した位置に、「５．ランダムな数字配列における、「めくったカード」に該当する数字を特定」で変換したカードを差し込みます。

再度、[選択]を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/28ea9965-0895-c472-e6dd-d1f110da0a51.png)

マップには以下を設定してください。

```
if(equals(outputs('Firsthandint_onCard'), item()?['index']), variables('turnCard'), item()?['value'])
```


最後に、JSON配列を文字列に変換します。
[データ操作]－[結合]アクションを利用し、結合文字はnullです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/09378126-9f11-5d65-3370-afb7c07f6c5e.png)

###７．場のカードの通常文字を機種依存文字に変換
「２．場のカードの機種依存文字を通常文字に変換」で変換した通常文字を機種依存文字に戻します。

変換前
 ■ Ａ ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ Ｊ Ｑ Ｋ 

変換後
🔳🐭🐞🐯🐔🐻🐼🐟🐨🐶🐷🐸🐹🐛

###８．めくったカードが2枚目の場合、当たり/ハズレの判定をする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/06b68fef-e0c6-d8b2-f88e-7d32c5a1e87f.png)

上記の「１．フローのパラメータ」の1手目でめくったカードの値がnullでない場合、2枚目をめくったとき独自の処理を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/b4262fc7-0739-1181-8861-72d1c5b0f13e.png)

2枚目の場合は、1枚目でめくったカードと一致するか判定します。
「１．フローのパラメータ」の1手目でめくったカードの値と、「５．ランダムな数字配列における、「めくったカード」に該当する数字を特定」の値を比較します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/85776264-d282-5bba-1d59-8bdeb4cbfb4a.png)

一致する場合は当たりの画像と、「７．場のカードの通常文字を機種依存文字に変換」で作成した場のカード情報をフローのアウトプットに設定します。
一致しない場合はハズレの画像と、「１．フローのパラメータ」の1枚目をめくる前の場のカードをアウトプットに設定します。

当たりとハズレの画像は、[いらすとや](https://www.irasutoya.com/2014/08/blog-post_384.html)さんから頂きました。

チャットボットの応答に画像を表示する方法は、
[「Power Virtual Agents でチャットボットに画像を表示する【改定版】」](https://qiita.com/MiyakeMito/items/ce962308a3514f37da31)
を参照ください。

また、当たりはずれを加味した場のカードとして
・当たりの場合は「６．場のカードにめくったカードを情報を付与」にかかわらずめくった後の場のカード
・ハズレの場合は1枚目と2枚目のカードを元に戻すため「１．フローのパラメータ」で受け取った1手目をめくる前の場のカード
を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/46de03c6-d508-1aac-c7a3-a6cfb36ee9a1.png)

###９．フローのアウトプット

① 「７．場のカードの通常文字を機種依存文字に変換」で変換した場のカード（当たり/ハズレ結果は反映していないもの）

② 「８．めくったカードが2枚目の場合、当たり/ハズレの判定をする」の当たり/ハズレの画像

③ 「８．めくったカードが2枚目の場合、当たり/ハズレの判定をする」の当たりはずれを加味した場のカード

⑤ 1手目をめくる前の場のカードは、当フローを呼び出すトピックA、およびトピックBのインプットである場のカードの値です。ハズレだった場合にめくったカードを戻す際に利用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/480768/939d85e4-5c92-f170-cc84-befbfde84d37.png)


#まとめ

全3回でPower Virtual Agetns と Power Automate を利用した神経衰弱ゲームの説明をしました。
文字列の処理では繰り返し処理を多用するルーチンですが、Power Automate のApply to each を利用すると処理時間が激増し、
チャットボットでのゲームとしては成立しません。
よって、Apply to each を一切使わないロジックを構築しました。