AIゲーム制作ラボ
Unity×AI6分で読めます

自動生成したパズルが必ず解ける作り方(逆再生+ソルバーで難しさも揃える)

箱押しパズルの面を自動生成しました。クリア状態から巻き戻して作れば必ず解けますが、それだけでは難しさが揃いません。1回引くたびに解かせて狙いの手数に合わせる方法と、狙いが外れ続けた原因、探索が重すぎて A* に変えた経緯を、実測つきで残します。

Libra

公開 2026年9月19日

この記事の要点4 points

  1. クリアした状態から巻き戻して作れば、その面は必ず解ける。 解けるかどうかの判定は要らない。
  2. ただし難しさは揃わない 引いた回数と、本当に必要な押し回数は一致しない(狙いに入ったのは 38/100)。
  3. 1回引くたびに解かせて、狙いの手数に入った瞬間に止める形にしたら 90/100 になった。 1面あたり 2.2ms
  4. 狙いの数字は実測してから決める 作れない難しさを狙っても、外れ続けるだけだった。
目次7
検証した環境最終確認 2026年9月19日
  • Windows 11
  • Unity 6.6(6000.6.0f1)
  • Claude Code(Opus 5 / 思考レベル 高)

書いてあるのは、この環境で実際に試した結果です。ツールの仕様や料金は変わるので、重要な判断の前には公式の情報も確認してください。

箱を押して目印に載せるパズルを作りました。面は自動生成です。この手のパズルで怖いのは、解けない面を配ってしまうこと。押した箱が角にはまれば、そこで詰みます。

答えは単純でした。クリアした状態から巻き戻して作る。

1000種

毎回検査する種の数

全部解ける

2.2ms

1面を作る時間

解かせながら作って

38→90

狙いの手数に入った数

100面あたり

4つ

ソルバーを速くした工夫

最初は1面5秒超

巻き戻して作れば、解けるかどうかは考えなくていい

作り方はこうです。

  1. STEP 01

    クリアした状態を作る

    箱は全部、目印の上

  2. STEP 02

    箱を引いて散らす

    押すのではなく引く

  3. STEP 03

    その配置が問題になる

    引いた手を逆にたどれば解ける

押す操作の逆をするので、作った時点で「解ける」ことが決まっている

押す操作は「プレイヤーが箱の反対側に立って、箱を前に動かす」。その逆は「プレイヤーが下がって、箱が自分のいた場所に来る」です。散らす手はこれしか使いません。

解ける面を選ぶのではなく、解ける面しか作らない。

それでも「難しさ」は揃わなかった

これで解ける面はできます。ところが遊んでみると、3手で終わる面と、10手以上かかる面が混ざる。狙いは面の番号ごとに決めていたのに、そこへ入ったのは100面中38面でした。

理由は単純で、引いた回数と、本当に必要な押し回数が一致しないからです。引いて散らしても、押して戻すときには近道が見つかります。

引き終わってから測る(38/100)

  • 決めた回数だけ引いて、最後に1回だけ解かせる
  • 近道があると、狙いより簡単な面になる
  • 外れたら作り直し。何度やっても当たらない

引くたびに測る(90/100)

  • 1回引くたびに解かせて、最短の押し回数を見る
  • 狙いの範囲に入った瞬間に止める
  • 超えてしまったら、その盤は捨てて次へ

狙いの数字は、実測してから決める

当たらない原因はもう1つありました。そもそも作れない難しさを狙っていたのです。

最初は「10〜24押し」を狙っていました。測ってみると、7×7の盤・箱2個で作れるのは中央値7押し前後。届くはずのない目標を置いて、外れ続けていただけでした。

難しさは押し回数だけで作らない

押し回数を伸ばすより、盤を広げる・箱を増やす・壁を足すほうが素直に難しくなります。いまは面の番号で「6×6・箱1個 → 8×8・箱3個・壁4枚」と上げていき、押し回数の狙いはそれぞれの構成で実際に作れる範囲に合わせています。

ソルバーが重すぎた(1面5秒超 → 2.2ms)

1回引くたびに解かせる」と決めた時点で、ソルバーの速さがそのまま面の生成時間になります。最初の素直な実装では、100面の検査が400秒でも終わりませんでした

01

プレイヤーの歩数で探索していた

症状
枝が4方向×深さぶんに増えて、まったく終わらない
原因
歩数を数えると、同じ箱の配置でも「プレイヤーがどこを歩いたか」で別の状態になる
対処
押した回数で測る。プレイヤーが歩いて行ける範囲が同じなら、同じ状態としてまとめる
02

全部の状態を平らに探していた

症状
解ける面なのに、探索の上限(20,000状態)に当たって「解けない」と返る
原因
幅優先だと、答えに近い枝も遠い枝も同じ優先度で広げてしまう
対処
A* にする。残りを「箱から近い目印までの距離の合計」で見積もる。これ以上小さくならない見積もりなので、最短であることは保ったまま探索が減る
03

詰んだ配置の先まで調べていた

症状
無駄な枝で状態が膨らむ
原因
角(隣り合う2辺が壁)に入った箱は、もう二度と動かせない。そこから先に解はない
対処
目印の上でなければ、その枝を切る
04

探索の中で毎回配列を作り直していた

ここだけで体感が変わった
症状
状態を1つ広げるたびに、盤の大きさぶんの配列を確保していた
原因
「歩いて行ける範囲」を塗るための作業用配列を、その場で `new` していた
対処
作業用の配列を使い回す。作り直すと数十倍遅い

結果はこうなりました。

Git BashOK
# 1面を作る(盤を作る → 引く → 解く をくり返す)
100面 220ms / 範囲内 90/100 / 平均押し 5.7

# 1000種類の種で作った面が、すべて解けることの検査
run_tests	true	33/33 件成功 (EditMode, 7.48s)

作業用の配列を共用して、解ける面を「解けない」と誤判定した

これは自分のバグです。探索の途中で、「いま広げている状態の塗り分け」と「状態をまとめるときの塗り分け」に同じ配列を使っていました。

結果、正しい押し手を取りこぼして、解けるはずの面に「解けない」と返る。生成器はそれを捨てるので、面が1つも作れないという形で表に出ました。

⚠️ 共用しているものが壊れると、遠いところで症状が出る

出たエラーは「面を作れなかった」。原因は探索の中の配列です。症状が出た場所と原因の場所が離れているときは、途中の値をそのまま信じないで、小さい盤で1回だけ解かせて確かめるのが早いです。

できあがったもの

ハコオシのプレイ画面。6×6の盤に、箱・目印・プレイヤーが並んでいる
この方式で作った面。上に「PUSHES 3」と出しているのが、ソルバーが出した最短の押し回数

画面に出している PUSHES 3 は、作るときにソルバーが出した最短の押し回数です。作った瞬間に「この面は3押しで解ける」と分かっているので、そのまま目安として見せています。

まとめ

  • クリアした状態から巻き戻して作ると、解けるかどうかの判定が要らなくなる
  • ただし難しさは揃わない。1回引くたびに解かせて、狙いの手数に入った瞬間に止める
  • 狙いの数字は実測してから決める。作れない難しさを狙っても外れ続ける
  • ソルバーは工夫しないと重い。押し回数で測る/見積もりつき探索/詰みの枝を切る/配列を使い回す4つで 1面 2.2ms
  • 共用した作業用の配列は、遠いところで症状が出るバグになる

FAQ

よくある質問

自動生成したパズルが「解ける」ことをどう保証しますか?

クリアした状態から逆向きに動かして作ります。箱を引く操作だけで散らすので、引いた手を逆にたどれば必ず元に戻せます。つまり作った時点で解けることが決まっていて、あとから判定する必要がありません。

難しさはどう揃えますか?

作った面をソルバーに解かせて、最短の押し回数が狙いの範囲に入ったものだけ採用します。引き終わってから測るのではなく、1回引くたびに測って、範囲に入った瞬間に止めるほうが当たります(38/100 → 90/100)。

ソルバーは重くないですか?

工夫しないと重いです。プレイヤーの歩数ではなく押し回数で測る、A* で残りを見積もる、角に入った箱の枝を切る、作業用の配列を使い回す、の4つで 1面あたり 2.2ms に収まりました。

2本目のゲームでも同じことをしましたか?

同じ目的を別の方法で解きました。2本目は「先に正解の道を引いて、その道が通らない場所だけ塞ぐ」という構造で保証しています。探索が要らない代わりに、作れる地形の形が決まります。

この記事は役に立ちましたか?

NEXT