90
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

もしも妖怪にしりとりを挑まれたら~ようかいしりとり完全解析

Posted at

はじめに

 いくら令和の世と言えども、まだまだ妖怪にしりとりを挑まれることもあるでしょう。そのときに備えて、今回開発したようかいしりとりシミュレーターで特訓をしておきましょう。

前日譚

 少し前に、「ようかいしりとり」という曲をほぼエンドレスで聴かされるという拷問を受けていました。

 ところで、100回目だか200回目だかの「ようかいしりとり」を聴かされているときに、ふと気づきました。「あれ…しりとりってもしかして、二人零和有限確定完全情報ゲームでは…?」

  • 二人: 通常のしりとりは二人とは限りませんが、ようかいしりとりは 妖怪 対 人間 の二人でおこないます。
  • 零和:しりとりで一方が勝てば必ずもう一方は負け、つまり「双方勝ち」などの結果は無いので、零和です。
  • 有限:しりとりでは一度使った語は使えず、そして語は無限には存在しないため、明らかに有限手数で終了します。
  • 確定:しりとりでは、サイコロを振るなどの偶然性のある要素はありません。
  • 完全情報:しりとりでは、トランプの手札のように「隠された要素」はありません。今までに使った語はどちらも知っています。

 そして二人零和有限確定完全情報ゲームは、必ず以下の3パターンに分類できることが知られています。

  • 双方のプレーヤーが最善手を指せば、必ず先手必勝となるゲーム。
  • 双方のプレーヤーが最善手を指せば、必ず後手必勝となるゲーム。
  • 双方のプレーヤーが最善手を指せば、必ず引き分けとなるゲーム。

 しりとりにはルール上「引き分け」はありませんから、つまりしりとりは先手必勝か後手必勝のどちらかです。妖怪にしりとりを挑まれる可能性に怯えるすべての人たちのため、ようかいしりとりの完全解析をしなければ――そんな使命感に駆られたのです。

「ようかいしりとり」ルールの詳細

 ようかいしりとりが先手必勝か後手必勝かを判断するためには、まず「ようかいしりとり」のルールを明確にしなければなりません。ほんの少しルールを変えるだけで、先手必勝か後手必勝かは変わってしまう可能性があります。ようかいしりとりの厳密なルールブック等は見つかりませんでしたので、「ようかいしりとり」の歌の歌詞から推測できる以下のルールに従うこととします。

使える語について

 ようかいしりとりで使用可能な語は妖怪名のみです。ここでは、Wikipedia「日本の妖怪一覧」のページ(2024年7月2日 (火) 15:49‎ の版)にある妖怪名のみ使用可能としました。

開始語について

 通常のしりとりでは「しりと」からスタートすることが多いですが、ようかいしりとりでは挑んできた妖怪が先手となり、自身の名前からスタートします。

 つまり、「ようかいしりとり」は細分化すると「ようかいしりとり~対ろくろっくびver」「ようかいしりとり~対ざしきわらしver」のように、妖怪の数だけ別バージョンがあると言えます。そして対戦する妖怪によって、先手必勝(つまり妖怪側の勝ち)か後手必勝(つまり人間側の勝ち)が決まります。今回のこの解析の目的は、先手必勝となる妖怪と後手必勝となる妖怪を明らかにすることで、もし先手必勝となる妖怪にようかいしりとりを挑まれた際には逃げるなりしりとり以外の対戦を提案するなりできるようにすることです。

別名について

 妖怪は、例えば「のっぺらぼう」=「ずんべらぼう」など、さまざまな別名を持っている場合があります。今回のルールでは、例えば「のっぺらぼう」が出たら、以後は「のっぺらぼう」はもちろん別名である「ずんべらぼう」も使用禁止とします。

 ただし、例えば「マジムン」と「アカングワーマジムン」は別名ではなく、生物で言うところの属と種のような関係と考えられます。これについては、一方が出ても他方は使用可能とします。

 詳細については、後述するデータを実際に御覧ください。

長音の扱い

 「ようかいしりとり」の歌詞の中に「みつめこぞ」→「みぼうず」と続いている箇所があります。「三つ目小僧」は実際の発音としては「ミツメコゾオ」なので「お」で続けるというルールもありますが、歌詞に従い かな書きしたときに「う」で終わるのであれば「う」で続けるものとします。

 しかし「アカガンター」のように表記上「ー」で終わるものについては、実際の発音「アカガンタア」に従い「あ」で続けるものとします。

拗音・促音の扱い

 最後の文字が拗音・促音(ぁ、ぃ、ぅ、ぇ、ぉ、ゃ、ゅ、ょ、っ)の場合、大きい文字に戻すルールとそのままのルールがあるようですが、歌詞に「だいだらぼっ」→「ょうちんおばけ」と続いている箇所があることから判断し、前者とします。(後者であれば、「ちょうちんおばけ」は「ちょ」で終わった場合でないと使えないため。)

濁点・半濁点の扱い

 最後の文字が濁音・半濁音の場合、それを取っていいルールと取ってはいけないルールがありますが、歌詞の中では「しちほ」→「いだらぼっち」のようにすべて濁点は濁点のまま続けています。もちろんこれだけでは論理的にはどちらのルールなのか判断できませんが、後者のルールであるものとしました。

敗者の扱いについて

 「ようかいしりとり」の歌の中では、人間側であるようかいはかせは2戦2勝しています。負けた妖怪側は悔しがるだけで、特に人間側に得るものは無さそうです。

 一方、歌では描かれていない人間が負けた場合はどうなるのでしょうか?なにしろ相手は妖怪です、きっと喰われるのでしょう。というわけでシミュレーターではそうしました。勝っても特に利益はなく、負ければ死。この理不尽さが妖怪というものです。

データの用意

 前述したWikipedia「日本の妖怪一覧」のページからデータを取得・加工しました。

 余談ですが、Wikipedia のデータを機械的に加工する場合、実際のページからコピペしたり あるいはそのページの HTML を使うよりも、編集画面にある Wiki 記法のテキストを使うほうが大抵楽です。

 そうして用意したデータがこちらです。

youkai_data.js(一部抜粋)
○	アスコココ
○	アズキアライ	アズキトギ
×	アズキトギ	アズキアライ

 最初にある ○× は、こちらで事前に解析した結果です。○ であれば後手(人間側)必勝、× であれば先手(妖怪側)必勝です。× の妖怪を覚えておき、そうした妖怪に勝負を挑まれたら逃げましょう。

 行によってはタブ区切りで複数の名前がありますが、先頭にあるものが実際にしりとり中で使う際の名前、2つめ以降が別名です。上の例にある通り、別名を持った妖怪(例:「小豆洗い/小豆とぎ」)についてはその別名の数だけ行があります。

 ところで面白いのは、同じ妖怪であっても名乗り方によって先手必勝・後手必勝が変わる場合があることですね。自身を「小豆洗い」と名乗った場合は人間側の勝ちですが、「小豆とぎ」を名乗った場合は妖怪側の勝ちです。「小豆洗い」を名乗ってきたらきっとその小豆洗いはようかいしりとり素人です。

解析

 しりとりは、三目並べや将棋と違い、先手と後手に順番以外の違いはありません。(例えば三目並べであれば、先手は ○ で後手は × といった違いがありますね。)さらに引き分けが無いおかげで、解析は簡単でした。

 JavaScript の擬似コードとして書くと、解析本体はこんな再帰関数1つです。

function foreseeing(既出妖怪名, 最後の一字) {
    if (最後の一字 == "") return true
    次で出せる妖怪候補一覧.some(次妖怪候補 => {
        return foreseeing(既出妖怪名.concat(次妖怪候補の別名も含めた全名前), 次妖怪候補の最後の一字) == false
    })
}

 この関数は、現在の手番の人に(お互いが最善を尽くした場合の)勝ちパターンがあれば true 、無ければ false を返します。

 まず 2 行目ですが、「最後の一字」が『ン』であれば、(『ン』で終わる妖怪名を出したのは一つ前の手番である相手側なので)自分の勝ちですから、true を返します。

 3~5 行目では、次で出せる妖怪の1つ1つに対して、その妖怪を出した場合に相手側に勝ちパターンがあるかどうかを調べています。相手側に勝ちパターンが無い(false)のであれば、つまりは自分側に勝ちパターンがあるということなので、その妖怪を出せば勝てるということになります。複数の選択肢の中に1つでも勝ちパターンがあるなら、それを選べば勝てるので、現時点で自分に勝ちパターンがあると言えます。一方、どの妖怪を出しても自分側に勝ちパターンが無ければ、現時点で自分に勝ちパターンが無いと言えます。

 そこで、Array.prototype.some() を使って、1つでも勝ちパターンがあれば true、1つも勝ちパターンが無ければ false を返しています。

具体例

 もう少し分かりやすくするため、具体例を挙げます。今は人間側の手番で、未出の妖怪を以下の妖怪に限定し、現時点の最後の一字を『ア』とします。

アカアシ    アカエイ    イガボウ    イタオニ    ウシゴゼン    ウバガビ    シカヒメ    シキガミ
ニクジン    ニタッラサンペ    ミツメハチメン    ミョウタラテン    メロリカンノン    メンレイキ

 すると、今後の全展開を木として表すと以下のようになります。

─┬─アカアシ─┬─シキガミ─┬─ミョウタラテン(妖怪勝ち)
 │         │         └─ミツメハチメン(妖怪勝ち)
 │     └─シカヒメ─┬─メロリカンノン(妖怪勝ち)
 │                  └─メンレイキ(人間勝ち)
 └─アカエイ─┬─イガボウ─┬─ウシゴゼン(妖怪勝ち)
           │         └─ウバガビ(人間勝ち)
       └─イタオニ─┬─ニクジン(妖怪勝ち)
                    └─ニタッラサンペ(人間勝ち)

 まず、右上のこの部分だけ見てみましょう。

─┬─ミョウタラテン(妖怪勝ち)
 └─ミツメハチメン(妖怪勝ち)

 このとき手番は人間側ですが、どちらを選んでも『ン』で負けてしまうので、そもそもこのシキガミの道に来た時点で人間の負けが確定したと言えます。そこで、元の木をこう書き換えてしまいましょう。

─┬─アカアシ─┬─シキガミ(妖怪勝ち)
 │         │        
 │     └─シカヒメ─┬─メロリカンノン(妖怪勝ち)
 │                  └─メンレイキ(人間勝ち)
 └─アカエイ─┬─イガボウ─┬─ウシゴゼン(妖怪勝ち)
           │         └─ウバガビ(人間勝ち)       
       └─イタオニ─┬─ニクジン(妖怪勝ち)
                    └─ニタッラサンペ(人間勝ち)

 続いて、この部分を見ます。

─┬─メロリカンノン(妖怪勝ち)
 └─メンレイキ(人間勝ち)

 今度は先程と違い、「メンレイキ」を選べば勝つことができます。わざわざ負ける「メロリカンノン」を選ぶ必要は無いですから、このシカヒメの道に来た時点で人間の勝ちが確定したと言えます。

 以下同様に、木の一番右の部分を書き換えたものがこちら。

─┬─アカアシ─┬─シキガミ(妖怪勝ち)
 │         │        
 │     └─シカヒメ(人間勝ち)
 │
 └─アカエイ─┬─イガボウ(人間勝ち)
           │ 
       └─イタオニ(人間勝ち)

 続いてこの部分を見ていきます。

─┬─シキガミ(妖怪勝ち)
 │        
 └─シカヒメ(人間勝ち)

 このときの手番は妖怪側ですから、当然自分が勝てるシキガミの方を選ぶでしょう。そうすれば勝つことができるので、そもそもこのアカアシの道に来た時点で妖怪の勝ちが確定したと言えます。

 一方、こちらはどうでしょう。

─┬─イガボウ(人間勝ち)
 │ 
 └─イタオニ(人間勝ち)

 こちらではどちらを選んでも人間の勝ちになってしまうので、このアカエイの道に来た時点で妖怪の負けが確定したと言えます。

 また確定した部分を書き換えてしまいましょう。

─┬─アカアシ(妖怪勝ち)
 │ 
 │ 
 │
 └─アカエイ(人間勝ち)

 このときの手番は人間側ですから、当然自分が勝てるアカエイの方を選ぶでしょう。そうすれば勝つことができるので、そもそもこの時点で人間の勝ちが確定したと言えます。

 このように「1つでも勝ちパターンがあれば勝ち」に従って再帰的に書き換えていくことで、現時点でどちらの勝ちパターンがあるか調べることができるわけです。

 まぁ要はミニマックス法の応用なので、詳しくはミニマックス法について調べてください。

工夫

 ただし、最初本当に上の擬似コードのように書いたところ、いつまで待っても解析が終わりませんでした。理論上はいつかは必ず解析できるはずですが、工夫しないと解析に非常に長い時間がかかってしまうのです。

 そこで、こんな一工夫を加えたところ、すぐに終わるようになりました。

function foreseeing(既出妖怪名, 最後の一字) {
    if (最後の一字 == "") return true
    次で出せる妖怪候補一覧.sort(その次で出せる妖怪の候補数が少ない順にソート) // ←追加
    次で出せる妖怪候補一覧.some(次妖怪候補 => {
        return foreseeing(既出妖怪名.concat(次妖怪候補の別名も含めた全名前), 次妖怪候補の最後の一字) == false
    })
}

 しりとりの性質から言って、「次で出せる妖怪の候補数」が少ないほど勝ちパターンがある可能性が高まります。上で書いた通り勝ちパターンが1つでも見つかればよいので、解析する順番にこうした一工夫をするだけで大きく時間が変わります。

見えてきたもの

る攻めは危険

 通常のしりとりでは「る攻め」という戦略があります。「る」で始める単語は少ないので、できるだけ「る」で終わる単語を選んでいくことで相手を追い詰める戦略ですね。

 しかし ようかいしりとりでは る攻めは絶対にしてはいけません。確かにようかいしりとりについても「る」で始まる妖怪名は少ない――それもたった1つ――ですが、その1つというのが「ルルコシンプ」です。「ぷ」で始まる妖怪名は存在しないので、る攻めをしたら確実な死が待ち受けています。

必殺、坊主コンボ

 比較的覚えやすい戦略が「坊主コンボ」です。「~坊主」という妖怪は多数いますが、「ず」で始まる妖怪は「ずんべらぼう」一択です。そのため

  1. 👴「~坊主」
  2. 👻「ずんべらぼう」
  3. 👴「海坊主(1. が海坊主であれば、海鳴り小坊主)」

というコンボがあります。「あ」「い」「う」「お」「か」「く」「け」「こ」「ざ」「し」「せ」「そ」「た」「ど」「に」「ぬ」「の」「ひ」から始まる「~坊主」が存在するので、前述のデータを見て坊主系妖怪の名前を一通り覚えておくといいでしょう。

実は先手必勝だったろくろっくび

 「ようかいしりとり」の歌ではようかいはかせは ろくろっくびと座敷わらしの2人と対戦をしていますが、データを見ると分かる通り、実はろくろっくびは先手必勝でした。(例:👻ロクロクビ→👴ビシャガツク→👻クツツラ→👴ラプシヌプルクル→👻ルルコシンプ)ろくろっくびがよわよわだったので勝ちを拾うことができましたが、ようかいはかせともあろう人が、どうしてそんな危険な勝負を引き受けてしまったのでしょうか…

 もしかしたら、ようかいはかせは命のかかった勝負に興奮を覚えるアレな人なのかもしれません。対座敷わらし戦においても前述の坊主コンボが決められるところを敢えて使わずにいることも、この説を補強するものと言えるでしょう。

今後の課題

 今回、長音・拗音・促音・濁音・半濁音の扱いについては前述のとおりとしましたが、このあたりを変更すれば先手必勝・後手必勝が大きく変化するでしょう。今回の解析では × であった妖怪に対しても、別ルールであれば勝てる可能性があるということです。

 ルール変更次第では勝てる妖怪、それでも勝てない妖怪…の解析については、今後の課題とします。

後日譚

 私がようかいしりとりの解析を終えたころには、飽きてしまったのか我が家でようかいしりとりが流れることはなくなりました。なんということだ。

90
27
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
90
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?