Edited at

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

More than 1 year has passed since last update.


概要

「ポ」「プ」「テ」「ピピック」をランダムに出力して「ポプテピピック」が完成したら竹●房を破壊する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());
}
}



実行結果例


参考

Reactive Extensions再入門

https://qiita.com/amay077/items/85dfc4bd194f57c52c57#_reference-557477ce374f1a552c5c


環境

System.Reactive 3.1.1

VisualStudio2017

.NET Framework 4.7

C#7.1