チェック機能

定石ルールを追加したり、不要になったコードを削除したりしつつ、職人さんが作った問題を解いたりしていたのだが。

問題にテーマが決められたりしているのはさすがだ。自動生成では難しい。

それはそれとして、最近思うことがある。チェック機能が便利すぎるのだ。

チェックボタンを押すと正解と違う部分が赤くなる。これにより、間違った部分が完璧にわかる。難しい推論をしたときに、ついつい「これ合ってるかな?」って押して確認してしまう。こいつがよろしくない。ヌルくなってしまう。

ということでチェックボタンを消してみた。ルール適用とヒントボタンもチェック機能が働いていたのでこれも削除。間違っている部分があれば、どこかで矛盾することで発見される。これは紙とペンでパズルを解く時と同様であり、緊張感がある。

ここまで書いてルール適用とヒントボタンから機能を消すだけでチェックボタンは残しておいても良かったかもしれないと思ったので復活させたりするかもしれない。押さなければいいだけだからだ。

ところで最近、定石ルールを追加したからなのか妙にMasterの問題が難しくなった気がするので、軽量Lookaheadの深さに制限を付けてみた。まあ、今まで無制限だったのがおかしいのだけども。

この状態でも職人高難易度問題は解けているので、生成される問題がめちゃくちゃ簡単になったりはしていないはず。

あとメモリ使用量がやたら多かった(1.6GBも食ってた)ので、いろいろと手直しして300MB切るぐらいに減らしておいた。

https://mirichi.github.io/loop_course_puzzle/

高難易度問題

現状で難易度的には足りている。10x10Masterでなんとか解けるぐらい、15x15Masterになるとちょっと無理かなーって感じのあたり。

ここからどういう方向性で進めるかというのは悩みどころである。

パズルという観点で考えると、解いてて楽しい、理論的な仕掛け、芸術性、そんな感じの話を求めると、これはテーマを設定する必要が出てきて、そういうのを作るのは難しい。テーマを自動生成することはできない。

ところでいまの難しさは高難易度に設定した手筋の賜物である。具体的には軽量Lookaheadの効果がデカい。簡易的な先読み。ループの検出。パズルは理論なので突き詰めると無制限の先読みが必要になれば難易度は上がる。でもやりたいのはそういう事ではないのだ。

ちょっと調べていたところ、スリザーリンク狂の人のドキュメントがあった。

スリザーリンク手筋収集 by fff 公開用 - Google ドキュメント

これはすごいな。今の俺でも知らない定石が書かれている。覚えるのも難しそうだが、こういうのを定石ルールに組み込んで、知らないと解くのが難しい、みたいな問題を生成できるようにするのもアリかもしれない。でも単純に追加するとEasyが難しくなってしまう。

このドキュメントを一通り眺めてから、3が一杯の問題を眺めると、ちょっと面白い。

ひとつおきに3が並んだスリザーリンクを解く|もうふ

記事で解き方を解説しているのだが、市松模様に塗るというアルゴリズムは俺も追加するかどうかを悩んだ手の一つである。でも個人的によくわからないので保留にした。

じゃあこれをどうやって解くのかと言うと、上のスリザーリンク狂氏の定石の一つにあった、0と2の斜め隣接パターンを使う。ユニークネスと紹介されているやつだ。0と2が斜めに隣接していて、かつ、2の上下左右が空白だった場合、2の0に近い側の2辺が確定するし、遠い側の点を通る、外側から入って外側に出る2辺も確定するのだ。

何故に確定するのかというと、複数回答があり得る問題は問題として成立しないからであり、0と2が斜めに隣接していて、2の上下左右が空白の場合、0から遠い側の2辺が線であるなら、近い側の2辺が線であってもループは成り立ってしまうのである。ゆえにそれはあってはならない。0に近い側の2辺が線でなくてはならない。そうなる必要があり、そのためには0から遠い側の2の点を通る線が無ければならない。そういう理由だ。

複数回答が無いことを逆手に取った解法は実際に有効だが、なんというか、問題の作成者の狙いではないような気がするので、多用は禁物だ。

そういうのをソルバーに組み込むのも面白そうではあるが。なんでも追加すりゃいいってもんでもないわな。

タバスコ

タバスコってあるじゃん。辛いやつ。

職場でタバスコを話題にした時に、若者が「タバスコって料理の味を壊すんですよね~」とかなんかの受け売りみたいなことを言っていたので、ちょっと考えていたのだが。

まあ料理に後から追加する調味料は合う/合わないを自分で判断して使うべきものなので、タバスコをかけたことによって料理の味が壊れたとすればそれは個人の責任以外の何物でもない。合わないものにかける奴が悪いのである。

そういう大前提はさておき、タバスコは塩と酢と唐辛子から作られている。日本人としては塩と酢をぶっかけて食べる物は想像が付かないわけだが、英国ではフィッシュアンドチップスやパイなんかを店で食べる際には、「どうぞご自由にお使いください」てな感じで塩と酢が置かれていたりする。つまりは、奴らは「なんか味が物足りないな」と思ったときには塩と酢をかけておけば良いと考えている。塩と酢が万能調味料なのだ。

実際には英国紳士は酢をこれでもかと言うほどドバドバかけるらしいのだが。

米国人も成り立ちから考えると似たようなものだろう(暴言)。だからアメリカでタバスコが生まれたわけだ。ちょっと辛みを追加したいときに使える万能調味料。塩と酢と唐辛子をブレンドすればそれは理想のホットソースになるんじゃないか!

うん、目玉焼きに何をかけるかで殴り合いの喧嘩が発生する日本ではちょっと考えられないことだが、そういう発想なのだ。目玉焼きに塩と酢をかけるという話は寡聞にして聞いたことが無いが、ともあれタバスコは日本人が適当に何にでもかけて使えるものでは決して無い。

まあ、塩と酢と唐辛子で味付けしておいしくなりそうだと思った物にかけてお使いくださいな。

そう考えるとあんまり思いつかんな。

難易度保証

ループ検出が必要な手筋が無制限に入ると難しい気がするが、それでもこれを大技に含めて制限すると簡単になりすぎる。

このあたりは俺自身がもう少し成長すればちょうどいい難易度になるのでは、ということで、ループ検出はMasterでは無造作に組み込まれるようにしておいた。5x5Masterでも少し考えさせられる問題が出たりしてなかなか楽しい。

さて、必要とする手筋により問題の難易度が変化するという理由で、問題生成時に使用可能な手筋を制限するロジックは入れたが、逆方向、つまり、例えばMasterなのに簡単な手筋だけでクリア可能な問題というのが出力される可能性は残っている。

そういえば前にバラエティ豊かな手筋が使われる問題を生成するようなのもやっていたがあまり効果が無いので今は消している。あれからロジックも増えているし、今の考えにはそぐわないかもしれない。

で、簡単な問題が出ない、安定して難しい問題が出せるように、モードごとに最低必要難易度手筋というのを設定して、それらが含まれていなければ作り直し、という感じにしてみた。

ところがMasterを最低難易度9に設定したところ、問題生成にとても時間がかかるようになってしまった。難易度9の手筋は無制限の軽量LookaheadとGF2だが、これらを使う必要が無い問題が、40x20のMasterでも頻繁に出力されているということを意味している。逆に言えば難易度8までの手筋で大半の問題は解けるということだ。

今はこのぐらいでちょうどいいかなと思っているが、先を考えると、ここから難易度を上げる手段はもう大技使用頻度制限を解除するか盤面を大きくするぐらいしかない。

問題生成速度や描画速度は継続して改善しているのでそろそろもっと大きな盤面の問題を作れるようにしてもいいかもしれないが、それとは別に、ランダムにループを作ってランダムにヒントを削るというやり方自体に見直しを入れるべきなのかもしれない。こっち方向は今まで全く考えていなかった。

と言ってもループを作る段階で問題の難易度が予想できるとも思えないし、ポイントはヒントの削り方か。今は単純に削るか削らないかで処理しているが、これが例えば難易度の高い部分にヒントを追加する、みたいなことができるようになると、もっと柔軟な難易度調整ができるようになるはずだが、この方向性では難易度を上げることができないな。

前にGeminiさんが焼きなまし法を提案してきたことがあって、ヒントABCがあった時に、Aを削るとBCが削れなくなるが、Aを削らないとBCが削れるみたいな場合に、ABCの順で処理するだけではなく、色んなパターンを試すことでヒントを最小化できる、とかそんな話だった。計算がアホほど多くなりそうだったので却下したが、そんな感じのことをまた考えてみてもいいのかもしれない。

ループ検出

軽量Lookaheadを分解して、3つのモードにわけてみた。

「既存の線から伸ばしてループを検出するだけ」「既存の線から伸ばして矛盾も検出する」「未確定のエッジを入れてみて矛盾も検出する」である。それぞれ難易度を6、8、9にして、Medium、Hard、Masterで使うようにした。

他いろいろ調整してこのようになった。

職人の高難易度パズルはループが検出できればわりといける。

さて、軽量Lookaheadのループ検出モードは既存の線から伸ばして、セルとドットの充足だけを見て線やxを確定していく、という処理をしている。これはとても簡単な処理なので高速であり、ループを検出するだけなので人間にとってもさほど難しいものではないだろう、と、そう思っていた。

が、違った。

現状、「ルール適用」ボタンを押すとその盤面で使える最も難易度の低い手を使って確定するが、詰まったときに押すと「Global (Loop Sim-Lookahead)」と表示されることがとても多い。これがループ検出モードなわけだが、いつも、「え?まじで?」って思って確認して、ほんまにループするわーって驚いている。

既存の線を伸ばして、xを確定させて、そのまま線を伸ばしていくというのは、やってみないとどうなるかが想像できないのだ。これもLUTで確定する手筋のようにそのうち雰囲気がわかるようになってくるのかもしれないが、少なくとも今はまるでわからない。

現在リリースしてある物は解き味を重視していて、難易度の高い手を連続して使わず、使った後は簡単な手が少し続くような問題を生成している。これは軽量Lookaheadが必要な手が連続されると非常に辛く、やっていて楽しいパズルではないからであり、そもそもこのパズルのプログラムは俺が楽しく無限に遊べるように作っているわけなので、辛いのは論外なのである。

ただ、この簡単な手の中にループ検出の軽量Lookaheadが含まれてしまっているのは少し計算外だった。いやほんと簡単だと思ってたのだがな。

https://mirichi.github.io/loop_course_puzzle/

 

コリドー法と軽量Lookahead

現在のループコースパズルのソルバーはそこそこの定石ルールや大域定理を突っ込んでいるが、実は早期閉路に関しての検出が弱い。解けずにLookahead(先読み)ロジックに頼っている部分はたいがいループ検出ができていないからだ。

分析モードではLookaheadが動くから難しい問題も解けるが、問題生成時には動いていない(遅いから無効にしている)ので、生成する問題はそういう意味でソルバーの能力限界よりも簡単な問題となっている。それでも相当難しいのだが。LUTとGF2のせいで。

この問題についてGeminiさんに相談してみると、コリドー(Corridor)法と軽量Lookaheadを提案された。ここ数日、Geminiさんの提案はハズレが続いていたのだが、今回はよさげだなと実装してみた。

まずはコリドー法だが、これはゲーム他いろんな分野で使われるグラフ理論のアルゴリズムで、平たく言えば障害物を避けて行動できる範囲を先に計算しておく、みたいなやつだ。ループコースパズルで適用する場合、先に計算もなにもないので、エッジを一つ確定させたときに、その先のドットの周囲にxが2つあったら、そのまま先に進むことができる、という感じで、一本道を検出して線を引いて行って、エッジにぶつかったらループを調べる、みたいに使う。

これは実際、そこそこの効果があった。現状のLookaheadやGF2を削減するほどの効果は無いが、いままで検出できないループを検出することは確かにできた。ただ、ロジックの組み方に問題があったようで、非常に遅かった。

 

次にもう一つの軽量Lookaheadだが、これは現状の汎用Lookaheadがとても遅いことを嘆いた結果で、汎用LookaheadはGF2以外のルールを全て適用しながらバックトラックをするのが原因で時間がかかる。盤面の確定状態をコピーして保存して、戻す必要があるのも問題だ。

しかしLookahead時にGF2のような重い処理を使って確定させる必要が無かった(GF2を無効にしても効果が変わらなかった)というのは重要な情報で、つまり、先読みで矛盾を探す際には、難しいことを考える必要が無いことを意味する。

これを押し進めると、Lookahead時に大域定理は必要ない、となるが、これは昨日やってめっちゃ遅かったので却下済である。今日の軽量Lookaheadは極限まで押し進めた形となり、コリドー法を拡張したようなものになる。

既存のエッジから1手確定して進めるが、この時、マスの充足およびドットの制約に基づいて、線とxを確定させる(ここがコリドー法より進歩したところ)。これをスタックに積んでおいて、再帰で処理する。ダメだったら元に戻すが、この処理はスタックから戻すことにより、盤面のコピーを発生させない。また、線やxを確定させるときに矛盾を検出できるから、ループ検出だけではない。難しい定石ルールは一切無視、基本中の基本ルールのみを適用する。

これを実装した結果、いつもの職人パズルの分析結果は以下のようになった。いつもお世話になっています。本当にありがとうございます。

難易度9はGF2だけじゃなく、軽量Lookaheadもここに含まれている。従来の汎用Lookaheadが難易度10で、ほとんど発生しなくなった。簡単なルールを見るだけで先読みは大概が成り立つのである。逆に言えば、先読みが必要な職人パズルは先読み中に難しい定石を必要としないように設計されている。例えば先読み中にGF2のような理論を要求されれば現実的には先読み中の先読み、多段先読み化してしまう。それは無理ゲーだ。

またこの軽量Lookaheadは強烈に軽い。汎用Lookaheadと比べるまでもなく、コリドー法よりもはるかに軽く、他の大域定理よりも軽い。ループ検出関連、例えば仮想パスなども重い処理だが、これも場合によっては消してもいいかもしれない。ぐらいには速いし強い。

問題生成時に使うことを考えると、基本的に先読みなのでこれがあると難易度は跳ね上がる。ただ、先読み中の使用定石は基本ルールのみなので汎用Lookaheadを発動するよりも簡単だろうと言うことが想像できる。まあ職人パズルレベルだが。

難易度の調整は問題になるかもしれない。Masterだけにとどめておくべきか?まあ、最後にビルドしようとしたらGeminiさんがクォーターに達してしまったのでリリースはお預けなのだが。

AIが無いとビルドすらできない軟弱なエンジニアになってしまった。いや環境とかオプションとかがややこしくてな…

解き味分析3

難易度の分類を変更し、バグで分類できてなかったものを整理し、定石を追加し、新アルゴリズムを追加した。BoundaryLUTの確定範囲の拡張も実装してみた。

結果、グラフの形は随分と変わってしまったが、職人さんの高難易度パズルの分析結果はこのようになった。いつもお世話になっております。

難易度4以下は通常の定石ルールで、分類は適当なのでスルー。5は早期閉路防止の簡単なやつとジョルダン曲線定理。6は仮想パス、7はブリッジ制約、8はLUT、9はGF2、10が先読みとなっている。この分類自体も適当ではある。

LUTはまとめて確定するのではなく、分析時は1手確定するごとに終了するように変更した。これにより必要なときにLUTで少しだけ確定して、その結果を使って定石ルールが次々に確定していく様子が見えるようになった。なのでGF2と難易度入れ替え。

このグラフより、LUTが必要な場所は実はほんの少しだったこと、定石ルールとアルゴリズムを足したらGF2と先読みが減ったこと、仮想パスで確定している場所が実際にあって効果が得られていること、早期閉路防止でxを打つことが意外に多いこと、そして、GF2と先読みが終盤に集中していることなどがわかる。

分析アルゴリズムは人間の手筋を再現することを目標にしていて、難易度の低い手から順番に使い、確定できなければ難易度を上げていって、1手確定できたらまた難易度の低い手から使う、ということをしている。従って難易度の高い手筋を使った場所は、それより低い難易度で確定できなかったことを意味している。

 

分析時には手筋をcsvで出力して、それを読み込んでグラフにしているのだが、このcsvの任意の位置の状態をPencilBox形式で出力して、Webアプリで読み込んで再現することができるようにしている。PencilBox形式は問題自体だけではなく、エッジの状態も保存できる形式である。

ルール適用ボタンもソルバーが確定させているが、アレとは適用順が違うし、一致していたとしても1000ステップ目が見たいからといって1000回ボタンを押すのもつらい。

で、先読みが適用されたあたりの状況を再現して眺めたりしていたのだが、職人が作るパズルは巧妙で確かに今のアルゴリズムでは逆立ちしても確定できないパターンになっていた。俺が見ると解き方はわかるのだが、それを処理する適切なアルゴリズムが思いつかない。

職人パズルが先読み無しで解けないということは、現状の問題生成ロジックでは職人パズルレベルの問題が生成できないということを意味する。

ふむ。まだまだ遊べそうだなこれ。