「ポ」「プ」「テ」「ピピック」をランダムに出力して「ポプテピピック」が完成したら竹●房を破壊するC#

概要

「ポ」「プ」「テ」「ピピック」をランダムに出力して「ポプテピピック」が完成したら竹●房を破壊するJavaScript
をC# + WPF + Reactive Extensions で書いてみました。

C#でもすでに先達がおられる
https://qiita.com/ruhiel/items/c98451e4700344654f25

今回コンソールじゃなくてWPFにしたのはAAをきれいに表示するため。WinFormsでも同じようにできます。

コード

AAは改行が含まれていてい直接コードに書きづらいので、ResourceにTakeで登録しておきます。
NugetでReactive Extensions(System.Reactive)を導入します。

WindowのXamlではAAが正しく表示されるようTextboxのフォントを「MS PGothic」に設定しています。

MainWindow.xaml
<Window
    x:Class="PopTeamEpicWpf.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="「ポ」「プ」「テ」「ピピック」をランダムに出力して「ポプテピピック」が完成したら竹●房を破壊するC#"
    Width="900"
    Height="600">
    <ScrollViewer>
        <TextBox
            x:Name="textBox"
            FontFamily="MS PGothic"
            FontSize="16"
            IsReadOnly="True"
            TextWrapping="Wrap" />
    </ScrollViewer>
</Window>

コードビハインドでは主に以下の2つを行っています。

  • ランダムに「ポ」「プ」「テ」「ピピック」が流れてきて、「ポプテピピック」に一致したらCompleteするIObservableの生成
  • それを購読してOnNextではそのまま、CompleteではAAをTextboxへ出力

注意点は一致判定のためにBufferでまとめている部分で、あえてBufferの長さを余分にしていることです。
これは一致した時の最後のOnNext「ピピック」を流すためです。

MainWindow.xaml.cs
public partial class MainWindow : Window
{
    static string[] pops = new[] { "ポ", "プ", "テ", "ピピック" };

    public MainWindow()
    {
        InitializeComponent();

        CreateRandomPops()
            //Textboxにアクセスするので、UIスレッド上で購読
            .ObserveOn(this.Dispatcher)
            .Subscribe(
                //OnNextは流れてきた文字をそのままTextboxに書き足す
                x => this.textBox.Text += x,
                //CompleteしたらリソースからAAを取得してTextboxに書き足す
                () => textBox.Text += Properties.Resources.Take);
    }

    /// <summary>
    /// ランダムに「ポ」「プ」「テ」「ピピック」がOnNextに流れてきて、「ポプテピピック」に一致したらCompleteするIObservable
    /// </summary>
    private static IObservable<string> CreateRandomPops()
    {
        int maxCount = pops.Count();
        var random = new Random();
        IObservable<string> randomPop = Observable
            //10msecごとに
            .Interval(TimeSpan.FromMilliseconds(10))
            //ランダムに「ポ」「プ」「テ」「ピピック」のどれか
            .Select(_ => pops[random.Next(maxCount)]);
        //※テスト用 最初がいきなり「ポプテピピック」になる
        //.Select(i => pops[i % maxCount]);

        //バッファを空白で埋めておく (["","","","",""])
        return Observable.Repeat("", maxCount)
            .Concat(randomPop)
            //最後の「ピピック」をOnNextに流すために1つ余分にBufferをもつ
            .Buffer(maxCount + 1, 1)
            //Bufferの前が「ポプテピピック」に一致する状態(["ポ","プ","テ","ピピック",*])になるまで続行 
            .TakeWhile(x => !x.Take(maxCount).SequenceEqual(pops))
            //Bufferの最後をOnNextに流す  ([*,*,*,*,○] の ○)
            .Select(x => x.Last());
    }
}

実行結果例

スクリーンショット 2018-03-30 01.00.14.png

参考

Reactive Extensions再入門
https://qiita.com/amay077/items/85dfc4bd194f57c52c57#_reference-557477ce374f1a552c5c

環境

System.Reactive 3.1.1
VisualStudio2017
.NET Framework 4.7
C#7.1

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.