0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

カナルビュー — 数が見ているのは行と列ではなく「手前の端」だけ、白1マスで運河は消える

0
Posted at

カナルビュー(Canal View)を、4 段のソルバー内蔵でブラウザに実装した。ルールは「数字は四方向に見える黒マスの合計」。効いているのは見えるという一語だ。連なりは最初の白マスで止まるので、その白の向こうにどれだけ運河が続いていても数には入らない。つまり数字は自分の行と列についての主張ではなく、その手前の端についての主張でしかない。出荷 72 盤で測ると、8×8 の数字は自分の四方向に実在する黒 5.28 マスのうち 3.84 しか見ておらず(72.8%)、12×12 では 64.7% まで落ちる。12×12 では白 1 マスが、それを正面から見ている数字に対して黒の連なり 10 マスを隠していた。視線を二度数えると 4 × 運河 = 見えた + その方向に数字が無い + 白に遮られた がぴったり成立し、8×8 で 4,616 = 1,576 + 2,451 + 589。ソルバー内蔵パズル第 61 弾。

デモ: https://sen.ltd/portfolio/canal-view/
リポジトリ: https://github.com/sen-ltd/canal-view

Canal View

ルール

いくつかのマスを黒く塗る(この記事とアプリでは「運河に沈める」と呼ぶ)。

  • 黒マス全体が辺で連結した 1 つのかたまりになる
  • 2×2 が全部黒になってはいけない
  • 数字のマスは常に白。その数字は、四方向に見える黒マスの合計。自分の隣から始まる連なりを足す

以上。ルール自体は 3 行で終わる。にもかかわらず、この 3 行のどこが実際に難しさを作っているのかは、書いてみるまで自分の予想と一致しなかった。

「見える」という一語

数字の光線を外に向かって歩くと、答えがどうであれ形は必ずこうなる。黒が a マス、次に白が 1 マス、その先は何でもよい

「その先は何でもよい」がこの puzzle の全部だ。白 1 マスの向こうに黒が 8 マス並んでいても、数字は 0 としか言わない。数字は行と列の総量ではなく、その手前の端しか報告していない。

出荷している 72 盤(8×8 と 12×12 を 36 枚ずつ)で測った。

数字が言う値 自分の光線上に実在する黒 見えている割合 壁の向こうに運河がある光線 隠れているマス(平均) 白1マスが隠した最大
8×8 3.84 5.28 72.8% 9.5 1.73 6
12×12 5.06 7.83 64.7% 25.5 2.34 10

盤が大きくなるほど、数字は自分の行と列に対して盲目になっていく。12×12 では 1 枚あたり 25.5 本の光線が「壁の向こうに運河を持つ」状態で、最大のものは白 1 マスが黒 10 連を隠していた

同じ 3 という数字が、隣が 3 連なのか、隣が 1 マスで奥に何かあるのか、まったく区別しない。これがクロダコ(黒どこ)系の「見通し」パズルと Canal View を分ける点でもある。

視線を二度数える

黒マス 1 つを固定すると、それを見ている数字は各方向にたかだか 1 つ(最初にぶつかった数字だけ)。だから「(数字, その数字が見ている黒マス)」の組を二通りに数えられる。

  • 数字側から数えれば、それは数字の総和
  • マス側から数えれば、黒マス 1 つあたり 4 以下

したがって探索を一切しなくても運河の下限が出る。上限は 2×2 禁止から出る。h×w の格子で 2×2 を含まない最大の部分集合は hw − ⌊h/2⌋⌊w/2⌋ だ(1 行おきに全部塗り、その間の行は 1 マスおきに塗る)。

⌈Σ数字 / 4⌉ ≤ 運河 ≤ hw − ⌊h/2⌋⌊w/2⌋
数字から出る下限 実際の運河 2×2 から出る上限 数字の総和 ÷ 運河
8×8 11 32.1 48 1.37
12×12 28 72.4 108 1.51

このサンドイッチは緩い。そしてその緩さの理由が、そのまま最初の節の内容になる。上限の 4 が達成されるのは「すべての黒マスが四方すべてから見えている」ときだけで、実測は 1.37(8×8)と 1.51(12×12)だった。足りない視線を分類するとこうなる。

(黒マス, 方向) の組 見えている その方向に数字が無い 途中に白がある 遮蔽の割合
8×8 4,616 1,576 2,451 589 12.8%
12×12 10,432 3,933 4,351 2,148 20.6%

2 列目は運河のマス数のちょうど 4 倍、3 列目はちょうど数字の総和になる。まったく別の経路で計算した 2 つの数が一致するので、この恒等式はテストにしてある(npm test が盤ごとに検証する)。遮蔽は 12×12 で全視線の 20.6% を占め、盤が大きくなるほど増える。

黒マスを「何人の数字が見ているか」で分けると、8×8 では 1,154 マス中 96 マスが誰からも見えていない

光線は 2m 通りではなく m+1 通り

さきほどの「黒 a、白 1、あとは自由」という形は、実装上とても都合がいい。長さ m の方向が取りうる状態は 2^m ではなく m+1(腕の長さ)しかなく、数字 1 つのクロス全体は 値を 4 つの腕に分ける有界な組成(composition) ちょうど 1 つで決まる。

ソルバーが分岐するのは、最後までこの組成の上だけだ。

数字の個数 数字の光線上のマス 2マス 実際に成立する組成
8×8 11.4 8.9 478 8.4
12×12 21.6 13 8,192 17.9

だから完全ソルバーが安い。12×12 の中央値は探索ノード 7 個、最悪でも 319 個、1 枚 22.2 ms で解ける。

ラダーは 4 段。

  • arm — 組成に対する弧無矛盾(arc consistency)。今の盤面と矛盾する腕の割り当てを捨て、生き残った全部が一致する所だけ確定させる
  • block — 加えて 2×2 のうち 3 つが黒なら残り 1 つは白
  • flow — 加えて連結性。もう合流できない 2 つの黒の塊は矛盾、運河が絶対に届かないマスは白、そこを失うと運河が二分されるマスは黒。さらに上のサンドイッチを残りマスの上下限として使う
  • search — 完全ソルバー。最も制約の強い数字の組成で分岐する
確定マス 8×8 確定マス 12×12 完答 8×8 完答 12×12
arm 54.6% 41.9% 0/36 0/36
block 63.7% 53.9% 0/36 0/36
flow 81.5% 67.5% 18/36 6/36
search 100% 100% 36/36 36/36

運河は湖を囲んでいい

Canal View には「白マスが連結していること」というルールが無い。ぬりかべ系に慣れているとつい足してしまうが、無い。そして実際の盤はそれを遠慮なく使う。

運河 盤に占める割合 湖(外から遮断された白の領域) 湖のある盤 独立サイクル数 ループのある盤
8×8 32.1 50.1% 3.5 36/36 2.1 35/36
12×12 72.4 50.3% 12.1 36/36 6.8 36/36

湖を囲む運河とは、サイクルを持つ運河のことだ。ここで 2×2 禁止が効いてくる。2×2 を含まないのに木でない最小の形は 8 マスのリング(3×3 の外周)で、しかも 8 マスではそれが唯一である。固定ポリオミノを両方の基準で数えると、2 つの概念が分かれる瞬間がそのまま見える。

マス数 固定ポリオミノ 2×2 を含まない 木構造 2×2 無しでサイクル有り
7 760 570 570 0
8 2,725 1,909 1,908 1
9 9,910 6,485 6,473 12
10 36,446 22,282 22,202 80

2 列目と 4 列目は登録済みの数列(A001168A066158)で、npm run ledger は列挙器がどちらかとずれた瞬間に出力を書かずに落ちる。3 列目(運河が実際に満たすべき条件)はどちらでもなく、OEIS にも見当たらなかった。

小さい格子に収まる運河そのものも数えた。全 2^hw 部分集合を 1 つずつ試す方法と、アンカーから成長させる方法の 2 通りで数え、一致を要求している(4×5 で連結集合 116,166 のうち運河は 25,822、22.2%)。

1 マス反転させると、どのルールが怒るか

出荷した答えの 1 マスを反転させる。6,301 通りすべてが不正だった。問題はどのルールがそれを止めたかだ。

ルール 止めた反転 そのルールだけが止めた反転
数字 5,298 2,974
2×2 禁止 932 197
運河の連結性 2,395 806
どれも止めない 0

15.9% は 2×2 か連結性だけが止めている。 言い換えると、この 2 つのどちらかを落とした瞬間に、答えの 1 マス編集がその数だけ合法になる。これは次の表で実際に測れる。

4 通りの誤読と、その結末

誤読 枚数 意図した答えが合法か 解の個数(中央値) 一意 解無し
2×2 禁止を落とす 8×8 24 24/24 32 2 0
12×12 24 24/24 ≥2,000 0 0
連結性を落とす 8×8 24 24/24 1,248 0 0
12×12 24 24/24 ≥2,000 0 0
白も連結させる 8×8 24 0/24 0 0 24
12×12 24 0/24 0 0 24
数字を行・列全体と読む 8×8 12 0/12 0 0 11
12×12 12 0/12 0 0 12

上下 2 組は逆向きに壊れる。ルールを落とすと、意図した答えは合法なまま数百の解に埋もれる——だから解けてしまうし、2 つ目の解に気づくまで何もおかしく見えない。無いルールを足す(白も連結させる)か、見えないものまで数えると、意図した答えのほうが違法になり、盤は空になる。存在しない答えを延々と探すことになるのはこちら側だ。

ダイヤルは「残す数字の数」

生成器は答えを先に描く。ランダムな連結・2×2 なしの集合を成長させ、白マス全部に数字を書き、そこから 1 つずつ数字を消していく——一意性が保たれる限り。

白マス全部に数字を書いた盤は、答えを声に出して言っているようなものだ。そこからランダムに消すとどうなるか。

残した数字(8×8) 試行 一意
白マス全部 108 108 100.0%
26 108 83 76.9%
22 108 54 50.0%
18 108 21 19.4%
15 108 6 5.6%
12 108 0 0.0%
10 108 0 0.0%

出荷盤が持っている数字は 8×8 で 11.4 個、12×12 で 21.6 個。ランダムに 12 個まで削った 108 回の試行で一意は 0 回(12×12 で 22 個に削った 108 回も 0 回)。出荷盤の数字は 1 つ残らず「消してみて、駄目だったから戻した」ものだ。

そして表の 1 行目には落とし穴がある。白マス全部に数字を書いても一意とは限らない。 新しく引いた 8×8 の運河 200 本のうち 3 本は、全マスに数字を書いてなお 2 つ目の答えを持っていた。動いたマスは 3 本とも、どの数字からも見えていないマスだった。答えを全部書き出しても、言ったことにならない場合がある。

偶然には生まれない

数字をランダムな位置に、ランダムな値で置いてみる(実際の盤が使う小さい範囲と、光線が許す全範囲の 2 通り)。1,600 回引いて一意な盤は 0 枚。面白いのは「解無し」の列で、答えの周りに作られていない盤は、そもそも解けないことのほうが多い。要求している数と、それを支える運河の形が、噛み合わないからだ。

2 つのエンジン

組成による分解がこの実装の要なので、それを一切知らないエンジンと突き合わせている。マスを読み順に走査し、黒→白の順に試し、枝刈りは「すでに閉じた 2×2」と「もう値に着地できない数字」だけ、最後にルールを直接検査する。

6×6 の 30 盤(総当たり側 1,939,359 ノード)で、両者は 30 回一致・0 回不一致。しかも解の集合が一致している。2×2 禁止を落として解が数百に増える設定でも 30 回一致(合計 1,126 解)。

まとめ

3 行のルールのうち、盤を難しくしているのは数字ではなかった。数字は運河の 65〜73% しか見ておらず、残りは白 1 マスの陰に隠れている。それでも盤が一意になるのは、見えない部分を連結性と 2×2 禁止が押さえているからだ。1 マス反転の 15.9% はその 2 つだけが止めている。

  • 実装: TypeScript + Vite、ランタイム依存ゼロ、20 テスト
  • 生成: 答えを描いてから数字を削る。出荷 72 盤はすべて「これ以上削れない」局所最大
  • 検証: 完全ソルバーと素朴エンジンの解集合一致、A001168 / A066158 との照合、格子上の運河数を 2 通りで計数

デモとソースは冒頭のリンクから。記事中の数値はすべて npm run stats / npm run ledger の出力から生成していて、README とページの本文はその JSON から機械的に書き出している。

0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?