数独(SUDOKU)パズルを J で解く -Labs システムによる Hui の

JAPL 研究会資料
2006/1/28
数独(SUDOKU)パズルを J で解く
-Labs システムによる Hui のプログラムのトレース-
Solving SUDOKU Puzzle in J
-Hui’s Program Tracing in Labs System-
西川
利男 Toshio.Nishikawa@kiu.ne.jp
数独(SUDOKU)パズルに筆者が親しむようになったのは次のようないきさ
つからである。昨年10月の初め、山下紀幸氏から“数独について知りません
か?”との FAX メールが入った。その後二、三日して読売新聞紙上(2005/10/15)
で数独パズルが紹介された。ところが、筆者の妻はもっと以前に毎日新聞でや
ったことがあると言っている。後に山下氏により毎日新聞での紹介記事
(2005/10/7)も送られてきた。
次なる大きなきっかけは、中野嘉弘氏から最近の Vector 誌に R. Hui により
数独パズルを解く J のプログラムが載っている[1]との知らせとともに、論文コ
ピーが送られてきた。 同時に山下氏からはニコリ社の「数独21」なる問題・
解答集も贈られた。
筆者が「Excel による数独の楽しみ」なる小文を読売新聞社に投稿し、昨年暮、
JAPLA シンポジウムで「Jによる数独パズル」を発表したのは[2]、このような
動機からである。
Hui による数独パズルの解を求める J のプログラムはコンパクトで強力であ
る。筆者にとってプログラムは分かり易いものではなかったが、おおよその流
れはつかめた。また、最新版の J5 でしか動かない原始関数などを一部手直し、
J4, J3, さらには DOS 版の JPC でも動くようにした。
今回の発表は Hui のプログラムを解説するというより、J の Labs システム上
でプログラム実行の流れをデモとして紹介する。
[1] Roger Hui, “A Sudoku Solver in J”, Vector, vol 21, No.4, p.49 (2005).
[2] 西川利男「J のオブジェクト指向プログラミング(OOP)」
JAPLA シンポジウム資料、2005/12/10 (2005).
その1-J の OOP とは
その2-J のスプレッドシート(Grid)と数独パズルへの適用
-1-
(以下は Labs システムの実行文書である)
------------------------------------------------------------Lab:
Explain Hui's SUDOKU Program
Author: Explained by T. Nishikawa
Originally programmed by R. Hui
Press <Ctrl+A> to advance.
-- (1 of 35) Introduction ----------------------------------さあ、これから数独パズルをやってみましょう!
問題は読売新聞に出された例です。
)
load 'user\nhui_sud.js'
see ym
+---+---+---+
|.5.|7.1|.4.|
|7.3|...|1.2|
|.8.|4.6|.9.|
+---+---+---+
|9.4|.6.|8.3|
|...|8.7|...|
|1.8|.5.|6.9|
+---+---+---+
|.1.|6.3|.8.|
|5.6|...|7.1|
|.3.|5.9|.2.|
+---+---+---+
-- (2 of 35) Introduction (continued) ----------------------数独とは、空いたセルを次のルールにしたがい、
つぎつぎに数字で埋めていくパズルです。
「ヨコに同じ数字があってはならない」
「タテに同じ数字があってはならない」
「ブロック内に同じ数字があってはならない」
簡単そうで、なかなかむずかしいでしょう。
J では、これを一瞬に解いてしまうことができます。
)
see ym, sudoku ym
+-------------+-------------+
|+---+---+---+|+---+---+---+|
||.5.|7.1|.4.|||659|721|348||
||7.3|...|1.2|||743|985|162||
||.8.|4.6|.9.|||281|436|597||
|+---+---+---+|+---+---+---+|
||9.4|.6.|8.3|||974|162|853||
||...|8.7|...|||365|897|214||
||1.8|.5.|6.9|||128|354|679||
|+---+---+---+|+---+---+---+|
||.1.|6.3|.8.|||412|673|985||
||5.6|...|7.1|||596|248|731||
||.3.|5.9|.2.|||837|519|426||
|+---+---+---+|+---+---+---+|
+-------------+-------------+
-- (3 of 35) Pre-definition --------------------------------数独では、行ごと、列ごと、ブロックごとの
取り出し、検索などの処理が必要です。
そのため準備として、いくつかの定義を行います。
)
-2-
j
r
c
b
=:
=:
=:
=:
(]/. i.@#) ,{;~3#i.3
9#i.9 9
81$|:i.9 9
(,j{9#i.9) { j
I
R
=: ~."1 r,.c,.b
=: j, (,|:)i.9 9
regions =: R"_ {"_ 1 ]
free
=: 0&= > (1+i.9)"_ e."1 I&{
-- (4 of 35) Pre-definition (continued) --------------------r は行ごとのインデックスを表します。
r と入力してください。
)
-- (5 of 35) Pre-definition (continued) --------------------c は列ごとのインデックスを表します。
c と入力してください。
)
-- (6 of 35) Pre-definition (continued) --------------------j を使って、b は
ブロックごとのインデックスを表します。
j と入力してください。
b と入力してください。
)
-- (7 of 35) Pre-definition (continued) --------------------I は行、列、ブロックをまとめて、
ユニークな(重複したものを除いた)セルの
インデックスを表します。
I と入力してください。
)
-- (8 of 35) Candidates ------------------------------------以上の準備をした上で、
free ym
とすると、
初期データ ym の空いた場所に、候補として入れられる数字を
ブール表示したパラメータを出力します。
free ym と入力してください。
)
-- (9 of 35) Candidates (continued) ------------------------これらのパラメータの意味を見てみましょう。
なお、ここでは行、列は1オリジンで示します。
第1行目=1行1列のセルのパラメータ
1 2 3 4 5 6 7 8 9
--------------------値 0 1 0 0 0 1 0 0 0
このセルには数字 2 と 6 とが候補として入れられる。
第2行目=1行2列のセルのパラメータ
1 2 3 4 5 6 7 8 9
--------------------値 0 0 0 0 0 0 0 0 0
このセルには入れられない。
-3-
第3行目=1行3列のセルのパラメータ
1 2 3 4 5 6 7 8 9
--------------------値 0 1 0 0 0 0 0 0 1
このセルには数字 2 と 9 とが候補として入れられる。
このようにして、
値 0 0 0 0 0 0 0 0 0
値 0 1 1 0 0 0 0 1 1
値 0 0 0 0 0 0 0 0 0
第7行目=1行7列のセルのパラメータ
1 2 3 4 5 6 7 8 9
--------------------値 0 0 1 0 0 0 0 0 0
このセルには数字 3 だけが入れられ、確定する。
)
-- (10 of 35) Candidates (continued) -----------------------これをわかり易く示すプログラムを作ってみましょう。
次のプログラムを作ったあとで
)
N
=: 1 + i.9
NN =: >, {N;N
NNN =: '(',"1 (": NN),"1 ,') = '
FRE =: (free ym)#N
NFR =: NNN ,"1 ":FRE
-- (11 of 35) Candidates (continued) -----------------------セル(行 列)に対して入れられる数字の候補を見るには
NFR と入力してください。
)
-- (12 of 35) Candidates (continued) -----------------------これらの候補の頻度を一覧表として見るには
以下のように行います。
数字はその場所に入れられる候補の数を示します。
)
see FREQ =: +/"1 free ym
+---+---+---+
|2.2|.4.|1.2|
|.3.|122|.2.|
|1.2|.2.|2.2|
+---+---+---+
|.2.|2.1|.3.|
|322|.5.|322|
|.2.|2.2|.1.|
+---+---+---+
|2.3|.3.|3.2|
|.3.|133|.1.|
|2.1|.4.|1.2|
+---+---+---+
-- (13 of 35) Candidates (continued) -----------------------まず、1は候補が一つに確定することを示します。
たとえば、3行1列のセルには1つだけの数字が入れられ、その値は
関数 cand を使って、つぎのように示されます。
他のセルでもやってみましょう。
)
-4-
2
cand 3 1
-- (14 of 35) Search Values --------------------------------これをまとめて行うには
関数 ac を使って、つぎのように取り出されます。
)
AC =: ac@free ym
see AC
+---+---+---+
|...|...|3..|
|...|9..|...|
|2..|...|...|
+---+---+---+
|...|..2|...|
|...|...|...|
|...|...|.7.|
+---+---+---+
|...|...|...|
|...|2..|.3.|
|..7|...|4..|
+---+---+---+
-- (15 of 35) Search Values (continued) --------------------次に頻度表で1以外は、
値が一つに確定しない場所です。
)
see FREQ
+---+---+---+
|2.2|.4.|1.2|
|.3.|122|.2.|
|1.2|.2.|2.2|
+---+---+---+
|.2.|2.1|.3.|
|322|.5.|322|
|.2.|2.2|.1.|
+---+---+---+
|2.3|.3.|3.2|
|.3.|133|.1.|
|2.1|.4.|1.2|
+---+---+---+
-- (16 of 35) Search Values (continued) --------------------たとえば、
2行2列のセルでは
3つの値が候補になります。
その値は次のようになります。
)
cand 2 2
4 6 9
-- (17 of 35) Search Values (continued) --------------------しかし、この場合でも
同じ行、同じ列、同じブロック内に
たった1回だけ、現れる値であれば
この場合は確定することになります。
関数 cand1 を使って、
2行2列であれば
つぎのようにして調べられます。
)
cand1 2 2
Row:
4 5 5 6 6 8 8 9 9 9
Column: 2 2 2 2 4 4 6 6 7 7 9 9
Block: 1 2 2 2 2 4 6 6 9 9
-5-
-- (18 of 35) Search Values (continued) --------------------数字4はただ1回だけ現れます。
つまり、この場所では
4が候補として、確定します。
)
-- (19 of 35) Search Values (continued) --------------------ところが、1行1列の場所で
同様のテストをすると
)
cand 1 1
2 6
cand1 1 1
Row:
2 2 2 3 3 6 6 8 8 9 9
Column: 2 2 2 2 3 4 4 6 6 8
Block: 1 2 2 2 2 4 6 6 9 9
-- (20 of 35) Search Values (continued) --------------------たった1回だけ、現れるという条件にあてはまる
値は見つかりません。
つまり、候補は確定しません。
他の場所でもやってみてください。
)
-- (21 of 35) Search Values (continued) --------------------このように2つ以上の候補があるものでも、
行、列、ブロック内でのユニーク性を利用して
1つに確定することができます。
これを、まとめて行うには
関数 ar を使って、つぎのように取り出されます。
)
see AR =: ar@free ym
+---+---+---+
|...|...|...|
|.4.|..5|...|
|..1|...|..7|
+---+---+---+
|...|1..|.5.|
|3..|.9.|2..|
|...|3..|...|
+---+---+---+
|...|...|...|
|...|...|.3.|
|8..|.1.|...|
+---+---+---+
-- (22 of 35) Search Values (continued) --------------------つまり、これら両方の数字の値を、
空いた場所に入れることが出来るのです。
)
see ACR =: AC >. AR
+---+---+---+
|...|...|3..|
|.4.|9.5|...|
|2.1|...|..7|
+---+---+---+
|...|1.2|.5.|
|3..|.9.|2..|
|...|3..|.7.|
+---+---+---+
|...|...|...|
|...|2..|.3.|
|8.7|.1.|4..|
+---+---+---+
-6-
-- (23 of 35) Search Values (continued) --------------------したがって、初期値と合わせれば
第一ステップとして、次の解が得られます。
)
see ASS_1 =: ym + ACR
+---+---+---+
|.5.|7.1|34.|
|743|9.5|1.2|
|281|4.6|.97|
+---+---+---+
|9.4|162|853|
|3..|897|2..|
|1.8|35.|679|
+---+---+---+
|.1.|6.3|.8.|
|5.6|2..|731|
|837|519|42.|
+---+---+---+
-- (24 of 35) Repeat ---------------------------------------まだ、埋まっていないセルがありますね。
これを埋めるには、今の解を初期値として
先の処理を繰り返せば、良いのです。
下記の関数 func を定義して行います。
)
func =: + (ac >. ar)@free
see func ASS_1
+---+---+---+
|659|721|34.|
|743|985|162|
|281|436|597|
+---+---+---+
|974|162|853|
|365|897|214|
|128|354|679|
+---+---+---+
|412|673|985|
|596|2.8|731|
|837|519|426|
+---+---+---+
-- (25 of 35) Repeat (continued) ---------------------------つまり、関数 func を次々に繰り返し実行して
空いたセルを埋めて行けばよいのです。
たとえば、4回繰り返すには
下記のようにします。
)
see ASS_4 =: func^:(i.4) ym
+-------------+-------------+-------------+-------------+
|+---+---+---+|+---+---+---+|+---+---+---+|+---+---+---+|
||.5.|7.1|.4.|||.5.|7.1|34.|||659|721|34.|||659|721|348||
||7.3|...|1.2|||743|9.5|1.2|||743|985|162|||743|985|162||
||.8.|4.6|.9.|||281|4.6|.97|||281|436|597|||281|436|597||
|+---+---+---+|+---+---+---+|+---+---+---+|+---+---+---+|
||9.4|.6.|8.3|||9.4|162|853|||974|162|853|||974|162|853||
||...|8.7|...|||3..|897|2..|||365|897|214|||365|897|214||
||1.8|.5.|6.9|||1.8|35.|679|||128|354|679|||128|354|679||
|+---+---+---+|+---+---+---+|+---+---+---+|+---+---+---+|
||.1.|6.3|.8.|||.1.|6.3|.8.|||412|673|985|||412|673|985||
||5.6|...|7.1|||5.6|2..|731|||596|2.8|731|||596|248|731||
||.3.|5.9|.2.|||837|519|42.|||837|519|426|||837|519|426||
|+---+---+---+|+---+---+---+|+---+---+---+|+---+---+---+|
+-------------+-------------+-------------+-------------+
-7-
-- (26 of 35) Repeat (continued) ---------------------------また、各ステップで入れられた数字を示すのには
関数 diff を用いて次のようにします。
)
see diff ASS_4
+-------------+-------------+-------------+-------------+
|+---+---+---+|+---+---+---+|+---+---+---+|+---+---+---+|
||.5.|7.1|.4.|||...|...|3..|||6.9|.2.|...|||...|...|..8||
||7.3|...|1.2|||.4.|9.5|...|||...|.8.|.6.|||...|...|...||
||.8.|4.6|.9.|||2.1|...|..7|||...|.3.|5..|||...|...|...||
|+---+---+---+|+---+---+---+|+---+---+---+|+---+---+---+|
||9.4|.6.|8.3|||...|1.2|.5.|||.7.|...|...|||...|...|...||
||...|8.7|...|||3..|.9.|2..|||.65|...|.14|||...|...|...||
||1.8|.5.|6.9|||...|3..|.7.|||.2.|..4|...|||...|...|...||
|+---+---+---+|+---+---+---+|+---+---+---+|+---+---+---+|
||.1.|6.3|.8.|||...|...|...|||4.2|.7.|9.5|||...|...|...||
||5.6|...|7.1|||...|2..|.3.|||.9.|..8|...|||...|.4.|...||
||.3.|5.9|.2.|||8.7|.1.|4..|||...|...|..6|||...|...|...||
|+---+---+---+|+---+---+---+|+---+---+---+|+---+---+---+|
+-------------+-------------+-------------+-------------+
-- (27 of 35) Repeat (continued) ---------------------------したがって、最終解を得るには、
値が確定するまで処理を繰り返す J の便利な機能を用いて
以下のように行えば良いのです。
)
see ASS_N =: func ^:_ ym
+---+---+---+
|659|721|348|
|743|985|162|
|281|436|597|
+---+---+---+
|974|162|853|
|365|897|214|
|128|354|679|
+---+---+---+
|412|673|985|
|596|248|731|
|837|519|426|
+---+---+---+
-- (28 of 35) Guess ----------------------------------------以上の関数は、Hui のプログラムでは
assign
として定義されてます。
このように易しい問題は、これで解が求まりますが、
難しい問題はこれだけでは、求まりません。
「数独21」から、
No.99, Hard
の問題を見てみましょう。
)
see su21_99
+---+---+---+
|..4|81.|...|
|..2|...|3..|
|1..|.94|6..|
+---+---+---+
|74.|...|...|
|.9.|.2.|.5.|
|...|...|.83|
+---+---+---+
|..7|13.|..2|
|..6|...|1..|
|...|.45|8..|
-8-
+---+---+---+
-- (29 of 35) Guess (continued) ----------------------------これをためしに解いてみましょう。
このように解は全く求まりません。
)
see sux =: assign su21_99
+---+---+---+
|..4|81.|...|
|..2|...|3..|
|1..|.94|6..|
+---+---+---+
|74.|...|...|
|.9.|.2.|.5.|
|...|...|.83|
+---+---+---+
|..7|13.|..2|
|..6|...|1..|
|.19|.45|8..|
+---+---+---+
-- (30 of 35) Guess (continued) ----------------------------この理由を頻度表を作って、調べてみましょう。
)
see bx =: +/"1 free sux
+---+---+---+
|44.|..4|433|
|44.|332|.46|
|.43|4..|.23|
+---+---+---+
|..4|435|243|
|3.3|4.5|2.4|
|332|534|4..|
+---+---+---+
|32.|..3|33.|
|54.|324|.44|
|2..|3..|.32|
+---+---+---+
-- (31 of 35) Guess (continued) ----------------------------このように候補値の頻度が高い場所が多く、
これではユニークな候補はなかなか見つけられません。
ためしに、いくつかの場所の候補値を見てみましょう。
)
sux cand 1 1
3 5 6 9
sux cand 1 2
3 5 6 7
sux cand 1 6
2 3 6 7
sux cand 1 7
2 5 7 9
sux cand 1 8
2 7 9
sux cand 1 9
5 7 9
-- (32 of 35) Guess (continued) ----------------------------頻度値が最小のものが現れるインデックス ind と
そのときの候補の値を
つぎのようにして求めます。
)
ind =: 1 + 9 9#: (i. <./) (bx) {10, 1, 2, 3, 4, 5, 6, 7, 8, 9
ind
2 6
-9-
6 7
sux cand ind
-- (33 of 35) Guess (continued) ----------------------------すると
2行6列のセルには
6 と 7
が候補として可能性があることが分かります。
)
-- (34 of 35) Guess (continued) ----------------------------Hui のプログラムではこのような考えで推論を行う
guess
という関数が用意されていて、
可能性のある2つの場合が求められます。
)
see guess sux
+-------------+-------------+
|+---+---+---+|+---+---+---+|
||..4|81.|...|||..4|81.|...||
||..2|..6|3..|||..2|..7|3..||
||1..|.94|6..|||1..|.94|6..||
|+---+---+---+|+---+---+---+|
||74.|...|...|||74.|...|...||
||.9.|.2.|.5.|||.9.|.2.|.5.||
||...|...|.83|||...|...|.83||
|+---+---+---+|+---+---+---+|
||..7|13.|..2|||..7|13.|..2||
||..6|...|1..|||..6|...|1..||
||.19|.45|8..|||.19|.45|8..||
|+---+---+---+|+---+---+---+|
+-------------+-------------+
-- (35 of 35) Guess (continued) ----------------------------最終的にはこのような推論を含めた関数
sudoku
により以下のように解が求められます。
)
see su21_99, sudoku su21_99
+-------------+-------------+
|+---+---+---+|+---+---+---+|
||..4|81.|...|||364|812|579||
||..2|...|3..|||982|756|314||
||1..|.94|6..|||175|394|628||
|+---+---+---+|+---+---+---+|
||74.|...|...|||743|581|296||
||.9.|.2.|.5.|||698|423|751||
||...|...|.83|||521|967|483||
|+---+---+---+|+---+---+---+|
||..7|13.|..2|||457|138|962||
||..6|...|1..|||836|279|145||
||...|.45|8..|||219|645|837||
|+---+---+---+|+---+---+---+|
+-------------+-------------+
End of lab
- 10 -
プログラム・リスト
NB. Hui's Sudoku Solving Program
NB. Modified executable in J3, J4, JPC by T. Nishikawa, 2006/1/2
j
r
c
b
=:
=:
=:
=:
(]/. i.@#) ,{;~3#i.3
9#i.9 9
81$|:i.9 9
(,j{9#i.9) { j
I
R
=: ~."1 r,.c,.b
=: j, (,|:)i.9 9
regions =: R"_ {"_ 1 ]
free
=: 0&= > (1+i.9)"_ e."1 I&{
ok
=: (27 9$1)"_ -:"2 (0&= +. ~:"1)@regions
ac
=: +/ .*&(1+i.9) * 1: = +/"1
Ip
=: # i.@#
NB. I.(indices) is defined as Ip
ar
=: 3 : 0
m=: 1=+/"2 R{y.
jj =: Ip +. /"1 m
k =: 1 i."1~ jj{m
i =: ,(k{"_1 |:"2 (jj{R){y.) #"1 jj{R
(1+k) i}81$0
)
assign =: (+ (ac >. ar)@free)^:_"1
guessa =: 3 : 0
if. -. 0 e. y. do. ,:y. return. end.
b =. free y.
i =. (i.<./) (+/"1 b){10,}.i.10
y. +"1 (1+ Ip i{b)*/i=i.81
)
guess =: ; @: (<@guessa"1)
sudoku =: guess @: (ok #]) @: assign ^:_ @ ,
see0
=:
see1
=:
NB. see1
see
=:
diff
=:
x0
2
0
4
5
0
0
0
7
0
)
0
0
0
0
3
0
0
0
0
0
6
0
0
0
2
1
8
0
({&'.123456789') @ (9 9&$) @ ,
NB. modified by TN
(3 3 ,: 3 3)&(<;.3) @ see0
NB. modified by TN
=: (;~9$1 0 0)&(<;.1) @ ({&'.123456789') @ (9 9&$) @ ,
<@see1"1`see1@.(1:=#@$)
* 0&=@}:@(0&,)
=: ] ;._2 (0 : 0)
6 7 0 0 0 0
0 0 0 2 0 1
0 0 0 8 0 0
0 0 9 3 0 0
0 0 0 0 5 0
8 0 0 0 0 7
0 0 0 0 0 4
0 0 0 6 0 0
0 5 3 0 0 8
x
=: ,
0". x0
f
=: + (ac >. ar)@free
g
=: guess @:(ok#])@:assign
- 11 -
NB. Yomiuri Shimbun 2005/10/15
ym =: , 0".] ;._2 (0 : 0)
0 5 0 7 0 1 0 4 0
7 0 3 0 0 0 1 0 2
0 8 0 4 0 6 0 9 0
9 0 4 0 6 0 8 0 3
0 0 0 8 0 7 0 0 0
1 0 8 0 5 0 6 0 9
0 1 0 6 0 3 0 8 0
5 0 6 0 0 0 7 0 1
0 3 0 5 0 9 0 2 0
)
NB. Sudoku21, p.10 No.1 Easy
su21_1 =: , 0". ] ;._2 (0 : 0)
0 0 0 0 0 2 5 0 3
0 3 1 0 0 0 0 0 8
0 2 4 0 5 1 0 0 0
0 0 0 0 1 7 0 6 5
2 6 0 0 0 0 0 4 7
7 1 0 5 6 0 0 0 0
0 0 0 6 8 0 2 9 0
4 0 0 0 0 0 8 3 0
6 0 3 4 0 0 0 0 0
)
NB. Sudoku21, p.110 No.99 Hard
su21_99 =: , 0". ] ;._2 (0 : 0)
0 0 4 8 1 0 0 0 0
0 0 2 0 0 0 3 0 0
1 0 0 0 9 4 6 0 0
7 4 0 0 0 0 0 0 0
0 9 0 0 2 0 0 5 0
0 0 0 0 0 0 0 8 3
0 0 7 1 3 0 0 0 2
0 0 6 0 0 0 1 0 0
0 0 0 0 4 5 8 0 0
)
NB. Sudoku
clark =: ,
0 3 4 0 0
0 7 5 0 2
0 0 0 0 0
3 4 0 0 0
0 0 0 2 6
0 6 0 3 4
0 0 3 5 8
0 0 0 0 0
0 0 0 0 0
)
with Dyalog APL, J. Clark's Example
0". ] ;._2 (0 : 0)
5 0 0 0
0 1 0 0
0 8 7 5
0 0 0 9
0 3 4 0
0 0 0 0
0 0 9 0
0 0 0 3
0 0 0 0
- 12 -
NB. Nishikawa's Utility Functions
cand =: 3 : 0
ym cand y.
:
ind =: +/9 1 * _1 + y.
v =. ind { (free x.)#>:i.9
(v>0) # v
)
wr =: 1!:2&2
cand1 =: 3 : 0
ym cand1 y.
:
M =: +/"2 R{free x.
B =. ((3#.<. 3%~ "(0)_1+y.){M) # >:i.9
R =. ((9 + {. _1+y.){M) # >:i.9
C =. ((18 + {: _1+y.){M) # >:i.9
wr 'Row:
', ": (R>0) # R
wr 'Column: ', ": (C>0) # C
'Block: ',": (B>0) # B
)
NB Sudoku Data Display Conversion
subs=: [. & ((((e.&) ((# i.@#)@)) (@])) })
NB. convert data from Nishikawa's format to Hui's format
n2h =: 3 : '9 9$0 subs _ (, y.)'
NB. convert data from Hui's format to Nishikawa's format
h2n =: 3 : '9 9$_ subs 0 (, y.)'
seeN =: 3 : '(3 3 ,: 3 3)&(<;.3) h2n y.'
- 13 -