85.8. Sudoku 示例决策(组合模式匹配、回调和 GUI 集成)
Sudoku 示例决定基于流行数量 puzzle Sudoku,演示了如何根据各种限制在 Red Hat Process Automation Manager 中使用规则来查找大型潜在解决方案空间的解决方案。本例还演示了如何将 Red Hat Process Automation Manager 规则集成到图形用户界面(GUI)中,在这种情况下,使用基于 Swing 的桌面应用程序,以及如何使用回调与正在运行的决策引擎交互,根据工作内存中的更改更新 GUI。
以下是 Sudoku 示例的概述:
-
名称 :sudo
ku -
主类 :
org.drools.examples.sudoku.SudokuExample(在src/main/java中) -
模块 :
drools-examples - 类型 :Java 应用程序
-
规则文件:
org.drools.examples.sudoku114.drl(在src/main/resources中) - 目标 :演示复杂模式匹配、问题问题、回调和 GUI 集成
Sudoku 是一个基于逻辑的数字放置 puzzle。目标是填充 9x9 网格,以便每列、每行和每 9 个 3x3 区域都包含从 1 到 9 次的数字。puzzle setter 提供了部分完成的网格,puzzle solver 的任务是完成具有这些限制的网格。
解决问题的一般策略是确保在插入新数字时,必须在其特定的 3x3 区域、行和列中是唯一的。这个 Sudoku 示例决策使用 Red Hat Process Automation Manager 规则从各种困难级别解决 Sudoku puzzles,并尝试解决包含无效条目的缺陷。
Sudoku 示例执行和交互
与其他 Red Hat Process Automation Manager 决策示例类似,您可以通过在 IDE 中作为 Java 应用程序运行 org.drools.examples.sudoku.SudokuExample 类来执行 Sudoku 示例。
当您执行 Sudoku 示例时,会出现 droyku Example GUI 窗口。此窗口包含一个空的网格,但程序在内部存储各种您可以加载和解决。
点 File
图 85.19. 启动后,Sudoku 示例 GUI
当您加载 简单示例 时,网格会根据 puzzle 的初始状态填充。
图 85.20. 在载入简单示例后,Sudoku 示例 GUI
ä»�以下选项ä¸é€‰æ‹©ï¼š
点 Solve 触发 Sudoku 示例中定义的规则,以填充剩余的值,并使按钮再次不活跃。
图 85.21. 已解决简单示例
点 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
Sudoku 示例包含一个破坏的示例文件,在示例中定义的规则可以解析。
点 File 5 值会出现两次,而这不允许使用。
图 85.22. broken Sudoku 示例初始状态
单击 Solve,以将过期规则应用到此无效的网格。Sudoku 示例中的关联规则会检测示例中的问题,并尝试尽可能解决 puzzle。这个过程没有完成,并保留一些单元为空。
过期规则活动显示在 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.
图 85.23. 有问题的示例解决方案尝试
标记为 Hard 的 Sudoku 文件示例更为复杂,一般规则可能无法解决它们。IDE 控制台窗口中会显示失败的解决方案尝试:
未解析的硬链接
Validation complete.
...
Sorry - can't solve this grid.
解决损坏的样本的规则根据仍为单元候选者的值集合实施标准过期技术。例如,如果集合包含单个值,则这是单元的值。对于在 9 个单元组中出现一个值,规则会插入带有某些特定单元的解决方案值的类型 设置 事实。这将导致从单元所属的所有其他单元中分离这个值,并且该值会被重新遍历。
示例中的其他规则可减少某些单元的感知值。规则 "naked pair", "hidden pair in row", "hidden pair in column", and "hidden pair in physical" 会消除可能性,但不建立解决方案。规则 "X-wings in rows", "'X-wings in column"', "intersection removal row", 和 "intersection removal column" 会执行更复杂的背景。
Sudoku 示例类
软件包 org.drools.examples.sudoku.swing 包含以下核心类集合,该类实现了 Sudoku puzzles 的框架:
-
SudokuGridModel类定义了一个接口,用来存储 Sudoku puzzle 作为Cell对象的 9x9 网格。 -
SudokuGridView类是一个 Swing 组件,它可以视觉化SudokuGridModel类的任何实现。 -
SudokuGridEvent和SudokuGridListener类在模型和视图之间进行通信状态更改。当单元值被解决或更改时,会触发事件。 -
SudokuGridSamples类提供了部分填充 Sudoku puzzles 用于演示目的。
这个软件包对 Red Hat Process Automation Manager 库没有任何依赖项。
软件包 org.drools.examples.sudoku 包含以下核心类集合,它们实现了元素 Cell 对象及其各种聚合:
-
CellFile类,带有子类型CellRow、CellCol和CellSqr,它们都是CellGroup类的子类型。 SetOfNine的Cell和CellGroup子类,它提供了一个没有类型Set; 的属性。对于<Integer>Cell类,该集合代表单个候选集。对于CellGroup类,该集合是其单元的所有候选集合(仍需要分配的数字集)。在 Sudoku 示例中,有 81
和 27CellCellGroup对象,以及Cell属性cellRow、cellCol、cellSqr以及CellGroup属性单元提供的链接。使用这些组件,您可以编写规则来检测特定情况,允许将值分配给单元,或从某些候选集中删除值。-
Setting类用于触发值分配附带的操作。所有规则中都会使用设置事实,以检测新情况以避免重新发生中间状态。 -
当
"Step"不会定期终止时,在低优先级规则中使用Stepping类来执行紧急停止。这个行为表示程序无法解决模糊。 -
主类
org.drools.examples.sudoku.SudokuExample实施了组合了所有这些组件的 Java 应用程序。
Sudoku 验证规则(validate.drl)
Sudoku 示例中的 validate.drl 文件包含验证规则,用于检测单元组中重复数字。它们合并到一个 "validate" 站组中,允许用户在用户加载 puzzle 后明确激活该规则。
三种规则 "duplicate in cell …" all 功能 的时间 条件如下:
- 规则中的第一个条件会找到带有分配值的单元。
- 规则中的第二个条件会拉取到该单元所属的三个单元组。
- 根据规则,最终条件找到一个单元(除前一个除外),其值为第一个单元和同一行、列或方括号。
规则 "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
Sudokucertification rules (sudoku.drl)
Sudoku 示例中的 sudoku.drl 文件包含三种类型的规则:一个组处理到单元的数字分配,另一个组检测到可行的分配,第三个组从 candidate 集合中删除值。
规则 "set a value", "eliminate a value from Cell", 和 "retract setting" 取决于 Setting 对象的存在。第一个规则处理到单元的分配,以及从单元格的三个组 的空闲 集合中删除值的操作。这个组也会减少计数器,当零时,将控制返回到名为 fireUntilHalt () 的 Java 应用程序。
规则 "eliminate a value from Cell" 的目的是减少与新分配的单元相关的所有单元的候选列表。最后,当完成所有提升后,规则 " Retract 设置" 会重新遍历触发器 设置 事实。
规则 "set a value", "eliminate a value from a Cell", and "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
双向规则会检测一个可能为单元分配数字的情况。规则 "single" 会为 Cell 触发一个包含单个数字的候选集。当单个候选不存在单元格时,规则 "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
来自最多组的规则,可以是独立或三组,可以手动实施用于手动的 Sudoku puzzles 的各种技术。
规则 "naked pair" 在组的两个单元中检测相同的大小为 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
三个规则 "hidden pair in …" 函数类似于规则 "naked pair "。这些规则检测一个组的两个单元中有两个数字的子集,且在组的任何其他单元中不会发生值。这意味着,所有其他候选者可以从两个单元格中删除。
规则"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
两个规则处理行和 列中的"X-wings "。当两个不同的行(或列)中只有两个可能的值的单元,且这些候选者也位于同一列(或行),那么可以消除列(或行)中这个值的所有其他候选者。当您遵循其中一个规则中的模式序列时,请注意,如何按照单词(如 相同或 仅 会导致带有适当约束或前缀的模式)表示的条件。
规则 "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
这两个规则 "intersection removal …" 基于一个方括号中的某个数字的限制,可以在一行或单个列中出现。这意味着这个数字必须在行或列的两或三个单元之一,并且可以从组所有其他单元的候选集合中删除。模式建立受限发生,然后针对方括号和同一单元文件之外的每个单元触发。
规则 "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
这些规则对于许多但不是 Sudoku puzzles 而言已经足够了。为了解决非常困难,规则集需要更复杂的规则。(类似地,只有试用和错误才能解决一些模糊。)