89.8. 数独例のデシジョン (複雑なパターン一致、コールバック、および GUI 統合)
数独例のデシジョンセットは、人気の数字パズルゲーム数独をもとにしています。このセットでは、Red Hat Decision Manager のルールを使用してさまざまな制約をもとに、多数の考えられる回答スペースの中で回答を導き出す方法を例示します。またこの例では、Red Hat Decision Manager ルールとグラフィカルユーザーインターフェイス (GUI) の統合方法が分かります。今回は Swing ベースのデスクトップアプリケーションを使用します。また、この例では、コールバックを使用して実行中のデシジョンエンジンと通信し、ランタイム時に加えられたワーキングメモリー内の変更をもとに GUI を更新する方法を例示しています。
以下は数独の例の概要です。
-
名前:
sudoku
-
Main クラス: (
src/main/java
内の)org.drools.examples.sudoku.SudokuExample
-
モジュール:
drools-examples
- タイプ: Java アプリケーション
-
ルールファイル: (
src/main/resources
内の)org.drools.examples.sudoku.*.drl
- 目的: 複雑なパターン一致、問題解決、コールバック、および GUI 統合を例示します。
数独は、ロジックベースの数字配置パズルです。目的は、各列、各行、および各 3x3 ゾーンに 1 から 9 の数字が一度だけ含まれるように 9x9 のグリッドを埋めることです。パズルセッターでは、グリッド内の一部だけ記入されており、上記の制約ですべての空白を埋めるのがパズルの回答者のタスクです。
問題解決の一般的なストラテジーとして、新しい番号の挿入時に、特定の 3x3 ゾーン、行、および列で同じ番号がないことを確認します。この数独例のデシジョンセットでは、Red Hat Decision Manager ルールを使用して、さまざまな難易度の数独パズルを解き、無効なエントリーが含まれ、不備のあるパズルの解決を試みます。
数独例の実行および対話
他の Red Hat Decision Manager のデシジョン例と同じように、お使いの IDE で org.drools.examples.sudoku.SudokuExample
クラスを Java アプリケーションとして実行し、数独の例を実行します。
数独の例を実行すると、GUI ウィンドウ Drools Sudoku Example
が表示されます。このウィンドウには空のグリッドが含まれていますが、プログラムには内部に保存されたさまざまなグリッドが含まれ、読み込んで解決できます。
File
図89.19 起動後の数独例の GUI
Simple サンプルを読み込むと、パズルの最初の状態に合わせて、グリッドが埋められます。
図89.20 Simple サンプルを読み込んだ後の数独例の GUI
以下のオプションから選択します。
Solve をクリックして、数独の例に定義されているルールを実行し、残りの値を埋めていき、このボタンを再度無効にします。
図89.21 Simple サンプルの解決
Step をクリックして、ルールセットに含まれる次の数字を表示します。IDE のコンソールウィンドウでは、解決手順を実行するルールに関する情報が詳細に表示されます。
IDE コンソールでの手順実行の出力
single 8 at [0,1] column elimination due to [1,2]: remove 9 from [4,2] hidden single 9 at [1,2] row elimination due to [2,8]: remove 7 from [2,4] remove 6 from [3,8] due to naked pair at [3,2] and [3,7] hidden pair in row at [4,6] and [4,4]
Dump をクリックしてグリッドの状態を表示します。セルには、解決済みの値か、残りの候補値が表示されます。
IDE コンソールでのダンプ実行の出力
Col: 0 Col: 1 Col: 2 Col: 3 Col: 4 Col: 5 Col: 6 Col: 7 Col: 8 Row 0: 123456789 --- 5 --- --- 6 --- --- 8 --- 123456789 --- 1 --- --- 9 --- --- 4 --- 123456789 Row 1: --- 9 --- 123456789 123456789 --- 6 --- 123456789 --- 5 --- 123456789 123456789 --- 3 --- Row 2: --- 7 --- 123456789 123456789 --- 4 --- --- 9 --- --- 3 --- 123456789 123456789 --- 8 --- Row 3: --- 8 --- --- 9 --- --- 7 --- 123456789 --- 4 --- 123456789 --- 6 --- --- 3 --- --- 5 --- Row 4: 123456789 123456789 --- 3 --- --- 9 --- 123456789 --- 6 --- --- 8 --- 123456789 123456789 Row 5: --- 4 --- --- 6 --- --- 5 --- 123456789 --- 8 --- 123456789 --- 2 --- --- 9 --- --- 1 --- Row 6: --- 5 --- 123456789 123456789 --- 2 --- --- 6 --- --- 9 --- 123456789 123456789 --- 7 --- Row 7: --- 6 --- 123456789 123456789 --- 5 --- 123456789 --- 4 --- 123456789 123456789 --- 9 --- Row 8: 123456789 --- 4 --- --- 9 --- --- 7 --- 123456789 --- 8 --- --- 3 --- --- 5 --- 123456789
数独の例には、不備のあるサンプルファイルが意図的に含められています。このファイルは、例で定義したルールを使用して解決できます。
File 5
の値を 2 回表示できないにもかかわらず表示されるなど、問題が含まれた状態で表示されます。
図89.22 不備のある数独例の最初の状態
Solve をクリックしてこの無効なグリッドに解決ルールを適用します。数独の例に含まれる関連の解決ルールにより、サンプルの問題が検出され、できる限りパズルを解決します。このプロセスでは、すべてを完了させず、空白のセルをいくつか残します。
解決ルールのアクティビティーが IDE コンソールウィンドウに表示されます。
不備のあるサンプルでの問題検出
cell [0,8]: 5 has a duplicate in row 0 cell [0,0]: 5 has a duplicate in row 0 cell [6,0]: 8 has a duplicate in col 0 cell [4,0]: 8 has a duplicate in col 0 Validation complete.
図89.23 不備のあるサンプルの解決試行
Hard のラベルの付いた数独サンプルファイルはより複雑で、解決ルールを使用しても解決できない可能性があります。解決をしようとして失敗した場合は、IDE コンソールウィンドウに表示されます。
解決不可の Hard サンプル
Validation complete. ... Sorry - can't solve this grid.
不備のあるサンプルを解決するためのルールでは、セルの候補となりえる値をもとにした標準の解決手法を実装します。たとえば、セットに値が 1 つ含まれる場合は、これが値になります。セルが 9 個あるグループの 1 つに値が 1 度挿入された場合に、ルールを使用して、特定のセルに対する値を持ち、タイプが Setting
のファクトを挿入します。このファクトにより、そのセルが含まれるグループにある他のすべてのセルからこの値が削除され、この値が取り消されます。
この例の他のルールで、セルに入力可能な値を減らしていきます。"naked pair"
、"hidden pair in row"
、"hidden pair in column"
、および "hidden pair in square"
のルールでは、候補の絞り込みはできますが、回答を得ることはできません。"X-wings in rows"
、"`X-wings in columns"`、"intersection removal row"
、および "intersection removal column"
のルールは、より高度な絞り込みを実行します。
数独例のクラス
org.drools.examples.sudoku.swing
パッケージには、以下のように、数独パズルのフレームワークを実装する主なクラスセットが含まれます。
-
SudokuGridModel
は、9x9 グリッドのCell
オブジェクトとして数独パズルを格納するために実装可能なインターフェイスを定義しています。 -
SudokuGridView
クラスは Swing コンポーネントで、SudokuGridModel
クラス実装の視覚化が可能です。 -
SudokuGridEvent
クラスおよびSudokuGridListener
クラスは、モデルとビューの間のステータスの変化をやり取りするために使用します。セルの値が解決または変更すると、イベントが実行します。 -
SudokuGridSamples
クラスは、デモ目的に一部入力されている数独パズルを複数提供します。
このパッケージには、Red Hat Decision Manager ライブラリーの依存関係は含まれません。
org.drools.examples.sudoku
パッケージには、以下のように、基本的な Cell
オブジェクトと各種アグリゲーションを実装する主なクラスセットが含まれます。
-
CellRow
、CellCol
、およびCellSqr
のサブタイプを含むCellFile
クラス。これはすべて、CellGroup
クラスのサブタイプになります。 Cell
とCellGroup
はSetOfNine
のサブクラスで、Set<Integer>
型のfree
プロパティーを提供します。Cell
クラスは、個別の候補セットを表します。CellGroup
は、セルの全候補セットの統合 (割り当ての必要がある数値セット) です。数独の例には、81 個の
Cell
と 27 個のCellGroup
オブジェクト、Cell
プロパティーのcellRow
、cellCol
、およびcellSqr
が提供するリンク、CellGroup
プロパティーcells
(Cell
オブジェクトリスト) が提供するリストが含まれます。これらのコンポーネントを使用して、セルに値を割り当てたり、候補セットから値を取り除いたりできるように、特定の状態を検出するルールを記述できます。-
Setting
クラスを使用して、値の割り当てに伴うオペレーションをトリガーします。Setting
ファクトは、整合性の取れない中間の状態に対して反応しないように、新しい状況を検出する全ルールに配置して使用します。 -
Stepping
クラスは、優先順位が低いルールに使用して、"Step"
が予期なく中断された場合に緊急停止を行います。この動作は、プログラムでパズルを解決できないということです。 -
Main クラス
org.drools.examples.sudoku.SudokuExample
は、全コンポーネントを統合する Java アプリケーションを実装します。
数独の検証ルール (validate.drl)
数独の例の validate.drl
ファイルには、セルグループで数が重複している状況を検出する検証ルールが含まれます。このグループは、"validate"
アジェンダグループに統合され、ユーザーがパズルを読み込むと、明示的にルールをアクティベートできます。
"duplicate in cell …"
の 3 つのルールの when
条件はすべて以下の方法で機能します。
- このルールの最初の条件で、割り当てられた値でセルを特定します。
- このルールの 2 番目の条件では、3 つのセルグループのどれかを所属先にプルします。
- 最終条件は、ルールに従い、最初のセル、同じ行、列、または四角に入る値と同じセル (上記のセル以外) を検索します。
ルール "duplicate in cell …"
rule "duplicate in cell row" when $c: Cell( $v: value != null ) $cr: CellRow( cells contains $c ) exists Cell( this != $c, value == $v, cellRow == $cr ) then System.out.println( "cell " + $c.toString() + " has a duplicate in row " + $cr.getNumber() ); end rule "duplicate in cell col" when $c: Cell( $v: value != null ) $cc: CellCol( cells contains $c ) exists Cell( this != $c, value == $v, cellCol == $cc ) then System.out.println( "cell " + $c.toString() + " has a duplicate in col " + $cc.getNumber() ); end rule "duplicate in cell sqr" when $c: Cell( $v: value != null ) $cs: CellSqr( cells contains $c ) exists Cell( this != $c, value == $v, cellSqr == $cs ) then System.out.println( "cell " + $c.toString() + " has duplicate in its square of nine." ); end
ルール "terminate group"
は最後に実行されます。このルールは、メッセージを出力して、シーケンスを停止します。
ルール "terminate group"
rule "terminate group" salience -100 when then System.out.println( "Validation complete." ); drools.halt(); end
数独の解決ルール (sudoku.drl)
数独の例の sudoku.drl
ファイルには、3 種類のルールタイプが含まれます。1 つ目のグループは、セルへの数値の割り当てを処理して、2 つ目は実行可能な割り当てを検出して、3 つ目は候補セットからの値を削除します。
"set a value"
、"eliminate a value from Cell"
、および "retract setting"
のルールは、Setting
オブジェクトの有無により左右されます。最初のルールは、セルへの割り当てと、3 つのセルグループの free
セットから値を削除する操作を処理します。また、ゼロの場合は、このグループでカウンターが 1 つ減り、fireUntilHalt()
を呼び出した Java アプリケーションに制御を戻します。
"eliminate a value from Cell"
ルールの目的は、新たに割り当てられたセルに関連する全セルの候補リストを絞り込むことです。最後に、すべての除外が完了したら、"retract setting"
ルールにより、トリガーされている Setting
ファクトを取り消します。
ルール "set a value"、"eliminate a value from a Cell"、および "retract setting"
// A Setting object is inserted to define the value of a Cell. // Rule for updating the cell and all cell groups that contain it rule "set a value" when // A Setting with row and column number, and a value $s: Setting( $rn: rowNo, $cn: colNo, $v: value ) // A matching Cell, with no value set $c: Cell( rowNo == $rn, colNo == $cn, value == null, $cr: cellRow, $cc: cellCol, $cs: cellSqr ) // Count down $ctr: Counter( $count: count ) then // Modify the Cell by setting its value. modify( $c ){ setValue( $v ) } // System.out.println( "set cell " + $c.toString() ); modify( $cr ){ blockValue( $v ) } modify( $cc ){ blockValue( $v ) } modify( $cs ){ blockValue( $v ) } modify( $ctr ){ setCount( $count - 1 ) } end // Rule for removing a value from all cells that are siblings // in one of the three cell groups rule "eliminate a value from Cell" when // A Setting with row and column number, and a value $s: Setting( $rn: rowNo, $cn: colNo, $v: value ) // The matching Cell, with the value already set Cell( rowNo == $rn, colNo == $cn, value == $v, $exCells: exCells ) // For all Cells that are associated with the updated cell $c: Cell( free contains $v ) from $exCells then // System.out.println( "clear " + $v + " from cell " + $c.posAsString() ); // Modify a related Cell by blocking the assigned value. modify( $c ){ blockValue( $v ) } end // Rule for eliminating the Setting fact rule "retract setting" when // A Setting with row and column number, and a value $s: Setting( $rn: rowNo, $cn: colNo, $v: value ) // The matching Cell, with the value already set $c: Cell( rowNo == $rn, colNo == $cn, value == $v ) // This is the negation of the last pattern in the previous rule. // Now the Setting fact can be safely retracted. not( $x: Cell( free contains $v ) and Cell( this == $c, exCells contains $x ) ) then // System.out.println( "done setting cell " + $c.toString() ); // Discard the Setter fact. delete( $s ); // Sudoku.sudoku.consistencyCheck(); end
解決ルールを 2 つ使用して、セルに数字を割り当てることができる状況を検出します。"single"
のルールは、Cell
に、数字が 1 つだけの候補セットが含まれる場合に実行します。"hidden single"
ルールは、候補が 1 つだけのセルが存在しない場合に実行しますが、セルに候補が含まれる場合は、セルが所属する 3 つのグループの 1 つに含まれるその他のすべてのセルに、この候補が存在しないということです。いずれのルールも Setting
ファクトを作成して、挿入します。
ルール "single" および "hidden single"
// Detect a set of candidate values with cardinality 1 for some Cell. // This is the value to be set. rule "single" when // Currently no setting underway not Setting() // One element in the "free" set $c: Cell( $rn: rowNo, $cn: colNo, freeCount == 1 ) then Integer i = $c.getFreeValue(); if (explain) System.out.println( "single " + i + " at " + $c.posAsString() ); // Insert another Setter fact. insert( new Setting( $rn, $cn, i ) ); end // Detect a set of candidate values with a value that is the only one // in one of its groups. This is the value to be set. rule "hidden single" when // Currently no setting underway not Setting() not Cell( freeCount == 1 ) // Some integer $i: Integer() // The "free" set contains this number $c: Cell( $rn: rowNo, $cn: colNo, freeCount > 1, free contains $i ) // A cell group contains this cell $c. $cg: CellGroup( cells contains $c ) // No other cell from that group contains $i. not ( Cell( this != $c, free contains $i ) from $cg.getCells() ) then if (explain) System.out.println( "hidden single " + $i + " at " + $c.posAsString() ); // Insert another Setter fact. insert( new Setting( $rn, $cn, $i ) ); end
最大グループからのルール (個別または 2 ~ 3 のグループ単位) は、数独パズルを手作業で解決するのに使用する、さまざまな解決手法を実装します。
"naked pair"
ルールは、グループの 2 つのセルで、全く同じ候補セットでサイズ 2
のものを検出します。これらの 2 つの値は、対象グループにあるその他のすべての候補セットから削除できます。
ルール "naked pair"
// A "naked pair" is two cells in some cell group with their sets of // permissible values being equal with cardinality 2. These two values // can be removed from all other candidate lists in the group. rule "naked pair" when // Currently no setting underway not Setting() not Cell( freeCount == 1 ) // One cell with two candidates $c1: Cell( freeCount == 2, $f1: free, $r1: cellRow, $rn1: rowNo, $cn1: colNo, $b1: cellSqr ) // The containing cell group $cg: CellGroup( freeCount > 2, cells contains $c1 ) // Another cell with two candidates, not the one we already have $c2: Cell( this != $c1, free == $f1 /*** , rowNo >= $rn1, colNo >= $cn1 ***/ ) from $cg.cells // Get one of the "naked pair". Integer( $v: intValue ) from $c1.getFree() // Get some other cell with a candidate equal to one from the pair. $c3: Cell( this != $c1 && != $c2, freeCount > 1, free contains $v ) from $cg.cells then if (explain) System.out.println( "remove " + $v + " from " + $c3.posAsString() + " due to naked pair at " + $c1.posAsString() + " and " + $c2.posAsString() ); // Remove the value. modify( $c3 ){ blockValue( $v ) } end
3 つのルールの "hidden pair in …"
関数は、ルール "naked pair"
と同じように機能します。ルールはグループの 2 つのセルで 2 つの数字を検出します。どの値もこのグループの他のセルには入りません。つまり、他の候補はすべて、隠れたペアを持つ 2 つのセルから削除します。
ルール "hidden pair in …"
// If two cells within the same cell group contain candidate sets with more than // two values, with two values being in both of them but in none of the other // cells, then we have a "hidden pair". We can remove all other candidates from // these two cells. rule "hidden pair in row" when // Currently no setting underway not Setting() not Cell( freeCount == 1 ) // Establish a pair of Integer facts. $i1: Integer() $i2: Integer( this > $i1 ) // Look for a Cell with these two among its candidates. (The upper bound on // the number of candidates avoids a lot of useless work during startup.) $c1: Cell( $rn1: rowNo, $cn1: colNo, freeCount > 2 && < 9, free contains $i1 && contains $i2, $cellRow: cellRow ) // Get another one from the same row, with the same pair among its candidates. $c2: Cell( this != $c1, cellRow == $cellRow, freeCount > 2, free contains $i1 && contains $i2 ) // Ascertain that no other cell in the group has one of these two values. not( Cell( this != $c1 && != $c2, free contains $i1 || contains $i2 ) from $cellRow.getCells() ) then if( explain) System.out.println( "hidden pair in row at " + $c1.posAsString() + " and " + $c2.posAsString() ); // Set the candidate lists of these two Cells to the "hidden pair". modify( $c1 ){ blockExcept( $i1, $i2 ) } modify( $c2 ){ blockExcept( $i1, $i2 ) } end rule "hidden pair in column" when not Setting() not Cell( freeCount == 1 ) $i1: Integer() $i2: Integer( this > $i1 ) $c1: Cell( $rn1: rowNo, $cn1: colNo, freeCount > 2 && < 9, free contains $i1 && contains $i2, $cellCol: cellCol ) $c2: Cell( this != $c1, cellCol == $cellCol, freeCount > 2, free contains $i1 && contains $i2 ) not( Cell( this != $c1 && != $c2, free contains $i1 || contains $i2 ) from $cellCol.getCells() ) then if (explain) System.out.println( "hidden pair in column at " + $c1.posAsString() + " and " + $c2.posAsString() ); modify( $c1 ){ blockExcept( $i1, $i2 ) } modify( $c2 ){ blockExcept( $i1, $i2 ) } end rule "hidden pair in square" when not Setting() not Cell( freeCount == 1 ) $i1: Integer() $i2: Integer( this > $i1 ) $c1: Cell( $rn1: rowNo, $cn1: colNo, freeCount > 2 && < 9, free contains $i1 && contains $i2, $cellSqr: cellSqr ) $c2: Cell( this != $c1, cellSqr == $cellSqr, freeCount > 2, free contains $i1 && contains $i2 ) not( Cell( this != $c1 && != $c2, free contains $i1 || contains $i2 ) from $cellSqr.getCells() ) then if (explain) System.out.println( "hidden pair in square " + $c1.posAsString() + " and " + $c2.posAsString() ); modify( $c1 ){ blockExcept( $i1, $i2 ) } modify( $c2 ){ blockExcept( $i1, $i2 ) } end
2 つのルールは行と列で "X-wings"
を処理します。2 つの異なる行 (または列) で、ある値を入力できるセルが 2 つしかなく、これらの候補が同じ列 (または行) に入る場合に、この列 (または行) のこの値に対する他の候補は除外できます。これらのルールの 1 つに含まれるパターンシーケンスに従うと、same
、only
などの用語で都合よく表現されている条件は、適切な制約が付けられたパターンになるか、not
のプリフィックスが付きます。
ルール "X-wings in …"
rule "X-wings in rows" when not Setting() not Cell( freeCount == 1 ) $i: Integer() $ca1: Cell( freeCount > 1, free contains $i, $ra: cellRow, $rano: rowNo, $c1: cellCol, $c1no: colNo ) $cb1: Cell( freeCount > 1, free contains $i, $rb: cellRow, $rbno: rowNo > $rano, cellCol == $c1 ) not( Cell( this != $ca1 && != $cb1, free contains $i ) from $c1.getCells() ) $ca2: Cell( freeCount > 1, free contains $i, cellRow == $ra, $c2: cellCol, $c2no: colNo > $c1no ) $cb2: Cell( freeCount > 1, free contains $i, cellRow == $rb, cellCol == $c2 ) not( Cell( this != $ca2 && != $cb2, free contains $i ) from $c2.getCells() ) $cx: Cell( rowNo == $rano || == $rbno, colNo != $c1no && != $c2no, freeCount > 1, free contains $i ) then if (explain) { System.out.println( "X-wing with " + $i + " in rows " + $ca1.posAsString() + " - " + $cb1.posAsString() + $ca2.posAsString() + " - " + $cb2.posAsString() + ", remove from " + $cx.posAsString() ); } modify( $cx ){ blockValue( $i ) } end rule "X-wings in columns" when not Setting() not Cell( freeCount == 1 ) $i: Integer() $ca1: Cell( freeCount > 1, free contains $i, $c1: cellCol, $c1no: colNo, $ra: cellRow, $rano: rowNo ) $ca2: Cell( freeCount > 1, free contains $i, $c2: cellCol, $c2no: colNo > $c1no, cellRow == $ra ) not( Cell( this != $ca1 && != $ca2, free contains $i ) from $ra.getCells() ) $cb1: Cell( freeCount > 1, free contains $i, cellCol == $c1, $rb: cellRow, $rbno: rowNo > $rano ) $cb2: Cell( freeCount > 1, free contains $i, cellCol == $c2, cellRow == $rb ) not( Cell( this != $cb1 && != $cb2, free contains $i ) from $rb.getCells() ) $cx: Cell( colNo == $c1no || == $c2no, rowNo != $rano && != $rbno, freeCount > 1, free contains $i ) then if (explain) { System.out.println( "X-wing with " + $i + " in columns " + $ca1.posAsString() + " - " + $ca2.posAsString() + $cb1.posAsString() + " - " + $cb2.posAsString() + ", remove from " + $cx.posAsString() ); } modify( $cx ){ blockValue( $i ) } end
この 2 つのルール "intersection removal …"
は、1 つの行または 1 つの列のいずれかで、1 つの四角の中で一部の数字が制限されたことに基づきます。これは、この番号が行または列の 2 つまたは 3 つのセルのいずれかにある必要があり、グループの他のすべてのセルの候補セットから削除できることを意味します。このパターンは、発生制限を確立して、同じセルファイルの中、かつ四角の外のセルそれぞれに対して実行されます。
ルール "intersection removal …"
rule "intersection removal column" when not Setting() not Cell( freeCount == 1 ) $i: Integer() // Occurs in a Cell $c: Cell( free contains $i, $cs: cellSqr, $cc: cellCol ) // Does not occur in another cell of the same square and a different column not Cell( this != $c, free contains $i, cellSqr == $cs, cellCol != $cc ) // A cell exists in the same column and another square containing this value. $cx: Cell( freeCount > 1, free contains $i, cellCol == $cc, cellSqr != $cs ) then // Remove the value from that other cell. if (explain) { System.out.println( "column elimination due to " + $c.posAsString() + ": remove " + $i + " from " + $cx.posAsString() ); } modify( $cx ){ blockValue( $i ) } end rule "intersection removal row" when not Setting() not Cell( freeCount == 1 ) $i: Integer() // Occurs in a Cell $c: Cell( free contains $i, $cs: cellSqr, $cr: cellRow ) // Does not occur in another cell of the same square and a different row. not Cell( this != $c, free contains $i, cellSqr == $cs, cellRow != $cr ) // A cell exists in the same row and another square containing this value. $cx: Cell( freeCount > 1, free contains $i, cellRow == $cr, cellSqr != $cs ) then // Remove the value from that other cell. if (explain) { System.out.println( "row elimination due to " + $c.posAsString() + ": remove " + $i + " from " + $cx.posAsString() ); } modify( $cx ){ blockValue( $i ) } end
これらのルールは、すべてではありませんが、多くの数独パズルでは十分です。非常に難度の高いグリッドを解決するには、ルールセットにさらに複雑なルールが必要です。(最終的には、パズルは試行錯誤でしか解決できません)。