/drools-examples/src/main/java/org/drools/examples/sudoku/Sudoku.java

https://github.com/wintonxu/drools · Java · 346 lines · 260 code · 33 blank · 53 comment · 67 complexity · 5564faa7e198b9f0e6fe5630630e79ad MD5 · raw file

  1. /*
  2. * Copyright 2011 JBoss Inc
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.drools.examples.sudoku;
  17. import java.util.Formatter;
  18. import java.util.HashSet;
  19. import java.util.Set;
  20. import org.drools.KnowledgeBase;
  21. import org.drools.event.rule.ObjectInsertedEvent;
  22. import org.drools.event.rule.ObjectRetractedEvent;
  23. import org.drools.event.rule.ObjectUpdatedEvent;
  24. import org.drools.event.rule.WorkingMemoryEventListener;
  25. import org.drools.examples.sudoku.swing.AbstractSudokuGridModel;
  26. import org.drools.examples.sudoku.swing.SudokuGridEvent;
  27. import org.drools.examples.sudoku.swing.SudokuGridModel;
  28. import org.drools.runtime.StatefulKnowledgeSession;
  29. import org.drools.runtime.rule.FactHandle;
  30. /**
  31. * An object of this class solves Sudoku problems.
  32. */
  33. public class Sudoku extends AbstractSudokuGridModel implements SudokuGridModel {
  34. public static Sudoku sudoku;
  35. public Cell[][] cells;
  36. private CellSqr[][] sqrs = new CellSqr[][]{ new CellSqr[3], new CellSqr[3], new CellSqr[3] };
  37. private CellRow[] rows = new CellRow[9];
  38. private CellCol[] cols = new CellCol[9];
  39. private KnowledgeBase kBase;
  40. private StatefulKnowledgeSession session;
  41. private SudokuWorkingMemoryListener workingMemoryListener = new SudokuWorkingMemoryListener();
  42. private Counter counter;
  43. private Boolean explain = false;
  44. private FactHandle steppingFactHandle;
  45. private Stepping stepping;
  46. private boolean unsolvable = false;
  47. /**
  48. * Constructor.
  49. * @param kBase a Knowledge Base with rules for solving Sudoku problems.
  50. */
  51. public Sudoku(KnowledgeBase kBase) {
  52. this.kBase = kBase;
  53. sudoku = this;
  54. }
  55. /*
  56. * (non-Javadoc)
  57. * @see org.drools.examples.sudoku.swing.SudokuGridModel#getCellValue(int, int)
  58. */
  59. public String getCellValue(int iRow, int iCol) {
  60. if (cells == null) return " ";
  61. return cells[iRow][iCol].valueAsString();
  62. }
  63. /**
  64. * Nice printout of the grid.
  65. */
  66. public void dumpGrid() {
  67. Formatter fmt = new Formatter(System.out);
  68. fmt.format(" ");
  69. for (int iCol = 0; iCol < 9; iCol++) {
  70. fmt.format("Col: %d ", iCol);
  71. }
  72. System.out.println();
  73. for (int iRow = 0; iRow < 9; iRow++) {
  74. System.out.print("Row " + iRow + ": ");
  75. for (int iCol = 0; iCol < 9; iCol++) {
  76. if (cells[iRow][iCol].getValue() != null) {
  77. fmt.format(" --- %d --- ", cells[iRow][iCol].getValue());
  78. } else {
  79. StringBuilder sb = new StringBuilder();
  80. Set<Integer> perms = cells[iRow][iCol].getFree();
  81. for (int i = 1; i <= 9; i++) {
  82. if (perms.contains(i)) {
  83. sb.append(i);
  84. } else {
  85. sb.append(' ');
  86. }
  87. }
  88. fmt.format(" %-10s", sb.toString());
  89. }
  90. }
  91. System.out.println();
  92. }
  93. }
  94. /**
  95. * Checks that everything is still according to the sudoku rules.
  96. */
  97. public void consistencyCheck() {
  98. for (int iRow = 0; iRow < 9; iRow++) {
  99. for (int iCol = 0; iCol < 9; iCol++) {
  100. Cell cell = cells[iRow][iCol];
  101. Integer value = cell.getValue();
  102. if (value != null) {
  103. if (! cell.getFree().isEmpty()) {
  104. throw new IllegalStateException("free not empty");
  105. }
  106. // any containing group
  107. for (Cell other: cell.getExCells()) {
  108. // must not occur in any of the other cells
  109. if (value.equals(other.getValue())) {
  110. throw new IllegalStateException("duplicate");
  111. }
  112. // must not occur in the permissibles of any of the other cells
  113. if (other.getFree().contains(value)) {
  114. throw new IllegalStateException("not eliminated");
  115. }
  116. }
  117. }
  118. }
  119. }
  120. for (int i = 0; i < rows.length; i++) {
  121. Set<Integer> aSet = new HashSet<Integer>();
  122. for (int j = 0; j < rows[i].getCells().size(); j++) {
  123. Cell cell = rows[i].getCells().get(j);
  124. Integer value = cell.getValue();
  125. if (value != null) {
  126. aSet.add(value);
  127. } else {
  128. aSet.addAll(cell.getFree());
  129. }
  130. }
  131. if (! aSet.equals(CellGroup.allNine)) {
  132. throw new IllegalStateException("deficit in row");
  133. }
  134. }
  135. for (int i = 0; i < cols.length; i++) {
  136. Set<Integer> aSet = new HashSet<Integer>();
  137. for (int j = 0; j < cols[i].getCells().size(); j++) {
  138. Cell cell = cols[i].getCells().get(j);
  139. Integer value = cell.getValue();
  140. if (value != null) {
  141. aSet.add(value);
  142. } else {
  143. aSet.addAll(cell.getFree());
  144. }
  145. }
  146. if (! aSet.equals(CellGroup.allNine)) {
  147. throw new IllegalStateException("deficit in column");
  148. }
  149. }
  150. for (int ir = 0; ir < sqrs.length; ir++) {
  151. for (int ic = 0; ic < sqrs[ir] .length; ic++) {
  152. Set<Integer> aSet = new HashSet<Integer>();
  153. for (int j = 0; j < sqrs[ir][ic].getCells().size(); j++) {
  154. Cell cell = sqrs[ir][ic].getCells().get(j);
  155. Integer value = cell.getValue();
  156. if (value != null) {
  157. aSet.add(value);
  158. } else {
  159. aSet.addAll(cell.getFree());
  160. }
  161. }
  162. if (! aSet.equals(CellGroup.allNine)) {
  163. throw new IllegalStateException("deficit in square");
  164. }
  165. }
  166. }
  167. System.out.println("+++ check OK +++");
  168. }
  169. /*
  170. * (non-Javadoc)
  171. * @see org.drools.examples.sudoku.swing.SudokuGridModel#solve()
  172. */
  173. public void solve() {
  174. if (this.isSolved()) return;
  175. explain = false;
  176. session.setGlobal("explain", explain);
  177. if( steppingFactHandle != null ){
  178. session.retract( steppingFactHandle );
  179. steppingFactHandle = null;
  180. stepping = null;
  181. }
  182. this.session.fireAllRules();
  183. // dumpGrid();
  184. }
  185. /*
  186. * (non-Javadoc)
  187. * @see org.drools.examples.sudoku.swing.SudokuGridModel#step()
  188. */
  189. public void step() {
  190. if (this.isSolved()) return;
  191. explain = true;
  192. session.setGlobal("explain", explain);
  193. this.counter.setCount(1);
  194. session.update(session.getFactHandle(this.counter), this.counter);
  195. if( steppingFactHandle == null ){
  196. steppingFactHandle = session.insert( stepping = new Stepping() );
  197. }
  198. this.session.fireUntilHalt();
  199. if( stepping.isEmergency() ){
  200. this.unsolvable = true;
  201. }
  202. // dumpGrid();
  203. }
  204. public boolean isSolved() {
  205. for (int iRow = 0; iRow < 9; iRow++) {
  206. for (int iCol = 0; iCol < 9; iCol++) {
  207. if (cells[iRow][iCol].getValue() == null) return false;
  208. }
  209. }
  210. return true;
  211. }
  212. public boolean isUnsolvable(){
  213. return unsolvable;
  214. }
  215. private void create() {
  216. for (int i = 0; i < 9; i++) {
  217. session.insert(Integer.valueOf(i+1));
  218. rows[i] = new CellRow(i);
  219. cols[i] = new CellCol(i);
  220. }
  221. cells = new Cell[9][];
  222. for (int iRow = 0; iRow < 9; iRow++) {
  223. cells[iRow] = new Cell[9];
  224. for (int iCol = 0; iCol < 9; iCol++) {
  225. Cell cell = cells[iRow][iCol] = new Cell();
  226. rows[iRow].addCell(cell);
  227. cols[iCol].addCell(cell);
  228. }
  229. }
  230. for (int i = 0; i < 3; i++) {
  231. for (int j = 0; j < 3; j++) {
  232. sqrs[i][j] = new CellSqr(rows[i*3], rows[i*3+1], rows[i*3+2],
  233. cols[j*3], cols[j*3+1], cols[j*3+2]);
  234. }
  235. }
  236. for (int iRow = 0; iRow < 9; iRow++) {
  237. for (int iCol = 0; iCol < 9; iCol++) {
  238. cells[iRow][iCol].makeReferences(rows[iRow], cols[iCol], sqrs[iRow/3][iCol/3]);
  239. session.insert(cells[iRow][iCol]);
  240. }
  241. session.insert(rows[iRow]);
  242. session.insert(cols[iRow]);
  243. session.insert(sqrs[iRow/3][iRow%3]);
  244. }
  245. }
  246. /*
  247. * (non-Javadoc)
  248. * @see org.drools.examples.sudoku.swing.SudokuGridModel#setCellValues(java.lang.Integer[][])
  249. */
  250. public void setCellValues(Integer[][] cellValues) {
  251. if (session != null) {
  252. session.removeEventListener(workingMemoryListener);
  253. session.dispose();
  254. }
  255. this.session = kBase.newStatefulKnowledgeSession();
  256. session.setGlobal("explain", explain);
  257. session.addEventListener(workingMemoryListener);
  258. Setting s000 = new Setting(0, 0, 0);
  259. FactHandle fh000 = this.session.insert(s000);
  260. this.create();
  261. int initial = 0;
  262. for (int iRow = 0; iRow < 9; iRow++) {
  263. for (int iCol = 0; iCol < 9; iCol++) {
  264. Integer value = cellValues[iRow][iCol];
  265. if (value != null) {
  266. session.insert(new Setting(iRow, iCol, value));
  267. initial++;
  268. }
  269. }
  270. }
  271. this.counter = new Counter(initial);
  272. this.session.insert(counter);
  273. this.session.retract(fh000);
  274. this.session.fireUntilHalt();
  275. }
  276. /*
  277. * (non-Javadoc)
  278. * @see java.lang.Object#toString()
  279. */
  280. @Override
  281. public String toString() {
  282. StringBuilder sb = new StringBuilder();
  283. sb.append("Sudoku:").append('\n');
  284. for (int iRow = 0; iRow < 9; iRow++) {
  285. sb.append(" ").append(rows[iRow].toString()).append('\n');
  286. }
  287. return sb.toString();
  288. }
  289. class SudokuWorkingMemoryListener implements WorkingMemoryEventListener {
  290. public void objectInserted(ObjectInsertedEvent ev) {
  291. if (ev.getObject() instanceof Counter) {
  292. fireRestartEvent(null);
  293. }
  294. }
  295. public void objectRetracted(ObjectRetractedEvent ev) {
  296. }
  297. public void objectUpdated(ObjectUpdatedEvent ev) {
  298. if (ev.getObject() instanceof Cell) {
  299. Cell cell = (Cell) ev.getObject();
  300. if (cell.getValue() != null) {
  301. fireCellUpdatedEvent(new SudokuGridEvent(this,
  302. cell.getRowNo(),
  303. cell.getColNo(),
  304. cell.getValue()));
  305. }
  306. }
  307. }
  308. }
  309. public void validate(){
  310. session.getAgenda().getAgendaGroup( "validate" ).setFocus();
  311. session.fireUntilHalt();
  312. }
  313. }