Power Automate Desktopを使ってゲーム「マスターマインド」を再現してみた(Recreating the game "Mastermind" with Power Automate Desktop)

:::note warn
こちらの記事ですが、クラベル様の下記リンク先の記事と同様のテーマを採用していたことを確認いたしました。
<A href="https://cravelweb.com/rpa/power-automate-desktop/how-to-create-game-on-power-automate-desktop">Power Automate Desktop：基本的なアクションを使ってゲーム「ヒット＆ブロー」を作ってみる</A>

日付を見ると当方の記事よりもクラベル社様の記事の方が先に作成されておりますが、当方は<strong>クラベル様の記事を参考にしたり、内容をパクったという事実はないことをお知らせいたします。</strong>当方が、記事作成時に検索をかけるなどのテーマの重複や既存記事の確認を怠っていたため、このような状況となっております。
:::

こんにちは。
今回は気分転換もかねて、マイクロソフト社のPower Automate Desktop（以下「PAD」）で、「マスターマインド」と呼ばれるゲームを再現してみました。

https://ja.wikipedia.org/wiki/%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%9E%E3%82%A4%E3%83%B3%E3%83%89

マスターマインド（「ヒット＆ブロー」とも呼ばれています）の説明はWikipediaの記事を読んでください。
私もボードゲームとしてのマスターマインドは遊んだ記憶がありません・・・

今回の仕様は以下としました

* 数字0-9（重複なし）でランダムで作成された4桁の数字を当てる
* 数字入力後、「ヒット」（数字も位置も同じ）、「ブロー」（数字は使われているが位置が異なる）を判定し、プレイヤーに結果を返す。プレイヤーはこの情報をヒントとして正解を推察する
* 正解したら（4ヒットしたら）そこで終了とする
* 20回数字を入力しても正解できない場合は正解を表示して終了とする

ゲームとしてはこういったシンプルなものなので、使用したPADのアクションについて説明します。

### 数字4桁の生成

**変数＞乱数の生成** を使用しました。

数値の生成範囲を決められる点と、複数の乱数生成時に重複の許可有無の設定ができるので大変便利でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/8fd9689e-dbd1-6a48-c2f3-16dae62c87eb.png)

複数の乱数を生成すると、リスト型変数が生成されます。このリスト型変数に格納された数字4つを1つの変数にまとめるのが、**テキスト>テキストの結合** です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/5730cdc6-6bd2-041b-b7a6-9e97d43d1b35.png)

なお、上記の「リスト項目を区切る区切り文字記号」を「なし」にすると、フロー上では「スペースx1で区切って結合する」という表示がされますがスペースは含まれていません（2.9xxxxで確認しています）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/ef6b80d3-841d-c298-40f4-b03cb7721717.png)

### 入力値の判定

以下のチェックを実装しました。
* 入力文字数が4桁以外→入力やり直し
* 入力値に数値以外が含まれている→入力やりなおし

以下のチェックは、処理が複雑になりそうなので現時点では未実装です。
* 同一数字が複数個所で使用されている→入力やりなおし

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/d4f59579-b08f-2a12-ab61-9eec8123a761.png)

入力文字数は**「入力値の変数.Length」**で取得しています。
また、数字以外の値の設定有無については、**テキスト>テキストの解析**で正規表現による、「\D（数字以外）が含まれていたら」という判定でチェックしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/8cc26a64-2172-6fa5-25bd-6222c12c1536.png)

判定結果がエラーとなった場合は、入力ダイアログの直前に設定したラベルに移動させることで、再度入力をさせる仕様です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/c3f2b716-08d3-c175-df7a-e4f1f210822b.png)

### ヒット・ブロー判定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/6cc2ce60-ef07-7dce-2605-9e2fbae7e209.png)

生成された値が格納されたリストをFOR EACHで回すか考えましたが、インデックス変数が自動で生成されるLOOPでの実装としました。
最初のLOOPで入力文字を1件づつ取得し、2つ目のLOOPで生成された番号から1文字づつ取得し、合致しているかを確認します。合致している場合、位置（＝インデックス）が合致していたらヒット件数に+1、そうでなければブロー件数に+1して、2つ目のLOOPを抜け入力文字のチェックを行う仕様としています。

入力文字の特定文字を取得する処理は、**テキスト>サブテキストの取得** で行います。LOOPのインデックスは1から始めていますが、サブテキストの文字の位置はゼロから始まるので「インデックス変数-1」の文字を1文字取得しています。変数からさらに演算した値を設定する場合、**演算処理も含めて%で囲う**ところが重要です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/4fea3bf2-727b-644f-2a0c-41bdfef532bf.png)

上記処理で取得した場合、中身が数字のみであっても、テキスト型で変数が作成されます。
一方で、2番目のLOOPで生成された数値から文字を抜き出した場合は整数型で変数が生成されています。
このままでは両方の値を比較できないため、入力文字のテキスト型変数を**テキスト>テキストを数値に変換** を行って数値型変数に取り直してから、値を比較するようにしています。

これらの処理を行って、正解した（4ヒットだった）場合は「正解です！」画面を表示し、そうでない場合は「惜しい！」画面にて入力した文字のヒット数とブロー数を表示して再度値を入力させます。これを最大20回繰り返して、この間に正解できなかった場合は正解を表示して終了します。

### 遊んでみた

* 1回目の入力画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/9d760a4b-ab93-e04f-c4a0-5ae12f82ef9d.png)

* 正解ではなかった場合
（常に「惜しい！」しか表示しませんｗ）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/2fa1d3d1-fa22-b944-ede5-c68cddbd41be.png)

* 2回目以降の入力画面
何回目の入力かと、前に入力した値を表示するようにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/e6e4a6ff-e2ff-36d8-6855-0292e02cfa79.png)

* 正解画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326392/28cef839-cfba-719d-5922-58ca8dc681f1.png)


### ソースコード

フロー作成にて生成されたRobinのソースコードは以下となります。サブフローは使用していません。

```robin:マスターマインド
Variables.ListOfRandomNumbers MinimumValue: 0 MaximumValue: 9 NumbersCount: 4 AllowDuplicates: False RandomNumbers=> RandomNumbers
Text.Join List: RandomNumbers Result=> Answer
LOOP LoopIndex FROM 1 TO 20 STEP 1
    SET HitCount TO 0
    SET BlowCount TO 0
    # プレイヤーが数字を入力
    LABEL INPUT_NUMBERS
    Display.InputDialog Title: $'''%LoopIndex% 回目のトライです！''' Message: $'''0から9までの半角数字を4つを入力してください（すべて違う数としてください）''' DefaultValue: UserInput InputType: Display.InputType.SingleLine IsTopMost: True UserInput=> UserInput ButtonPressed=> ButtonPressed
    # キャンセル確認後に入力値判定
    IF ButtonPressed = $'''Cancel''' THEN
        EXIT Code: 0
    END
    # 入力文字数が4以外→再入力
    IF UserInput.Length <> 4 THEN
        GOTO INPUT_NUMBERS
    END
    # 数字以外が入力→再入力
    Text.RegexParseForFirstOccurrence Text: UserInput TextToFind: $'''\\D''' StartingPosition: 0 IgnoreCase: False OccurrencePosition=> Position Match=> Match
    IF Position <> -1 THEN
        GOTO INPUT_NUMBERS
    END
    # ヒット/ブロー判定
    LOOP IndexInputChar FROM 1 TO 4 STEP 1
        Text.GetSubtext Text: UserInput CharacterPosition: IndexInputChar - 1 NumberOfChars: 1 Subtext=> EachUserInput
        Text.ToNumber Text: EachUserInput Number=> EachUserInputNumber
        # HIT/BLOWチェック
        LOOP IndexRandomNumbers FROM 1 TO 4 STEP 1
            SET EachRandomNumber TO RandomNumbers[IndexRandomNumbers - 1]
            IF EachUserInputNumber = EachRandomNumber THEN
                IF IndexInputChar = IndexRandomNumbers THEN
                    Variables.IncreaseVariable Value: HitCount IncrementValue: 1 IncreasedValue=> HitCount
                    EXIT LOOP
                ELSE
                    Variables.IncreaseVariable Value: BlowCount IncrementValue: 1 IncreasedValue=> BlowCount
                    EXIT LOOP
                END
            END
        END
    END
    IF HitCount = 4 THEN
        Display.ShowMessageWithTimeout Title: $'''正解です！''' Message: $'''正解は、%Answer% でした！
%LoopIndex% 回目のトライで成功しました！''' Icon: Display.Icon.Information Buttons: Display.Buttons.OK DefaultButton: Display.DefaultButton.Button1 IsTopMost: True Timeout: 10 ButtonPressed=> ButtonPressed
        EXIT Code: 0
    ELSE
        Display.ShowMessage Title: $'''惜しい！''' Message: $'''%UserInput%は、
%HitCount%HIT、%BlowCount%BLOWです！''' Icon: Display.Icon.Information Buttons: Display.Buttons.OK DefaultButton: Display.DefaultButton.Button1 IsTopMost: True ButtonPressed=> ButtonPressed
    END
END
Display.ShowMessageWithTimeout Title: $'''残念！''' Message: $'''正解は%Answer%でした！''' Icon: Display.Icon.Information Buttons: Display.Buttons.OK DefaultButton: Display.DefaultButton.Button1 IsTopMost: True Timeout: 10 ButtonPressed=> ButtonPressed
```

## まとめ

業務改善化・定型作業自動化のためにマイクロソフトが無償で提供しているアプリでゲームを再現しました・・・黎明期の業務用パソコンでも、業務以外のプログラムをこっそり組んで空き時間に遊んでいた方もいたようですし、歴史は繰り返すということで・・・:sweat_smile:

今回は対応しませんでしたが、自分が入力した数字が何ヒット何ブローだったかをログのように見られると便利なので、入力した文字や結果をメモ帳やエクセルに都度出力するよう改造してみるのもよいかと思います。

お気づきの際はお手数をおかけいたしますが、コメントや質問等をいただけましたら幸いです。
今回の内容は以上となります。ありがとうございました。