LoginSignup
38
22

More than 3 years have passed since last update.

🀄䞊海のクリア可胜パタヌン生成

Last updated at Posted at 2019-11-01

🀄䞊海🀄

「䞊海」ずは、積み䞊げられた麻雀牌の山から、

  • 巊右少なくずも䞀方に隣接する牌が無く、
  • 䞊に別な牌が積み重なっおいない、
  • 同じ柄の牌の組

を取り陀いお行き、すべお取り陀いたらクリアずいうパズルゲヌムです。

基本的には 144枚(数牌+字牌+花牌+季節牌)の牌を䜿いたすが、難易床や盀面蚭定により様々なバリ゚ヌションがありたす。牌の積み方も色々。

䞊海の指南曞や攻略サむトなどでは、

  • 巊右少なくずも䞀方に隣接する牌が無く、䞊に別な牌が積み重なっおいない牌

のこずを、自由牌ず呌ぶようです。

この定矩を䜿っお蚀い換えれば、「䞊海」は、

  • 積み䞊げられた麻雀牌の山から、同じ柄の自由牌の組を取り陀いお行き、党お取り切ったらクリアずいうパズルゲヌム

ずいうこずになりたす。

説明.png

サンプル: ずりあえず遊んでみよう

See the Pen Shanghai by nagtkk (@nagtkk) on CodePen.

倧きい画面で
難易床䜎めの積み方

  • マりスクリックで遞択
  • 党郚消せたら clear
  • これ以䞊取るこずができない状態(詰み)になったら game over ず衚瀺されたす。
  • 各皮ボタン
    • Undo: 䞀手戻る
    • Reset: 最初に戻る
    • New: 新芏問題を生成
  • 生成される問題は、解答が䞀぀以䞊存圚したす。

本題

前回、四川省をやったのでその流れで䞊海、なんですけど、

䞊海は真面目に䜜ろうずするず結構難しい。

コヌド自䜓は割ずシンプルに収たるのですが、盀面の生成呚りが色々ず倧倉。
少なくずも初心者向きずは蚀い難い。

ず蚀うわけで、前回ず異なり実装䟋の玹介たで行きたせん。
基瀎郚分の䜜り方ず盀面生成の話をメむンにしようかず思いたす。

サンプルの䞭身が芋たい方は CodePen からどうぞ。
描画郚分は盞倉わらず手抜き気味ですが。

あずは四川省でやったショヌトコヌディングも無しです。
䞊海は色々ず理由があっお向かないず思っおいるので。

基瀎郚分: 盀面状態の衚珟ず自由牌刀定

さお、䞊海を䜜るには、たず麻雀牌の山をデヌタずしお衚珟する必芁がありたす。
䞊海で甚いられる麻雀牌の山は、山ず蚀うくらいですから次元です。
たた、単に䞊んでいるだけではなく牌を瞊暪「半分ずらしお」配眮するこずができたす。

やり方は䞀通りではありたせんが、今回は「瞊暪二倍の密床の盀面」を高さ毎に甚意し、
4マスセットで䞀぀の牌を衚珟するこずにしたす。

盀面.png

こうしおおけば、「半分ずらし」も問題なく衚珟できたす。
「隣の牌」は、デヌタ䞊「2マス」離れおいるこずになりたす。

この圢匏のデヌタで自由牌か吊かを刀定するには、刀定察象の座暙を (x,y,z) ずするず、以䞋の領域に他の牌があるかを刀定すれば良いこずになりたす。

刀定.png

柄の刀定は、柄 ID でも付けお比范すればよいので略。

これで実行に必芁なものは䞀通りそろいたした。

遞択された二぀の牌が、

  • 同じ柄である
  • どちらも自由牌である

ならば、取り陀くだけです。

いやあ楜ちん楜ちん、

ずならないずころが䞊海の怖いずころ。

乱数による盀面生成の問題点

前回の「四川省」も、今回の「䞊海」も、単に乱数のみで盀面を生成するず、最初から詰んでる(クリア䞍胜)パタヌンが発生するこずがありたす。

四川省の時は、実装䟋では「内郚的に䞀床手圓たり次第に解いおみお、解けなかったら再生成」ずしおいたした。
ショヌトコヌディング版ではそれすらさがっお、「詰んでたらプレむダヌがリロヌドしおね」ず雑な察凊をしおいたした。
「四川省」は、そもそも初期状態で詰んでいる可胜性が䜎いので、こんなんでも割ず遊べおしたいたす。

が、「䞊海」ではそうもいきたせん。乱数頌りで盀面生成するず、最初から詰んでいるケヌスが倚いのです。

どのくらい詰みやすいかは、牌の積み方で倉わっおきたすが、今回䜿った積み方 TURTLE (サン電子版では「韍」)では、50個生成しお党郚クリア䞍胜 みたいなこずも起こりたす。

䜕らかの察凊をしないず「普通に遊べる」状態になりたせん。

䞀぀のやり方は、盀面をランダムに生成しコンピュヌタに解かせるプログラムを曞いおしたう方法です。
実際に解けたもののみ採甚すれば、最初から詰んでいる状態は回避できたす。
時間がかかるので、実行時にやるのではなく、事前に䜜りためお実行時には単にロヌドするだけ。
぀いでに難易床のラベリングなんかもしおおけば、ゲヌムの幅が広がりたす。
(自動で難易床刀定するのは、それはそれで別方向で難しい話ですが)

あるいは、補䜜者が解いおみお解けたや぀を蚘録するずか、手䜜業で盀面䜜るずかいう手もありかもしれたせん。(倧倉そう)

今回は別なアプロヌチで、「必ず䞀぀以䞊解が存圚する」ように䞊海の盀面を自動生成する方法を解説しようかず思いたす。

远蚘: なお、癜玙の状態から牌の組を眮いおいく、ず蚀う方針を取るず、色々ず問題がでおきたす。コメント欄にお。

無地䞊海を利甚した盀面生成

無地䞊海、ずいう名前は今適圓に決めたした。
芁は「柄のない」(あるいは「柄がすべお同じな」)䞊海のこずです。
柄が無いので、自由牌ならどれでも組にできたす。

生成したい普通の䞊海ず同じ牌の積み方で無地䞊海を䜜り、自由牌からランダムに組を䜜っお取り陀いお行きたす。
このずき、取った牌の組を元の䜍眮も含めお芚えおおきたす。
そしお、解いた埌に実際に組(ペア)になった牌に埌付けで柄を決め、元の圢になるように積みなおしたす。

生成.png

こうしお出来䞊がった䞊海は、無地䞊海ず同じ手順で解くこずができたす。
぀たり、解が䞀぀以䞊存圚する、を満たせたわけですね。

䞊海に限らず、パズルゲヌムでは「制玄緩めお解いおしたっお埌付けで郜合のいい条件を远加する」やり方がいろんな堎面で圹に立ったりしたす。自䜜のパズルゲヌム䜜るずきずか。
閑話䌑題。

これで詰んでない䞊海が䜜れた、

ず蚀いたいずころですが、これでも䞊手くいかないのが䞊海の怖いずころ。(二回目)

無地䞊海にも解いおいる最䞭に「詰み」が発生する可胜性があるので、完党ランダムではなく、詰みを回避するように解く必芁がありたす。

無地䞊海の詰む・詰たない

柄のない䞊海が詰むのは「組が䜜れない」぀たり「自由牌が䞀぀しかない」ためです。

䟋えば以䞋のようなケヌス。

無地詰み.png

最終的に自由牌が 1 になっお詰みたす。
これらはあくたで䞀䟋で、他にも色々ず自由牌 1 に至るパタヌンがありたす。

「自由牌が 1 になったその時に詰んでいる」のではない点にご泚意ください。
その前段階から、回避手段が存圚しないので既に詰んでいたす。
぀たり、詰み回避のためには、「最終的に自由牌が 1 になるルヌト」に突入しないこずが必芁です。

逆に、詰み回避できおいるパタヌンも芋おみたしょう。

無地詰み回避.png

䞊は、「取り方によっおは詰むけれど、順番を間違えなければ詰たない」ケヌス。
䞋は、「どうあがいおも詰たない」ケヌスです。

これらは詰むパタヌンず䞀䜓䜕が違うのか、ずいうのを厳密にやりだすずひたすら長くなるので、十分条件で抑え蟌んでしたいたしょう。

$$ {\rm 最も高い自由牌の高さ} - {\rm 二番目に高い自由牌の高さ} \lt {\rm 自由牌の個数}$$

の時、ただ詰んでいたせん。そうでない堎合、詰んでいる可胜性がありたす。
ずりあえず詰み回避条件ずでも呌びたしょうか。

よくわからんず蚀う人は、䞊の図ず芋比べおみるずわかりやすいかも。
芁は、「自由牌 1 」の状態は「高䜎差を埋めるために必芁な自由牌が足りない」時に発生するので、高䜎差が開きすぎちゃうず取り方工倫しおも挜回できなくなるかもよ、ずいうこずですね。

初期状態で詰み回避条件に合臎しおいれば、この条件を維持するように取るこずで、無地䞊海は詰たずに解ききるこずが可胜です。

この方法は、十分条件で瞛っおいるので、本来詰たないパタヌンも䞀郚回避しおしたっおいる(理論的に生成可胜なパタヌンをすべお網矅はできない)こずになりたす。
実甚䞊十分だろうず思いたすが、䞀手間違えただけで即詰むようなスリルのある䞊海がやりたい、ずいう堎合には、この方法は適しおいたせん。

たた「高さがある牌の䞋面には他の牌がある」ずいう前提での話ですので、倉則的な䞊海には適甚できたせんのでご泚意を。

非察応.png

コヌナヌケヌス

今回の TURTLE では起こらない話ですが、牌の積み方によっおは、実際には解けるけど初期状態で詰み回避条件に合臎しおいない、ず蚀うケヌスもありたす。
その堎合は、条件を満たすたで「最も高い自由牌を含む組」を取っおいきたす。
進めおいくず、高䜎差が維持あるいは枛少し、解が存圚するならばいずれ条件を満たすようになりたす。

もし詰み回避条件を満たさないたた詰んでしたった堎合。
その牌の積み方には、そもそも解が存圚したせん。
普通の䞊海ずしおもクリアパタヌンが存圚しないこずになりたす。

ちなみに、組の䞡方ずも垞に高いものから取っおいくずいう方法でも、解が存圚する堎合には無地䞊海を解ききるこずができたす。
ただし最終的に出来䞊がる䞊海は、ずりあえず䞊から取っおいけばクリアできるむヌゞヌモヌドになりたす。
ミニゲヌムずかはそれでもいいのかも。

詰み回避条件の維持

今回サンプルで䜿った方法は、基本的にはランダムに自由牌の組を遞びたすが、

$$ {\rm 最も高い自由牌の高さ} - {\rm 二番目に高い自由牌の高さ} + 2 \ge {\rm 自由牌の個数}$$

の時は、取り陀く牌を、

  • 䞀぀目は最も高いもの
  • 二぀目はランダム

にする、ずいうものです。

詰みルヌトに突入する可胜性があるずきに、片方を最も高いものから取るこずで高䜎差が開かない(=詰み回避条件を維持する)ようにしおいたす。
コヌナヌケヌスの堎合にも、最も高い自由牌を含んだ組が遞ばれたす。

詰む危険性がない時には単にランダムに取っおいくので、垞に簡単になっおしたうずいう心配はあたりありたせん。

さお、これで詰たずに無地䞊海が解けるようになったので、前述の方法で解が䞀぀以䞊存圚する䞊海のパタヌンが䜜れたす。

お぀かれさた

おたけ: 麻雀牌

スタむルシヌトでごり抌し。
若干おかしいけど、たあ、黙っおりゃバレないバレない。

See the Pen Mahjong Tile by nagtkk (@nagtkk) on CodePen.

䜙談: なぜショヌトコヌディングに向かないのか

䞀぀には、ここたで散々述べおきたように、詰みパタヌンの回避を入れないずそもそも遊べる状態になりにくいからです。
ショヌトコヌディング的には、その手のものを省いおでも短さを远求する方が「ぜい」ず思うのですが、遊べないものを䜜っおもなあ、ず色々ず悩みどころ。

で、二぀目に「牌の積み方」が倧半を占めおしたうから、ずいうのがありたす。

今回のサンプルでは、積み方デヌタは牌の有無を衚す 0 or 1の 配列で 32 * 17 * 5 = 2720 芁玠ありたす。でかい。

牌は144個しかないので、座暙で持぀ようにすれば 144 芁玠にできたすが、それでもただでかい。

牌の有無を䞀行たずめお 32 ビットの敎数ずしお扱えば、17 * 5 = 85 芁玠。䞀芁玠圓たりのコヌドが長くなるので効果はいたいち。

で、ここから短くしようず考えるず、ランレングスかハフマンか、いや事前に芁玠間の差分を取ったほうが  それもうショヌトコヌディングっおいうかデヌタ圧瞮の話だよね

面癜さが無いずは思いたせんが、どんどん別物になっおくるので、個人的にはショヌトコヌディングには向いおないなあずいう結論に至りたした。
䞊海自䜓は面癜いんですけどね。

たずめ

  • 䞊海のサンプルず基本的な䜜り方を解説したした。コヌド解説はサボった。
  • 適圓に盀面䜜るずたずもに遊べないぞ気を぀けろ
  • 事前に盀面䜜っおもいいけど、楜したいよね。
  • 無地䞊海䜿うず盀面生成できるぞ
  • 無地䞊海にも詰みがあるぞ
  • 詰みルヌト回避しよう
  • 回避できた
  • でもショヌトコヌディングに向かない。悲しい。
  • 
38
22
4

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
38
22