こういう状態遷移が絡む系のチェックは Enumerator の仕組みを使うといい感じに書けそうですね。
というわけでできたもの
IEnumerator<string> RandomZunDoko()
{
for (;;) {
yield return Random.Range(0, 2) == 0 ? "ズン" : "ドコ";
}
}
IEnumerator<string> KiyoshiCheck(IEnumerator<string> source)
{
for (;;) {
Retry:
for (int i = 0; i < 4; ++i) {
if (!source.MoveNext()) {
yield break;
}
yield return source.Current;
if (source.Current != "ズン") {
goto Retry;
}
}
if (!source.MoveNext()) {
yield break;
}
yield return source.Current;
if (source.Current != "ドコ") {
goto Retry;
}
break;
}
yield return "キ・ヨ・シ!";
}
使い方
IEnumerator<V> SafetyLimit<V>(IEnumerator<V> source, int n)
{
for (var i = 0; i < n; ++i) {
if (source.MoveNext()) {
yield return source.Current;
} else {
yield break;
}
}
}
IEnumerator<string> Counter(IEnumerator<string> source)
{
var c = 0;
while (source.MoveNext()) {
yield return source.Current;
++c;
}
yield return string.Format("({0}回ズンドコしました)", c - 1);
}
string Result(IEnumerator<string> source)
{
var s = "";
while (source.MoveNext()) {
s += source.Current;
s += " ";
}
return s;
}
void Start()
{
Debug.Log(Result(Counter(KiyoshiCheck(SafetyLimit(RandomZunDoko(), 1000)))));
}
結果
解説
RandomZundoko
は "ズン"
か "ドコ"
をランダムに無限に返し続ける Enumerator です。Enumerator は実は有限でななくても成り立ちます。
IEnumerator<string> RandomZunDoko()
{
for (;;) {
yield return Random.Range(0, 2) == 0 ? "ズン" : "ドコ";
}
}
KiyoshiCheck
は string
を返す Enumerator source
を受け取り、その内容を返しつつ、さらにズンドコがいい感じに続いた場合は "キ・ヨ・シ!"
を返して完了する Enumerator です。
IEnumerator<string> KiyoshiCheck(IEnumerator<string> source)
{
for (;;) {
Retry:
for (int i = 0; i < 4; ++i) {
if (!source.MoveNext()) {
yield break;
}
yield return source.Current;
if (source.Current != "ズン") {
goto Retry;
}
}
if (!source.MoveNext()) {
yield break;
}
yield return source.Current;
if (source.Current != "ドコ") {
goto Retry;
}
break;
}
yield return "キ・ヨ・シ!";
}
まず最初に 4 回連続ズンのチェックをします。source
を MoveNext()
で一個進めて、Current
で値を取ってきます。とりあえずそれを返します。そして、それが "ズン"
だった場合はループを続けます。違った場合は、Retry:
へ戻ります。これによって、最初から 4 連続ズンチェックがやり直されます。無事 for
ループを抜けると、4 連続ズンが成立しことになり、次へ進みます。
こういうチェックをループでサクッと書けてしまうのは面白いですね。
for (int i = 0; i < 4; ++i) {
if (!source.MoveNext()) {
yield break;
}
yield return source.Current;
if (source.Current != "ズン") {
goto Retry;
}
}
同様の原理で、次に "ドコ"
が続いているかチェックします。
if (!source.MoveNext()) {
yield break;
}
yield return source.Current;
if (source.Current != "ドコ") {
goto Retry;
}
break;
合格すれば、"キ・ヨ・シ!"
を返して終わります。
yield return "キ・ヨ・シ!";
ね、簡単でしょう?
順序通りにチェックが書けてしまうのがこのやり方の面白いところですね。今回は無かったですが、分岐が入ったりするとさらに威力を発揮します。皆さんも是非。