sudoku.c --- solve number place puzzle Masahide Kashiwagi kashi@mn.waseda.ac.jp [はじめに] 「数独」とか、「ナンバープレイス」とか言われている パズルを解くためのプログラムです。 データファイルに問題を記述し、 % sudoku xxx.dat とすると、全ての解を表示します。 オプション -v を付けると、途中経過を表示します。 [ルール] 数独は、例えば、 ---+---+---++---+---+---++---+---+--- | 7 | || | | || | | 5 ---+---+---++---+---+---++---+---+--- | | 6 || | 2 | || | | ---+---+---++---+---+---++---+---+--- | 9 | || 1 | | || 3 | | ===+===+===++===+===+===++===+===+=== | | || | | 4 || | | 2 ---+---+---++---+---+---++---+---+--- | 8 | || | | || | 1 | ---+---+---++---+---+---++---+---+--- 5 | | || 3 | | || | | ===+===+===++===+===+===++===+===+=== | | 4 || | | 7 || | 6 | ---+---+---++---+---+---++---+---+--- | | || | 8 | || 1 | | ---+---+---++---+---+---++---+---+--- 2 | | || | | || | 9 | ---+---+---++---+---+---++---+---+--- のような9×9の桝目に、 (1) 同じ行 (2) 同じ列 (3) 二重線で仕切られた3×3の小ブロック に同じ数字が重複しない用に空白に1〜9の数字を当てはめるパズルです。 この問題の解は一つで、 ---+---+---++---+---+---++---+---+--- 1 | 7 | 2 || 8 | 9 | 3 || 6 | 4 | 5 ---+---+---++---+---+---++---+---+--- 3 | 4 | 6 || 7 | 2 | 5 || 9 | 8 | 1 ---+---+---++---+---+---++---+---+--- 8 | 9 | 5 || 1 | 4 | 6 || 3 | 2 | 7 ===+===+===++===+===+===++===+===+=== 7 | 6 | 1 || 9 | 5 | 4 || 8 | 3 | 2 ---+---+---++---+---+---++---+---+--- 4 | 8 | 3 || 6 | 7 | 2 || 5 | 1 | 9 ---+---+---++---+---+---++---+---+--- 5 | 2 | 9 || 3 | 1 | 8 || 4 | 7 | 6 ===+===+===++===+===+===++===+===+=== 9 | 1 | 4 || 5 | 3 | 7 || 2 | 6 | 8 ---+---+---++---+---+---++---+---+--- 6 | 3 | 7 || 2 | 8 | 9 || 1 | 5 | 4 ---+---+---++---+---+---++---+---+--- 2 | 5 | 8 || 4 | 6 | 1 || 7 | 9 | 3 ---+---+---++---+---+---++---+---+--- のようになります。 これが最も一般的なルールですが、これに対角線上での 重複も許さないとか、12×12、16×16の桝目で行うものなど、 いくつかの変種があります。 [データファイルの書き方] 本プログラムは、データファイルの書き方によって様々なルールに 対応できます。以下にその書き方を示します。 例として、3×3で同一行、同一列の重複を許さないというルールで、 ---+---+--- 1 | | ---+---+--- | | 2 ---+---+--- | | ---+---+--- の空白を1〜3で埋めるという問題を考えます。 その場合のデータファイルは、 # vertical size 3 # horizontal size 3 # maximum number 3 # number of blocks under constraint 6 # board data 1.. ..2 ... # blocks under constraint +++ ... ... # ... +++ ... # ... ... +++ # +.. +.. +.. # .+. .+. .+. # ..+ ..+ ..+ のようになります。以下に説明します。 なお、先頭が'#'の行はコメントで、読み込み時には無視されます。 (1) まず、盤面の縦の大きさを > # vertical size > 3 のように書きます。 (2) 次に、盤面の横の大きさを > # horizontal size > 3 のように書きます。 (3) 使われる数値の最大値を書きます。 > # maximum number > 3 これで、1〜3の数値を用いることになります。 (4) 同一の数字の重複を許さない桝目の集合の数を書きます。 > # number of blocks under constraint > 6 この場合、 ・1行目の3つの桝に同一の数字は重複できない。 ・2行目の3つの桝に同一の数字は重複できない。 ・3行目の3つの桝に同一の数字は重複できない。 ・1列目の3つの桝に同一の数字は重複できない。 ・2列目の3つの桝に同一の数字は重複できない。 ・3列目の3つの桝に同一の数字は重複できない。 という6つの制約があり、従って6と書きます。 普通のナンバープレイスの場合は、9(同一行) + 9(同一列) + 9(同一ブロック) で27になります。 (5) 盤面を > # board data > 1.. > ..2 > ... のように書きます。このように、空白の部分は'.'で表します。 また、10以上の数値を書く場合には、10='A', 11='B'...のように記述します。 (6) 最後に、同一数字が重複できないブロックを記述します。 > # blocks under constraint > +++ > ... > ... > # > ... > +++ > ... > # > ... > ... > +++ > # > +.. > +.. > +.. > # > .+. > .+. > .+. > # > ..+ > ..+ > ..+ このように、同一数の存在が許されない部分を'+'で表します。この盤面は、(4)で 記述した数だけ存在しなければなりません。この場合は、6個です。 datという拡張子で、いくつかのsampleを付けています。 データファイルを書くのは特に最後の重複を許さない部分の記述が 大変なので、sampleをコピーして盤面のデータだけ書き換えてお使い下さい。 ex1.dat, ex2.dat, ex3.datが普通のナンバープレイス、ex4.datは 12×12、ex5.datは16×16です。 なお、ex6.datは盤面が長方形でないという変種ですが、中身を 見て頂ければ分かるように、盤面を含む長方形を考え、(5)の盤面の 定義において数字が入らない部分に'x'を書けば、任意の形状の 盤面に対応できます。 [おわりに] 動作確認は、FreeBSD 2.2.8上で行いました。 特殊なことは何もしていないので、大抵のC compilerでOKでしょう。 アルゴリズムは単純で、最初に各桝目に全ての数字の可能性を仮定し、 ルールを繰り返し適用して可能性を消去していきます。 これだけで一意に決まらない場合は、back trackで全ての場合を 調べます。 プログラムの構想時にはもっと凝った可能性の消去法を考えていたのですが、 最低限動作するプログラムを書いたら十分速かったのでそれで良しと してしまいました。ちょっとback trackの力業に頼りすぎているかも 知れません。 自分の興味で作っただけですし、単なるプログラムの練習問題程度のものなので あまり役に立つとも思えませんが、せっかく作ったので公開します。 PDSとします。煮るなり焼くなりお好きなように。