nkhrlab~

140字超の記事

ドミノを用いたパズルの自動生成

概要

ドミノはトランプと同じくそれひとつでいろいろなゲームを楽しむことができる玩具のひとつである.本記事では,ドミノを用いたパズルの自動生成の手法とサンプルコードを紹介する.

準備

1セットのドミノは28枚の牌からなる*1.それぞれの牌は2つの領域それぞれに0~6のスートのうちいずれかが描かれており,すべての牌のスートの組み合わせが異なっている.

f:id:nkhrlab:20190608020749j:plain
1セットのドミノ牌

日本ではドミノの遊び方が広く知られているとはいいがたく*2,遊び相手を見つけるにも苦労する状況である.そこで,トランプのソリティアのように,ドミノを使って一人でも遊べるパズルを紹介しよう.次にパズルの例題を掲載する.

f:id:nkhrlab:20190608021242p:plain
例題


ドミノの牌をこの図の通りに並べることができればパズルはクリアとなる.ただし,2, 3, 6の目については目の向きが区別される.そのほかのスートの目の向きは区別しない.例題の解を以下に示す.

f:id:nkhrlab:20190608024652j:plain
例題の解
次のような敷き詰め方は誤りである.なぜならば,画像中央の2の目の向きが例題に示したものと異なっているからである.
f:id:nkhrlab:20190608024258j:plain
誤りの例

このように,問題さえ用意できればパズルで遊ぶことができる.しかしながら,問題を作るために牌を並べていたのではその過程で答えを知ることになってしまう.したがって,本当に一人で遊ぶつもりなら問題を自動生成する必要がある.そこで本記事ではパズルを自動生成し,解の存在を保証する手法と実際にパズルを自動生成して遊べるサンプルコードを紹介する.

問題の自動生成

パズルは長方形とし,その横の大きさを X,縦の大きさを Yとする.問題の自動生成とはすなわち \displaystyle \frac{XY}{2}枚の牌をこの長方形に敷き詰めることである.ただし, X Yのうち少なくとも一方は偶数でなければならない.

ここで,パズルに用いるスートの種類数と使用できる牌の枚数,被覆できるマスの最大数を表にまとめる.例えば, 5種類のスート (例えば,0, 1, 2, 4, 5の5種類) を用いたパズルを作成する場合,使用できる牌は 15枚であるから, XY \leq 30でなければならない.さもなければ,牌が足りなくなってしまう.

スートの種類の数 牌の枚数 マスの数
1 1 2
2 3 6
3 6 12
4 10 20
5 15 30
6 21 42
7 28 56

次に,未完成のパズルの盤面に座標を導入しよう.最も左上にあるマスを原点とし,その座標を (x, y) = (0, 0)とする.また,右方向に x座標,下方向に y座標をとる.

このパズルの盤面に1枚の牌を (2つのマスにまたがって) 横向きに置くとき,牌を置いた2つのマスのうち左側のマスを (x_l, y_l)とすれば,右側のマスの座標は (x_r, y_r) = (x_l + 1, y_l)と表せる.ここで,点 (x, y)の原点からのマンハッタン距離 d_1( (x, y) ) = x + yを考えると, d_1( (x_r, y_r) ) - d_1( (x_l, y_l) ) = 1がわかる. d_1( (x_l, y_l) ), d_1( (x_r, y_r) )はいずれも整数であるから, d_1( (x_l, y_l) ), d_1( (x_r, y_r) )のうちいずれか一方は奇数,もう一方は偶数である.牌を縦向きに置く場合でも,同様にして牌を置いた2つのマスの原点からのマンハッタン距離のうちいずれか一方は奇数,もう一方は偶数であること,それらの差の絶対値が 1であることがわかる.逆に,2つの相異なるマスが縦横に隣接していない場合は1枚の牌で同時に被覆することはできない.

以上のことから,ある2つのマス A, Bが1枚の牌で同時に被覆されうることは |d_1(A) - d_1(B)| = 1であることと同値である.これに基づいて,反射的関係 R = \left\{(A, B) \mid A, B \in V, |d_1(A) - d_1(B)| = 1\right\}を定義する.

盤面上のすべてのマスの集合を V,原点からのマンハッタン距離が奇数であるマスの集合を V_o,原点からのマンハッタン距離が偶数であるマスの集合を V_eとする.明らかに, V_o \cap V_e = \emptyset, V_o \cup V_e = Vである.また,1枚の牌で同時に被覆されうるマスの組の集合 E = \left\{ \{A, B\} \mid A\;R\;B \right\}を定義すると,すべての \{A, B\} \in Eについて A, Bのうちいずれか一方が奇数,もう一方が偶数であるから, E \subseteq \left\{ \{A', B'\} \mid (A', B') \in V_o \times V_e\right\}.したがって,グラフ G = (V, E) V_o, V_eを部集合とする二部グラフである.さらに,盤面のある位置に1枚の牌を (2つのマスにまたがって) 置くことを,その2つのマスに対応する Gの辺を選択することに対応付ければ,盤面に牌を隙間なく敷き詰めることは Gの完全マッチングを発見することと対応付けられる. Gは二部グラフであるから,完全マッチングは増加道法により多項式時間で発見できる.牌を不祝儀敷きすることが完全マッチングの1つと対応しているので,完全マッチングは常に存在する.
mathtrain.jp

牌を置く位置が決まったら,よくシャッフルした牌をそれらの位置に置けばパズルの解が完成する.牌の目だけを表示し,牌の敷き方の情報を表示しないように設定すれば,解の存在が保証されたパズルの問題が完成する.

サンプルコード

github.com

上のサンプルコードはここまでの議論に基づくパズルの自動生成プログラム,それを用いて生成したサンプル問題が含まれているリポジトリである.自動生成プログラムの用法はリポジトリのReadmeに書く.生成した問題はHTML形式で出力され,チェックボックスで解答の表示,非表示が切り替えられるようになっている.パズルを解く際は物理的なドミノ牌を使用することを推奨する.

f:id:nkhrlab:20190608233726p:plain
生成した問題 (解答表示時)*3

*1:最も流通しているダブル6のセットの場合.より多人数で遊ぶなどの目的でスートを増やしたものも販売されているが,本記事では取り扱わない.

*2:私もこの前まで知らなかった.

*3:実は最初に示した例題はこれである.