Detour を、5 段のソルバー内蔵でブラウザに実装した。ルールは「全マスを 1 回ずつ通る一本の閉ループを引く。領域内の数字は、ループがその領域で曲がる回数」。効いているのは「全マスを 1 回ずつ」の一語で、これはループについてのヒントではなくループそのものだ。答えは格子グラフのハミルトン閉路以外にありえない。だから数字を読む前から全マスの次数は 2 で確定していて、残る自由度はマスあたり 1 ビット(2 本が直進か曲がりか)だけ。厳密に数えると 8×8 で 4,638,576、14×14 で 56,126,499,620,491,437,281,263,608 を 35 秒(OEIS A003763 と一致)。盤に印刷されていない 2 条項の値段も測った:8×8 で「一本のループ」を落とすと 77.8 倍、「全マス」を落とすと 130,178 倍。そして分岐点で測ると、印刷された数字より印刷されていない条項の方がよく効く(中央値 1,155 → 46)。全 37 テスト。ソルバー内蔵パズル第 64 弾。
デモ: https://sen.ltd/portfolio/detour/
リポジトリ: https://github.com/sen-ltd/detour
ルール
- 盤はいくつかの領域に分かれている
- セル中心を通る一本の閉ループを引く
- ループは全マスをちょうど 1 回ずつ通る
- 領域内の数字は、ループがその領域内で曲がる回数
ルールは実装前に一次資料で確認した(puzz.link のルール DB と cross-plus-a、2 つの独立した記述が一致)。過去に「ルールを間違えたまま実装して捨てた WIP」を何度かやっているので、ここは省略しない工程にしている。
「全マスを 1 回ずつ」はヒントではなく、ループそのもの
これまでに作ったループ系(スリザーリンク、ましゅ、ヤジリン、カントリーロード)は、ヒントが全部「ループがどこを通るか」を語っていた。どのマスを使い、どのマスを使わず、どう縫うか。
Detour にはその問いが無い。「全マスを 1 回ずつ」は答えの定義であって、ヒントではない。つまり:
答え = 格子グラフのハミルトン閉路。それ以外ではありえない。
結果、数字を 1 つも読まない段階で、既に全マスの次数が 2 に確定している。残った自由度は、マスごとに「その 2 本が**向かい合わせ(直進)か直交(曲がり)**か」という 1 ビットだけだ。盤の数字は、この 1 ビットについてしか語らない。
これがどれくらい極端かというと、数字を全部消した盤でソルバーを限界まで回して確定するのは、たった 8 本しかない。
| 盤 | 盤の辺 | 数字を全部消して確定する辺 | その正体 |
|---|---|---|---|
| 6×6 | 60 | 8 (13.3%) | 四隅、各 2 本 |
| 8×8 | 112 | 8 (7.1%) | 四隅、各 2 本 |
| 10×10 | 180 | 8 (4.4%) | 四隅、各 2 本 |
四隅は辺が 2 本しかないマスなので両方使うしかない。それ以外は 1 本も出てこない。 逆に言えば、この盤の情報は全部「数字」と「印刷されていない構造条項」に載っている。
厳密に数える — プラグ DP
答えがちょうどハミルトン閉路なので、「この盤に答えは何通りありうるか」は他人が既に発表している数だ。だから数え上げ機は、それを再現できなければ嘘ということになる。
使ったのは連結性プロファイル掃引(プラグ DP)。セルを読み順に走査し、w + 1 個のプラグ(各列の走査線を横切る縦辺 + 直前セルからぶら下がる横辺)を frontier として持ち回る。プラグは部分パスの端点なので必ず 2 個 1 組で、状態は「どのプラグとどのプラグが繋がっているか」。初出順に正規化すると、閉路の数がいくら爆発しても生きている状態数は小さいままだ。
| 盤 | ハミルトン閉路 | 既発表値 | frontier 状態数 | 時間 |
|---|---|---|---|---|
| 2×2 | 1 | 一致 | 4 | 1 ms |
| 4×4 | 6 | 一致 | 77 | 1 ms |
| 6×6 | 1,072 | 一致 | 1,119 | 2 ms |
| 8×8 | 4,638,576 | 一致 | 12,852 | 14 ms |
| 10×10 | 467,260,456,608 | 一致 | 134,010 | 205 ms |
| 12×12 | 1,076,226,888,605,605,706 | 一致 | 1,333,112 | 2,693 ms |
| 14×14 | 56,126,499,620,491,437,281,263,608 | 一致 | 12,910,377 | 35,091 ms |
この列が A003763。最後の行は 5.6×10^25 個の対象を 35 秒で数えている。コードを一切共有しない DFS 列挙器が、届く範囲(6×6 まで)で同じ数を返すことも確認した。「速い」だけでなく「正しい」ことの担保はそこにある。
そして、ただで手に入る強い判定がひとつ:
格子グラフは二部グラフなので、ハミルトン閉路は色を交互に踏む。奇数×奇数の盤には、数字が何であれ解が存在しない。
3×3, 5×5, 7×7, 9×9, 11×11 いずれも 0。ソルバーは盤を見る前に return する。
盤に印刷されていない 2 条項の値段
ルールには、盤のどこにも書かれていない構造条項が 2 つある。「全マス」と「一本のループ」。同じ掃引で、それぞれを外したときの答えの数が数えられる。
| ルールの読み方 | 4×4 | 6×6 | 8×8 | 8×8 での倍率 |
|---|---|---|---|---|
| 両方の条項 | 6 | 1,072 | 4,638,576 | — |
| 複数ループ可 | 18 | 13,903 | 360,783,593 | 77.8× |
| マスを飛ばしてよい | 213 | 1,222,363 | 603,841,648,931 | 130,178× |
| どちらも無し | 321 | 5,735,477 | 11,282,914,491,065 | 2,432,411× |
「マスを飛ばしてよい」の行は A140517(n×n 格子の閉路の数)で、「複数ループ可」の行は A222202(頂点素な閉路被覆)。同じ DP が 3 つの既発表数列を再現するので、汎用化した実装が壊れていないことの独立な検算になっている。両方落とした 1, 13, 321, 23857, 5735477, 4468252413 は、2026 年 9 月時点で OEIS に該当が無かった。
2 つの条項の重さが全く違うのが面白い。「全マス」は 13 万倍、「一本のループ」は 78 倍。桁が 3 つ違う。 ……のだが、後で見るように、探索で効くのは軽い方だ。
曲がりの総数は必ず偶数。でも下限 4 は役に立たない
閉じた直交曲線は一周で 360 度回るので、右折が左折をちょうど 4 だけ上回る。R − L = 4、よって総曲がり数 T = R + L = 2L + 4。
全領域に数字が入っている盤では、数字の総和は必ず偶数。
これはタダで手に入る大域テストで、出荷 108 盤すべてで成立した。ついでに T ≥ 4 という下限も出る。そしてこの下限は完全に無意味だ。 曲がりを消費させているのは「盤を埋めること」の方で、掃引は閉路を曲がり数で分割できるので、そこを厳密に言える。
| 盤 | マス | 最小の曲がり | 最大の曲がり | 平均 | 相異なる総数 | 全部偶数 |
|---|---|---|---|---|---|---|
| 4×4 | 16 | 8 | 12 | 9.3 | 2 | yes |
| 6×6 | 36 | 12 | 28 | 20.0 | 9 | yes |
| 8×8 | 64 | 16 | 56 | 35.5 | 21 | yes |
| 10×10 | 100 | 20 | 88 | 55.9 | 35 | yes |
多角形の理屈は「4 以上」と言う。実際の床は「短辺 × 2」で、表のどの盤でも例外なく成立する。そしてこの最小値を達成するのは、手で描くならまずこれを描くという蛇行路(1 列だけ縦に降りて、残りを横に往復する櫛型)そのものだ。天井の方にはそんな綺麗な形は無いし、どのサイズでも「全マスが曲がり」の答えは存在しない。
平均は作問者が覚えておくと良い数字で、10×10 の答えは 100 マス中 55.9 マスが曲がる。出荷盤の実測も 55.9% で一致する。これは偶然ではなく、答えを全閉路から一様抽出しているからそうなる — つまりサンプラが本当に一様であることの、一番安いチェックになっている。
数字 1 つの値段
6×6 の答えは 1,072 通りしかないので、「この数字は何を排除しているか」を全数で答えられる。出荷 6×6 盤の全領域について、その答えが含意する数字を印刷し、生き残る解を数えた。
| 残る解 | 6×6 全解に対する割合 | |
|---|---|---|
| 6×6 の全解 | 1,072 | 100.0% |
| 領域 1 つの数字(332 個の中央値) | 327 | 30.5% |
| 最も鋭い 1 つ | 10 | 0.9% |
| 最も鈍い 1 つ | 760 | 70.9% |
| 出荷盤が実際に印刷する数字全部 | 1 | 0.1% |
中央値の数字は解空間の 30.5% を残す。一番鈍いものは 70.9% を残す — ほとんど何も言っていない。この genre は「1 つの数字で決まる」ことが無い。 全部が同時に生き残る解が何通りか、だけで決まる。
全領域に数字を書いても足りない
生成器は、答えを一様に引き、領域をランダムに切り、全領域に真の曲がり数を書く。その分割が運べる最大限の情報だ。それでも、たいてい一意にならない。
| 盤 | 試行 | 全領域に数字を書いて一意 | 率 |
|---|---|---|---|
| 6×6 | 200 | 149 | 74.5% |
| 8×8 | 200 | 86 | 43.0% |
| 10×10 | 60 | 7 | 11.7% |
10×10 では 88.3% が「全部書いても第 2 の答えがある」。生成器の時間の大半は、これを捨てることに消えている。出荷盤はそこから逆向きに進み、一意なままでいる限り数字を消していく。残るのは中央値で 5 個 / 10 個 / 16 個だ。
ダイヤルは効く。ただし生成器が回しているダイヤルはそれではない
出荷盤の完全ヒント集合からランダムに部分集合を取ると、一意率は数字の数に応じて上がる。ちゃんとダイヤルはある。ただし立ち上がりが遅い。8×8 は 9 個以下ではどう選んでも一意にならない。
(最終行が 100% なのは構成上当たり前で、これらの盤は「全部書けば一意」であることを条件に選ばれている。)
生成器が本当に回しているダイヤルは分割の方だ。出荷済みの答えを 1 つ取り、答えには一切触れず、上から領域だけを 40 回引き直して毎回全領域に数字を書く。12 個の答え × 40 回 = 480 回のうち、一意になったのは 257 回(53.5%)。答えごとの幅は最良 27/40、最悪 16/40。
答えも規則も数字の数も同じ。パズルになるかどうかは、壁がどこに落ちたかで決まる。
領域サイズも効く。小さい領域は 0〜2 しか言えないが数が多くループの角と密に重なる。大きい領域は大きい数を言うが、それが多数のマスに散っていて何も固定しない。
| 目標領域サイズ | 8×8 の領域数 | 試行 | 全領域に数字を書いて一意 |
|---|---|---|---|
| 2 | 24.8 | 120 | 82 (68.3%) |
| 3 | 20.6 | 120 | 78 (65.0%) |
| 4 | 16.1 | 120 | 51 (42.5%) |
| 5 | 13.4 | 120 | 36 (30.0%) |
| 6 | 11.5 | 120 | 32 (26.7%) |
| 8 | 9.2 | 120 | 10 (8.3%) |
出荷盤は 6×6 / 8×8 で 4、10×10 で 5 を使っている。一意盤が一番たくさん採れる設定ではなく、数字 1 つが読む価値を持つだけのマス数がある設定を選んだ。
梯子 — そして盤に載っていない段
-
degree— 全マスの辺はちょうど 2 本。4 ビットの算数で、四隅を確定させるのはここ -
turn— 領域の数字を直進/曲がりビット経由で読む。曲がりと確定したマスは「各軸から 1 本ずつ」なので、1 ビットの確定が最大 4 本の辺を決める -
cut— 任意のマス集合について、ループはその境界を偶数回、かつ内部に到達する必要があるので2 回以上横切る。全領域と、盤を貫く全ての直線カットに適用 -
loop— 盤全体を飲み込む前に閉じる閉路は禁止 -
probe— 辺を仮定し、下位段を回し、盤が死んだら捨てる
| 段 | 6×6 確定辺 | 6×6 完答 | 8×8 確定辺 | 8×8 完答 | 10×10 確定辺 | 10×10 完答 |
|---|---|---|---|---|---|---|
degree |
13.3% | 0/36 | 7.1% | 0/36 | 4.4% | 0/8 |
turn |
29.0% | 0/36 | 21.0% | 0/36 | 14.7% | 0/8 |
cut |
30.9% | 0/36 | 21.2% | 0/36 | 14.7% | 0/8 |
loop |
36.9% | 1/36 | 24.0% | 0/36 | 15.6% | 0/8 |
probe |
94.7% | 33/36 | 71.3% | 18/36 | 42.6% | 0/8 |
伝播だけで終わるのは 6×6 で 33/36、8×8 で 18/36、10×10 は 0。正直に言えば「数字が局所的には弱く、たいていの盤はどこかで場合分けが要る」ジャンルだ。
では段を枝刈りとして値付けするとどうなるか。完全探索を回すが、分岐の間に許す伝播を段ごとに制限して、分岐点を数える。
| 許した伝播 | 6×6 分岐点(中央値) | 6×6 最悪 | 8×8 分岐点(中央値) | 8×8 最悪 |
|---|---|---|---|---|
degree まで |
13,902 | 13,902 | 20 万上限超 | 36/36 が上限超 |
turn まで |
46 | 649 | 1,412 | 26,135 |
cut まで |
35 | 402 | 1,155 | 21,843 |
loop まで |
10 | 68 | 46 | 1,232 |
1 つだけ残すならこの表を残す。 8×8 の中央値は、数字を読むことで 20 万超から 1,412 まで落ちる。そのあと、盤のどこにも印刷されていないたった 1 つの条項 — 辺が成すのは一本のループであって複数ではない — を足すと、1,155 から 46 まで、さらに 25 倍落ちる。
印刷された数字はよく効く。印刷されていない条項の方がもっと効く。
(前の節で「全マス」条項の方が答えの数では 3 桁重かったことを思い出してほしい。空間を狭める力と、探索を刈る力は別物だ。「全マス」は degree 段として最初から構造に埋め込まれているので探索中に稼ぐ余地が無く、「一本のループ」は部分解を見ながら初めて効く。)
読み違えは対称ではない
| 読み違え | 盤 | 意図した答えが合法のまま | 答え(中央値、上限 12) | それでも一意 |
|---|---|---|---|---|
| 複数ループ可 | 6×6 | 36/36 | 8 | 0/36 |
| 数字を直進の数と読む | 6×6 | 0/36 | 0 | 3/36 |
| 数字を全部無視 | 6×6 | 36/36 | 1,072 | 0/36 |
| 複数ループ可 | 8×8 | 36/36 | 12 | 0/36 |
| 数字を直進の数と読む | 8×8 | 0/36 | 0 | 0/36 |
| 数字を全部無視 | 8×8 | 36/36 | 4,638,576 | 0/36 |
数字を「直進の数」と読むのはうるさい失敗だ。両サイズとも 72/72 盤で意図した答えが違法になるので、最初の演繹で気づく。出荷できない。
「複数ループ可」は静かな失敗で、こちらが危ない。一本のループは複数ループの特殊ケースなので、意図した答えは合法のまま残る。矛盾は起きない。ただ、72/72 盤で一意性が消える。守るべきはこちらで、守っているのがちょうど loop 段だ。
まとめ
- Detour の答えは格子グラフのハミルトン閉路そのもので、数字が語るのはマスあたり 1 ビットだけ
- プラグ DP で厳密計数(A003763 / A140517 / A222202 の 3 数列を再現、14×14 を 35 秒)
- 総曲がり数は常に偶数、最小は短辺 × 2
- 全領域に数字を書いても一意率は 10×10 で 11.7%。生成器が回しているダイヤルは分割
- 分岐点で測ると、印刷されていない「一本のループ」条項が印刷された数字よりよく効く
ページと README の数値は全て stats.json / counts.json から生成していて、手書き転記はゼロ。全 37 テスト。
デモ: https://sen.ltd/portfolio/detour/
リポジトリ: https://github.com/sen-ltd/detour
