├── .classpath ├── .gitignore ├── .project ├── .settings └── org.eclipse.core.resources.prefs ├── LICENSE ├── Make-Jar.jardesc ├── README.md ├── _config.yml ├── diuf └── sudoku │ ├── Cell.java │ ├── Grid.java │ ├── Link.java │ ├── Settings.java │ ├── SolvingTechnique.java │ ├── applet │ └── SudokuApplet.java │ ├── generator │ ├── Generator.java │ ├── Point.java │ └── Symmetry.java │ ├── gui │ ├── APE.html │ ├── AboutDialog.java │ ├── AdvancedPlayer.html │ ├── AutoBusy.java │ ├── BUGs.html │ ├── BigClue.html │ ├── Diabolical.html │ ├── Easy.html │ ├── EmptyRectangle.html │ ├── Fiendish.html │ ├── Fiendish2.html │ ├── FourLinks.html │ ├── GenerateDialog.java │ ├── Grouped2StrongLinks.html │ ├── Hard.html │ ├── HintNode.java │ ├── HintsTreeBuilder.java │ ├── Knife.gif │ ├── L1Ring.html │ ├── Light.gif │ ├── Medium.html │ ├── Multiple.html │ ├── Quintuplet.html │ ├── SmallClue.html │ ├── Sudoku.gif │ ├── SudokuExplainer.java │ ├── SudokuFrame.java │ ├── SudokuPanel.java │ ├── Superior.html │ ├── SuperiorPlus.html │ ├── TUVWXYZ.html │ ├── TechniquesSelectDialog.java │ ├── ThreeLinkER.html │ ├── ThreeLinkEmL.html │ ├── ThreeStrongLinks.html │ ├── TwoStringKite.html │ ├── UL10.html │ ├── UL12Type3.html │ ├── UVWXYZ.html │ ├── Uniqueness.html │ ├── VWXYZ.html │ ├── Valid.html │ ├── WXYZ.html │ ├── Warning.gif │ ├── Welcome.html │ ├── XLoop.html │ ├── XYorXYZ.html │ └── gXLoop.html │ ├── io │ ├── ErrorMessage.java │ ├── FastSinCos.java │ └── SudokuIO.java │ ├── solver │ ├── DirectHint.java │ ├── DirectHintProducer.java │ ├── Hint.java │ ├── HintProducer.java │ ├── HintsAccumulator.java │ ├── IndirectHint.java │ ├── IndirectHintProducer.java │ ├── Rule.java │ ├── SingleHintAccumulator.java │ ├── Solver.java │ ├── WarningHint.java │ ├── WarningHintProducer.java │ ├── checks │ │ ├── Analyser.java │ │ ├── Analysis.html │ │ ├── AnalysisInfo.java │ │ ├── BruteForceAnalysis.java │ │ ├── DoubleSolution.html │ │ ├── DoubleSolutionWarning.java │ │ ├── DoubleValue.html │ │ ├── MissingCandidates.html │ │ ├── NoDoubles.java │ │ ├── NoSolution.html │ │ ├── NumberOfFilledCells.java │ │ ├── NumberOfValues.java │ │ ├── Solution.html │ │ ├── Solution.java │ │ ├── SolutionHint.java │ │ ├── SudokuSolved.html │ │ ├── TooFewCells.html │ │ ├── TooFewValues.html │ │ ├── UnderConstruction.html │ │ └── WarningMessage.java │ └── rules │ │ ├── AlignedExclusion.java │ │ ├── AlignedExclusionHint.html │ │ ├── AlignedExclusionHint.java │ │ ├── AlignedPairExclusion.java │ │ ├── AlignedPairExclusionHint.html │ │ ├── DirectHiddenSetHint.html │ │ ├── DirectHiddenSetHint.java │ │ ├── DirectLockingHint.html │ │ ├── DirectLockingHint.java │ │ ├── Fisherman.java │ │ ├── Grouped2LinksFishHint.html │ │ ├── GroupedStrongLinksHint.html │ │ ├── GroupedStrongLinksLoopHint.html │ │ ├── GroupedTCFishHint.html │ │ ├── HasParentPotentialHint.java │ │ ├── HiddenSet.java │ │ ├── HiddenSetHint.html │ │ ├── HiddenSetHint.java │ │ ├── HiddenSingle.java │ │ ├── HiddenSingleHint.html │ │ ├── HiddenSingleHint.java │ │ ├── Locking.java │ │ ├── LockingHint.html │ │ ├── LockingHint.java │ │ ├── NakedSet.java │ │ ├── NakedSetGen.java │ │ ├── NakedSetGenHint.html │ │ ├── NakedSetGenHint.java │ │ ├── NakedSetHint.html │ │ ├── NakedSetHint.java │ │ ├── NakedSingle.java │ │ ├── NakedSingleHint.html │ │ ├── NakedSingleHint.java │ │ ├── SimpleLockingHint.html │ │ ├── Single.html │ │ ├── StrongLinks.java │ │ ├── StrongLinksHint.html │ │ ├── StrongLinksHint.java │ │ ├── TUVWXYZWing.java │ │ ├── TUVWXYZWing2Hint.html │ │ ├── TUVWXYZWingHint.html │ │ ├── TUVWXYZWingHint.java │ │ ├── TurbotFish.java │ │ ├── TurbotFishHint.html │ │ ├── TurbotFishHint.java │ │ ├── UVWXYZWing.java │ │ ├── UVWXYZWing2Hint.html │ │ ├── UVWXYZWingHint.html │ │ ├── UVWXYZWingHint.java │ │ ├── VLocking.html │ │ ├── VLocking.java │ │ ├── VLockingHint.java │ │ ├── VWXYZWing.java │ │ ├── VWXYZWing2Hint.html │ │ ├── VWXYZWingHint.html │ │ ├── VWXYZWingHint.java │ │ ├── WXYZWing.java │ │ ├── WXYZWing2Hint.html │ │ ├── WXYZWingHint.html │ │ ├── WXYZWingHint.java │ │ ├── XYWing.java │ │ ├── XYWingHint.html │ │ ├── XYWingHint.java │ │ ├── XYZWingHint.html │ │ ├── chaining │ │ ├── BinaryChainingHint.java │ │ ├── CellChainingHint.java │ │ ├── Chaining.java │ │ ├── ChainingHint.java │ │ ├── CycleHint.java │ │ ├── DynamicCellReductionHint.html │ │ ├── DynamicContradictionHint.html │ │ ├── DynamicReductionHint.html │ │ ├── DynamicRegionReductionHint.html │ │ ├── ForcingChain.html │ │ ├── ForcingChainHint.java │ │ ├── ForcingXChain.html │ │ ├── FullChain.java │ │ ├── NishioHint.html │ │ ├── Potential.java │ │ ├── RegionChainingHint.java │ │ ├── StaticCellReductionHint.html │ │ ├── StaticRegionReductionHint.html │ │ ├── UnderConstruction.html │ │ ├── X-Cycle.html │ │ ├── XY-Cycle.html │ │ └── Y-Cycle.html │ │ ├── forcingCellFNC.java │ │ ├── forcingCellFNCHint.java │ │ ├── forcingCellNC.html │ │ ├── forcingCellNC.java │ │ ├── forcingCellNCHint.java │ │ ├── lockedFNC.java │ │ ├── lockedFNCHint.java │ │ ├── lockedNC.html │ │ ├── lockedNC.java │ │ ├── lockedNCHint.java │ │ └── unique │ │ ├── BivalueUniversalGrave.java │ │ ├── BivalueUniversalGrave1.html │ │ ├── BivalueUniversalGrave2.html │ │ ├── BivalueUniversalGrave3.html │ │ ├── BivalueUniversalGrave4.html │ │ ├── Bug1Hint.java │ │ ├── Bug2Hint.java │ │ ├── Bug3Hint.java │ │ ├── Bug4Hint.java │ │ ├── BugHint.java │ │ ├── UniqueLoopHint.java │ │ ├── UniqueLoopType1.html │ │ ├── UniqueLoopType1Hint.java │ │ ├── UniqueLoopType2.html │ │ ├── UniqueLoopType2Hint.java │ │ ├── UniqueLoopType3Hidden.html │ │ ├── UniqueLoopType3HiddenHint.java │ │ ├── UniqueLoopType3Naked.html │ │ ├── UniqueLoopType3NakedHint.java │ │ ├── UniqueLoopType4.html │ │ ├── UniqueLoopType4Hint.java │ │ └── UniqueLoops.java │ ├── test │ └── serate.java │ └── tools │ ├── Asker.java │ ├── CellSet.java │ ├── CommonTuples.java │ ├── HtmlLoader.java │ ├── LinkedSet.java │ ├── Pair.java │ ├── Permutations.java │ ├── SingletonBitSet.java │ ├── StrongReference.java │ ├── Twomutations.java │ └── ValuesFormatter.java └── doc ├── SukakuExplainer01.png ├── SukakuExplainer02.png ├── SukakuExplainer03.png ├── SukakuExplainer04.png ├── generateDiff.ods └── rules.ods /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .metadata 2 | bin/ 3 | tmp/ 4 | *.tmp 5 | *.bak 6 | *.swp 7 | *~.nib 8 | *.class 9 | local.properties 10 | .settings/ 11 | .loadpath 12 | .recommenders 13 | 14 | # External tool builders 15 | .externalToolBuilders/ 16 | 17 | # Locally stored "Eclipse launch configurations" 18 | *.launch 19 | 20 | # PyDev specific (Python IDE for Eclipse) 21 | *.pydevproject 22 | 23 | # CDT-specific (C/C++ Development Tooling) 24 | .cproject 25 | 26 | # CDT- autotools 27 | .autotools 28 | 29 | # Java annotation processor (APT) 30 | .factorypath 31 | 32 | # PDT-specific (PHP Development Tools) 33 | .buildpath 34 | 35 | # sbteclipse plugin 36 | .target 37 | 38 | # Tern plugin 39 | .tern-project 40 | 41 | # TeXlipse plugin 42 | .texlipse 43 | 44 | # STS (Spring Tool Suite) 45 | .springBeans 46 | 47 | # Code Recommenders 48 | .recommenders/ 49 | 50 | # Annotation Processing 51 | .apt_generated/ 52 | 53 | # Scala IDE specific (Scala & Java development for Eclipse) 54 | .cache-main 55 | .scala_dependencies 56 | .worksheet 57 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | SukakuExplainer 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jem.workbench.JavaEMFNature 16 | org.eclipse.jdt.core.javanature 17 | org.eclipse.jem.beaninfo.BeanInfoNature 18 | 19 | 20 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | #Mon Dec 24 18:30:39 CET 2007 2 | eclipse.preferences.version=1 3 | encoding/=ISO-8859-1 4 | -------------------------------------------------------------------------------- /Make-Jar.jardesc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sukaku Explainer: 2 | 3 | Is a sukaku (Pencilmark Sudoku) and sudoku Solver/Rater with a command line entry point and a GUI. It is based on serate 4 | modification of "Sudoku Explainer" by Nicolas Juillerat 5 | 6 | ![](/doc/SukakuExplainer01.png) 7 | 8 | ## Installation: 9 | 10 | Visit the [releases page](https://github.com/SudokuMonster/SukakuExplainer/releases) to download the latest version(s) of Sukaku Explainer and Serate. 11 | 12 | ## Usage: 13 | 14 | Sukaku Explainer has both a GUI and command line point functionalities. 15 | 16 | Visit the [Command line parametrs page](https://github.com/SudokuMonster/SukakuExplainer/wiki/Batch-mode-command-line-parameters) for further information. 17 | 18 | ### INVOCATION 19 | 20 | GUI: 21 | 22 | java.exe -jar SukakuExplainer.jar 23 | 24 | Command Line: 25 | 26 | java.exe -Xrs -Xmx500m -cp SukakuExplainer.jar diuf.sudoku.test.serate ... 27 | 28 | ### GUI 29 | 30 | Functionality is retained with vanilla sudoku puzzles. It also accepts parsing most 31 | vanilla audokus, Pencilmark grids or Sukakus 32 | 33 | ![](/doc/SukakuExplainer02.png) 34 | 35 | ![](/doc/SukakuExplainer03.png) 36 | 37 | ![](/doc/SukakuExplainer04.png) 38 | 39 | ## Contributors: 40 | 41 | @dobrichev 42 | 43 | @1to9only 44 | 45 | @SudokuMonster 46 | 47 | @sudokuwiki 48 | 49 | ## About: 50 | 51 | Sudoku Explainer (Nicolas Juillerat 2006-2019) is a popular Sudoku solver/generator/rater which solves every known Sudoku 52 | puzzle using logical techniques. This popularity and consistency was the basis of the [Patterns game](http://forum.enjoysudoku.com/patterns-game-1-5-t5760.html) on 53 | the "New Sudoku Players Forum". It was further modified to incorporate "serate" which allowed 54 | the explainer rating (ER), the pearl rating (EP) and he diamond rating (ED). 55 | 56 | Pencilmark Sudoku (Sukaku) is the normal extension of Sudoku and [Sukaku Explainer was developed](http://forum.enjoysudoku.com/help-with-sudoku-explainer-t6677-60.html) based 57 | on the serate modification of Sudoku explainer 58 | 59 | ## Credits: 60 | 61 | Nicolas Juillerat, Sudoku Explainer author 62 | 63 | gsf, serate modification of Sudoku Explainer 64 | 65 | ## License: 66 | 67 | GNU Lesser General Public License v2.1 68 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /diuf/sudoku/Link.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku; 7 | 8 | /** 9 | * A link between two potential values (candidates) of two cells. 10 | */ 11 | public class Link { 12 | 13 | private final Cell srcCell; 14 | private final int srcValue; 15 | private final Cell dstCell; 16 | private final int dstValue; 17 | 18 | 19 | public Link(Cell srcCell, int srcValue, Cell dstCell, int dstValue) { 20 | this.srcCell = srcCell; 21 | this.srcValue = srcValue; 22 | this.dstCell = dstCell; 23 | this.dstValue = dstValue; 24 | } 25 | 26 | public Cell getSrcCell() { 27 | return srcCell; 28 | } 29 | 30 | public int getSrcValue() { 31 | return srcValue; 32 | } 33 | 34 | public Cell getDstCell() { 35 | return dstCell; 36 | } 37 | 38 | public int getDstValue() { 39 | return dstValue; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /diuf/sudoku/SolvingTechnique.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku; 7 | 8 | public enum SolvingTechnique { 9 | 10 | HiddenSingle("Hidden Single"), 11 | DirectPointing("Direct Pointing"), 12 | DirectHiddenPair("Direct Hidden Pair"), 13 | NakedSingle("Naked Single"), 14 | forcingCellNC("Non-Consecutive Forcing Cell"), 15 | lockedNC("Locked Non-Consecutive"), 16 | forcingCellFNC("Ferz Non-Consecutive Forcing Cell"), 17 | lockedFNC("Ferz Locked Non-Consecutive"), 18 | DirectHiddenTriplet("Direct Hidden Triplet"), 19 | PointingClaiming("Pointing & Claiming"), 20 | VLocking("Generalized Intersections"), 21 | NakedPair("Naked Pair"), 22 | NakedPairGen("Generalized Naked Pair"), 23 | XWing("X-Wing"), 24 | HiddenPair("Hidden Pair"), 25 | NakedTriplet("Naked Triplet"), 26 | NakedTripletGen("Generalized Naked Triplet"), 27 | Swordfish("Swordfish"), 28 | HiddenTriplet("Hidden Triplet"), 29 | TurbotFish("Scraper, Kite, Turbot"), 30 | XYWing("XY-Wing"), 31 | XYZWing("XYZ-Wing"), 32 | // WWing("W-Wing"), 33 | WXYZWing("WXYZ-Wing"), 34 | UniqueLoop("Unique Rectangle / Loop"), 35 | NakedQuad("Naked Quad"), 36 | NakedQuadGen("Generalized Naked Quad"), 37 | Jellyfish("Jellyfish"), 38 | HiddenQuad("Hidden Quad"), 39 | ThreeStrongLinks("3 Strong-linked Fishes"), 40 | //VWXYZWing4("VWXYZ-Wing 4"), 41 | //VWXYZWing5("VWXYZ-Wing 5"), 42 | NakedQuintGen("Generalized Naked Quintuplet"), 43 | NakedSextGen("Generalized Naked Sextuplet"), 44 | VWXYZWing("VWXYZ-Wing"), 45 | BivalueUniversalGrave("Bivalue Universal Grave"), 46 | FourStrongLinks("4 Strong-Linked Fishes"), 47 | AlignedPairExclusion("Aligned Pair Exclusion"), 48 | FiveStrongLinks("5 Strong-Linked Fishes"), 49 | SixStrongLinks("6 Strong-Linked Fishes"), 50 | UVWXYZWing("UVWXYZ-Wing"), 51 | ForcingChainCycle("Forcing Chains & Cycles"), 52 | TUVWXYZWing("TUVWXYZ-Wing"), 53 | AlignedTripletExclusion("Aligned Triplet Exclusion"), 54 | NishioForcingChain("Nishio Forcing Chains"), 55 | MultipleForcingChain("Multiple Forcing Chains"), 56 | DynamicForcingChain("Dynamic Forcing Chains"), 57 | DynamicForcingChainPlus("Dynamic Forcing Chains (+)"), 58 | NestedForcingChain("Nested Forcing Chains"); 59 | 60 | private final String name; 61 | 62 | private SolvingTechnique(String name) { 63 | this.name = name; 64 | } 65 | 66 | @Override 67 | public String toString() { 68 | return name; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /diuf/sudoku/applet/SudokuApplet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.applet; 7 | 8 | import java.applet.*; 9 | 10 | import javax.swing.*; 11 | 12 | import diuf.sudoku.gui.*; 13 | 14 | /** 15 | * Minimal applet support for the sudoku explainer. 16 | */ 17 | public class SudokuApplet extends Applet { 18 | 19 | private static final long serialVersionUID = -1770658360372460892L; 20 | 21 | 22 | @Override 23 | public void init() { 24 | super.init(); 25 | SwingUtilities.invokeLater(new Runnable() { 26 | public void run() { 27 | new Thread() { 28 | @Override 29 | public void run() { 30 | try { 31 | /* 32 | * It seems that IE want to get the focus just after the applet 33 | * has started, which result in bringing our main window to the 34 | * back. This small delay is a hack that solves this problem. 35 | */ 36 | Thread.sleep(500); 37 | } catch (InterruptedException ex) {} 38 | SudokuExplainer.main(null); 39 | } 40 | }.start(); 41 | } 42 | }); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /diuf/sudoku/generator/Point.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.generator; 7 | 8 | 9 | public class Point { 10 | 11 | public final int x; 12 | public final int y; 13 | 14 | public Point(int x, int y) { 15 | this.x = x; 16 | this.y = y; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /diuf/sudoku/generator/Symmetry.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudokuMonster/SukakuExplainer/362854eea4e983017726d406ac9ee8a28909bcc7/diuf/sudoku/generator/Symmetry.java -------------------------------------------------------------------------------- /diuf/sudoku/gui/APE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | APE: The Sudoku requires an Aligned Pair Exclusion to solve. 4 | Rating: 6.2 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/AdvancedPlayer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AdvancedPlayer: The Sudoku requires the use of Forcing Chains
4 | Rating: 7.0 - 8.0 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/BUGs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | BUGs: The Sudoku requires Bivalue Universal Grave techniques to solve. 4 | Rating: 5.6 - 6.1 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/BigClue.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Big Clue

4 |

5 | {0} 6 |

7 | 8 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Diabolical.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Diabolical: The Sudoku requires the use of Forcing Chains or trial & error.
4 | Rating: 6.1 - 10.0 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Easy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Easy: The Sudoku can be solved using Hidden Singles in blocks only.
4 | Rating: 1.0 - 1.2 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/EmptyRectangle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Empty Rectangle: The Sudoku requires an 2 strong links one of which is an Empty rectangle to solve. 4 | No skyscrapers, 2-String Kites or XY-Wings allowed. 5 | Rating: 4.3 6 | 7 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Fiendish.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fiendish: The Sudoku requires techniques that can only be applied 4 | when the candidates are written in the empty cells. But Forcing Chains are not required. 5 | Rating: 2.6 - 6.0 6 | 7 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Fiendish2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fiendish 2: The Sudoku requires techniques that can only be applied 4 | when the candidates are written in the empty cells. Beyond intersections 5 | and possibly difficult but Forcing Chains are not required. 6 | Rating: 3.0 - 7.0 7 | 8 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/FourLinks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 strong links: Requires a pattern with 4 single value strong links which can be grouped. 4 | Rating: 5.8-6.1 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Grouped2StrongLinks.html: -------------------------------------------------------------------------------- 1 | 2 | 2 Grouped strong links: The Sudoku requires an 2 grouped strong links to solve. 3 | No XY or XYZ Wings allowed. 4 | Rating: 4.3 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Hard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hard: The Sudoku can be solved without writing candidates in empty cells. But it 4 | may require Direct Pointings, Direct Claimings, Direct Hidden Pairs or Naked Singles in 5 | addition to Hidden Singles. Rating: 1.6 - 2.5 6 | 7 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/HintNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.gui; 7 | 8 | import javax.swing.tree.*; 9 | 10 | import diuf.sudoku.solver.*; 11 | 12 | /** 13 | * A tree node representing a hint in the hints tree 14 | * of the user interface. 15 | */ 16 | public class HintNode extends DefaultMutableTreeNode { 17 | 18 | private static final long serialVersionUID = 7857073221166387482L; 19 | 20 | private final Hint hint; 21 | private String name; 22 | 23 | public HintNode(Hint hint) { 24 | super(); 25 | this.hint = hint; 26 | this.name = hint.toString(); 27 | } 28 | 29 | HintNode(String name) { 30 | super(); 31 | this.hint = null; 32 | this.name = name; 33 | } 34 | 35 | public Hint getHint() { 36 | return this.hint; 37 | } 38 | 39 | public String getName() { 40 | return this.name; 41 | } 42 | 43 | public boolean isHintNode() { 44 | return this.hint != null; 45 | } 46 | 47 | @Override 48 | public boolean getAllowsChildren() { 49 | return !isHintNode(); 50 | } 51 | 52 | private int getCountHints() { 53 | if (isHintNode()) 54 | return 1; 55 | else { 56 | int result = 0; 57 | for (int i = 0; i < super.getChildCount(); i++) { 58 | HintNode child = (HintNode)super.getChildAt(i); 59 | result += child.getCountHints(); 60 | } 61 | return result; 62 | } 63 | } 64 | 65 | public HintNode getNodeFor(Hint hint) { 66 | if (hint == null) 67 | return null; 68 | if (hint.equals(this.hint)) 69 | return this; 70 | for (int i = 0; i < getChildCount(); i++) { 71 | HintNode child = (HintNode)getChildAt(i); 72 | HintNode result = child.getNodeFor(hint); 73 | if (result != null) 74 | return result; 75 | } 76 | return null; 77 | } 78 | 79 | void appendCountChildrenToName() { 80 | int count = getCountHints(); 81 | if (count <= 1) 82 | this.name += " (" + count + " hint)"; 83 | else 84 | this.name += " (" + count + " hints)"; 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | return this.name; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/HintsTreeBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.gui; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.solver.*; 11 | 12 | /** 13 | * Builder for the hints tree. This class is responsible 14 | * for the classification of hints in groups, and counting 15 | * the number of hints in groups. 16 | */ 17 | public class HintsTreeBuilder { 18 | 19 | private HintNode _root = new HintNode("Hints"); 20 | private HintNode _directHintsNode = null; 21 | private HintNode _warningHintsNode = null; 22 | private HintNode _indirectHintsNode = null; 23 | 24 | private HintNode root() { 25 | return _root; 26 | } 27 | 28 | private HintNode directHintsNode() { 29 | if (_directHintsNode == null) { 30 | _directHintsNode = new HintNode("Sudoku Rules"); 31 | root().insert(_directHintsNode, 0); 32 | } 33 | return _directHintsNode; 34 | } 35 | 36 | private HintNode warningHintsNode() { 37 | if (_warningHintsNode == null) { 38 | _warningHintsNode = new HintNode("Informations"); 39 | root().add(_warningHintsNode); 40 | } 41 | return _warningHintsNode; 42 | } 43 | 44 | private HintNode indirectHintsNode() { 45 | if (_indirectHintsNode == null) { 46 | _indirectHintsNode = new HintNode("Solving Techniques"); 47 | root().add(_indirectHintsNode); 48 | } 49 | return _indirectHintsNode; 50 | } 51 | 52 | public HintNode buildHintsTree(List hints) { 53 | Map directHintsNodes = new HashMap(); 54 | Map warningHintsNodes = new HashMap(); 55 | Map indirectHintsNodes = new HashMap(); 56 | for (Hint hint : hints) { 57 | if (hint instanceof DirectHint) { 58 | DirectHint directHint = (DirectHint)hint; 59 | String producerName = directHint.getRule().toString(); 60 | HintNode parent = directHintsNodes.get(producerName); 61 | if (parent == null) { 62 | parent = new HintNode(producerName); 63 | directHintsNode().add(parent); 64 | directHintsNodes.put(producerName, parent); 65 | } 66 | HintNode node = new HintNode(hint); 67 | parent.add(node); 68 | } else if (hint instanceof WarningHint) { 69 | WarningHint iHint = (WarningHint)hint; 70 | String producerName = iHint.getRule().toString(); 71 | HintNode parent = warningHintsNodes.get(producerName); 72 | if (parent == null) { 73 | parent = new HintNode(producerName); 74 | warningHintsNode().add(parent); 75 | warningHintsNodes.put(producerName, parent); 76 | } 77 | HintNode node = new HintNode(hint); 78 | parent.add(node); 79 | } else { 80 | IndirectHint iHint = (IndirectHint)hint; 81 | String producerName = iHint.getRule().toString(); 82 | HintNode parent = indirectHintsNodes.get(producerName); 83 | if (parent == null) { 84 | parent = new HintNode(producerName); 85 | indirectHintsNode().add(parent); 86 | indirectHintsNodes.put(producerName, parent); 87 | } 88 | HintNode node = new HintNode(hint); 89 | parent.add(node); 90 | } 91 | } 92 | if (_root != null) 93 | _root.appendCountChildrenToName(); 94 | if (_directHintsNode != null) 95 | _directHintsNode.appendCountChildrenToName(); 96 | if (_indirectHintsNode != null) 97 | _indirectHintsNode.appendCountChildrenToName(); 98 | for (HintNode topNode : directHintsNodes.values()) 99 | topNode.appendCountChildrenToName(); 100 | for (HintNode topNode : indirectHintsNodes.values()) 101 | topNode.appendCountChildrenToName(); 102 | return _root; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Knife.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudokuMonster/SukakuExplainer/362854eea4e983017726d406ac9ee8a28909bcc7/diuf/sudoku/gui/Knife.gif -------------------------------------------------------------------------------- /diuf/sudoku/gui/L1Ring.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | L1 Ring: Requires a pattern with 3 or 4 single value strong links forming an X-Loop. 4 | Rating: 4.0-6.6 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Light.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudokuMonster/SukakuExplainer/362854eea4e983017726d406ac9ee8a28909bcc7/diuf/sudoku/gui/Light.gif -------------------------------------------------------------------------------- /diuf/sudoku/gui/Medium.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Medium: The Sudoku requires Hidden Singles in row or columns in order 4 | to be solved.
5 | Rating: 1.3 - 1.5 6 | 7 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Multiple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Multiple hints selected

4 |

5 | You have selected multiple hints. Applied together, these hints could 6 | place the green values in the highlighted cells and 7 | remove the red values from their empty cells. 8 | Click the "Apply hint" button to apply all the selected hints together. 9 |

10 |

11 | To get an explanation on a particular hint, select only that hint. 12 |

13 | 14 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Quintuplet.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Quintuplet: The Sudoku solves using generalized naked quintuplet. 4 | Rating: 5.6 - 7.1 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/SmallClue.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Small Clue

4 |

5 | {0} 6 |

7 | 8 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Sudoku.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudokuMonster/SukakuExplainer/362854eea4e983017726d406ac9ee8a28909bcc7/diuf/sudoku/gui/Sudoku.gif -------------------------------------------------------------------------------- /diuf/sudoku/gui/Superior.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Superior: The Sudoku requires Triple(s) and / or X-Wing(s) to solve. 4 | Rating: 3.2 - 4.0 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/SuperiorPlus.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Superior+: The Sudoku requires techniques that can only be applied 4 | when the candidates are written in the empty cells. But Forcing Chains are not required. No XY or XYZ wings allowed. 5 | Rating: 3.8 - 6.1 6 | 7 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/TUVWXYZ.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | TUVWXYZ-wing: The Sudoku requires a double-linked TUVWXYZ-wing to solve. No Chains 4 | Rating: 7.4 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/ThreeLinkER.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 3 strong links with 2 ER: Requires a fish with 3 strong links that has 2 empty rectangles. 4 | Rating: 5.7 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/ThreeLinkEmL.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 3 strong links with EmL: Requires a fish with 3 strong links that has an EmL type c or d (Grouped strong link in a block inferred from 2 parallel mini lines). 4 | Rating: 5.7 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/ThreeStrongLinks.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 3 strong links: Requires a pattern with 3 single value strong links (101 or 102 formation). 4 | Rating: 5.5-5.6 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/TwoStringKite.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2-String Kite: The Sudoku requires a 2-String Kite to solve. 4 | Rating: 4.1 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/UL10.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Uniqueness: The Sudoku requires Uniqueness Loop 10 techniques to solve. 4 | Rating: 5.0 - 5.3 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/UL12Type3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Uniqueness: The Sudoku requires Uniqueness Loop 12 Type 3 techniques to solve. 4 | Rating: 5.0 - 5.3 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/UVWXYZ.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | UVWXYZ-wing: The Sudoku requires a double-linked UVWXYZ-wing to solve. 4 | Rating: 6.6 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Uniqueness.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Uniqueness: The Sudoku requires Uniqueness techniques to solve. 4 | Rating: 4.5 - 4.9 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/VWXYZ.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | VWXYZ-wing: The Sudoku requires a double-linked VWXYZ-wing to solve. 4 | Rating: 6.2 - 6.4 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Valid.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This Sudoku is valid

4 |

5 | It has a solution, and the solution is unique. 6 |

7 | 8 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/WXYZ.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WXYZ-wing: The Sudoku requires a double-linked WXYZ-wing to solve. 4 | Rating: 5.5 - 5.6 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/Warning.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudokuMonster/SukakuExplainer/362854eea4e983017726d406ac9ee8a28909bcc7/diuf/sudoku/gui/Warning.gif -------------------------------------------------------------------------------- /diuf/sudoku/gui/XLoop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | X-Loop: Requires a pattern with 3 or 4 single value strong links forming an X-Loop. 4 | Rating: 4.0-6.6 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/XYorXYZ.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Empty Rectangle: The Sudoku requires an XY-Wing or an XYZ-Wing to solve. 4 | Rating: 4.2-4.4 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/gui/gXLoop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | grouped X-Loop: Requires a pattern with 2 to 4 single value grouped strong links forming an X-Loop. 4 | Rating: 4.0-6.6 5 | 6 | -------------------------------------------------------------------------------- /diuf/sudoku/io/ErrorMessage.java: -------------------------------------------------------------------------------- 1 | package diuf.sudoku.io; 2 | 3 | /** 4 | * A generic error message. 5 | * Consists of a message with "{0}", "{1}", ... patterns 6 | * and zero or more arguments. The "{0}", "{1}", ... 7 | * patterns are replaced by the arguments when the 8 | * message is converted to a string by {@link #toString()}. 9 | */ 10 | public class ErrorMessage { 11 | 12 | private final String message; 13 | private final Object[] args; 14 | private boolean fatal = false; 15 | 16 | public ErrorMessage(String message, Object... args) { 17 | this.message = message; 18 | this.args = args; 19 | } 20 | 21 | public ErrorMessage(String message, boolean isFatal, Object... args) { 22 | this.message = message; 23 | this.args = args; 24 | this.fatal = isFatal; 25 | } 26 | 27 | public String getMessage() { 28 | return this.message; 29 | } 30 | 31 | public Object[] getArgs() { 32 | return this.args; 33 | } 34 | 35 | public void setFatal(boolean isFatal) { 36 | this.fatal = isFatal; 37 | } 38 | 39 | public boolean isFatal() { 40 | return this.fatal; 41 | } 42 | 43 | @Override 44 | public String toString() { 45 | String result = message; 46 | for (int i = 0; i < args.length; i++) { 47 | String pattern = "{" + i + "}"; 48 | String value = (args[i] == null ? "" : args[i].toString()); 49 | result = result.replace(pattern, value); 50 | } 51 | return result; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /diuf/sudoku/io/FastSinCos.java: -------------------------------------------------------------------------------- 1 | package diuf.sudoku.io; 2 | 3 | /** 4 | * Fast sine and cosine approximations. 5 | */ 6 | public class FastSinCos { 7 | 8 | private final static double B = 4.0 / Math.PI; 9 | private final static double C = -4.0 / (Math.PI * Math.PI); 10 | private final static double P = 0.218; 11 | private final static double hPI = Math.PI / 2.0; 12 | private final static double PI2 = Math.PI * 2.0; 13 | private final static double sPI = Math.PI * 1.5; 14 | 15 | 16 | public static double wrap(double angle) { 17 | angle = angle % PI2; 18 | if (angle > Math.PI) 19 | angle -= PI2; 20 | else if (angle < -Math.PI) 21 | angle += PI2; 22 | assert angle >= -Math.PI && angle <= Math.PI; 23 | return angle; 24 | } 25 | 26 | /** 27 | * Fast sine approximation 28 | * @param theta the argument 29 | * @return sin(theta) 30 | */ 31 | public static double fastSin(double theta) { 32 | return fastSin0(wrap(theta)); 33 | } 34 | 35 | /** 36 | * Fast cosine approximation 37 | * @param theta the argument 38 | * @return cos(theta) 39 | */ 40 | public static double fastCos(double theta) { 41 | return fastSin0(wrap(theta + hPI)); 42 | } 43 | 44 | /** 45 | * Fast sine approximation. Slightly faster than 46 | * {@link #fastSin(double)} but the argument must 47 | * be within the [-π..π] range! 48 | * @param theta the argument. 49 | * @return sin(theta) 50 | */ 51 | public static double fastSin0(double theta) { 52 | double y = B * theta + C * theta * Math.abs(theta); 53 | y = P * (y * Math.abs(y) - y) + y; 54 | return y; 55 | } 56 | 57 | /** 58 | * Fast cosine approximation. Can be slightly faster than 59 | * {@link #fastCos(double)} but the argument must 60 | * be within the [-π..π] range! 61 | * @param theta the argument. 62 | * @return cos(theta) 63 | */ 64 | public static double fastCos0(double theta) { 65 | if (theta > hPI) { 66 | theta -= sPI; // - 1.5pi 67 | } else { 68 | theta += hPI; // + 0.5pi 69 | } 70 | return fastSin0(theta); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/DirectHint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver; 7 | 8 | import diuf.sudoku.*; 9 | 10 | /** 11 | * Abstract class for hints that allow the direct placement of a value 12 | * in a cell of the sudoku grid. 13 | */ 14 | public abstract class DirectHint extends Hint { 15 | 16 | private DirectHintProducer rule; // The rule that produced this hint 17 | private Grid.Region region; // The concerned region, if any 18 | private Cell cell; // The cell that can be filled 19 | private int value; // The value that can be put in the cell 20 | 21 | 22 | /** 23 | * Create a new hint 24 | * @param rule the rule that discovered the hint 25 | * @param region the region for which the hint is applicable, 26 | * or null if irrelevent 27 | * @param cell the cell in which a value can be placed 28 | * @param value the value that can be placed in the cell 29 | */ 30 | public DirectHint(DirectHintProducer rule, Grid.Region region, Cell cell, int value) { 31 | this.rule = rule; 32 | this.region = region; 33 | this.cell = cell; 34 | this.value = value; 35 | } 36 | 37 | @Override 38 | public DirectHintProducer getRule() { 39 | return this.rule; 40 | } 41 | 42 | protected Grid.Region getRegion() { 43 | return this.region; 44 | } 45 | 46 | @Override 47 | public Grid.Region[] getRegions() { 48 | return new Grid.Region[] {this.region}; 49 | } 50 | 51 | @Override 52 | public Cell getCell() { 53 | return this.cell; 54 | } 55 | 56 | @Override 57 | public int getValue() { 58 | return this.value; 59 | } 60 | 61 | @Override 62 | public void apply(Grid targetGrid) { 63 | cell.setValueAndCancel(value, targetGrid); 64 | } 65 | 66 | @Override 67 | public boolean equals(Object o) { 68 | if (!(o instanceof DirectHint)) 69 | return false; 70 | DirectHint other = (DirectHint)o; 71 | return this.value == other.value && this.cell.equals(other.cell) && this.rule.equals(other.rule); 72 | } 73 | 74 | @Override 75 | public int hashCode() { 76 | return this.cell.hashCode() ^ this.rule.hashCode() ^ this.value; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | String result = cell.toString() + ": " + value; 82 | if (region != null) 83 | result += " in " + region.toString(); 84 | return result; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/DirectHintProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver; 7 | 8 | /** 9 | * Interface for rules that are able to produce direct hints. 10 | * @see diuf.sudoku.solver.DirectHint 11 | */ 12 | public interface DirectHintProducer extends HintProducer { 13 | 14 | public String toString(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/Hint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver; 7 | 8 | import diuf.sudoku.*; 9 | 10 | /** 11 | * Abstract class for a hint. 12 | *

13 | * A hint is (usually) capable of advancing one step in the solving 14 | * process of a sudoku. Warnings and info messages are also implemented 15 | * as hints, see {@link WarningHint} subclass. 16 | * @see DirectHint 17 | * @see IndirectHint 18 | */ 19 | public abstract class Hint { 20 | 21 | /** 22 | * Get the solving technique that discovered this hint 23 | * @return the solving technique that discovered this hint 24 | */ 25 | public abstract HintProducer getRule(); 26 | 27 | /** 28 | * Get the cell that can be filled, if any, 29 | * by applying this hint 30 | * @return the cell that can be filled 31 | */ 32 | public Cell getCell() { 33 | return null; 34 | } 35 | 36 | /** 37 | * Get the value that can be placed in the cell, 38 | * if any. 39 | * @return the value that can be placed in the cell 40 | * @see #getCell() 41 | */ 42 | public int getValue() { 43 | return 0; 44 | } 45 | 46 | /** 47 | * Apply this hint on the target sudoku grid 48 | */ 49 | public abstract void apply(Grid targetGrid); 50 | 51 | /** 52 | * Get the regions concerned by this hint. 53 | * null can be returned if this hint does 54 | * not depend on regions. 55 | * @return the regions concerned by this hint 56 | */ 57 | public abstract Grid.Region[] getRegions(); 58 | 59 | /** 60 | * Get a string representation of this hint. 61 | *

62 | * The string returned by this method is displayed in the hint tree. 63 | * @return a string representation of this hint 64 | */ 65 | @Override 66 | public abstract String toString(); 67 | 68 | /** 69 | * Get an HTML explanation of this hint, understandable by human beings. 70 | * @return an HTML explanation of this hint 71 | */ 72 | public abstract String toHtml(Grid grid); 73 | 74 | } 75 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/HintProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver; 7 | 8 | import diuf.sudoku.*; 9 | 10 | /** 11 | * Interface for solving techniques that are able to produce hints. 12 | * @see diuf.sudoku.solver.Hint 13 | */ 14 | public interface HintProducer { 15 | 16 | /** 17 | * Get all the hints that applicable of the given grid according to 18 | * this solving technique. 19 | * @param grid the sudoku grid 20 | * @param accu the accumulator in which to add hints 21 | * @throws InterruptedException if the search for hints has been interrupted. 22 | * This exception might be thrown by the accumulator and you must not try 23 | * to catch it. 24 | */ 25 | public void getHints(Grid grid, HintsAccumulator accu) throws InterruptedException; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/HintsAccumulator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver; 7 | 8 | /** 9 | * Accumulator for hints. 10 | * The accumulator can choose to throw an InterruptedException 11 | * whenever it has gathered enough hints. 12 | */ 13 | public interface HintsAccumulator { 14 | 15 | /** 16 | * Add an hint to this accumulator 17 | * @param hint the hint to add 18 | * @throws InterruptedException if this accumulator want to 19 | * stop the gathering of hints. You must not catch this exception. 20 | */ 21 | public void add(Hint hint) throws InterruptedException; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/IndirectHintProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver; 7 | 8 | /** 9 | * Interface for rules that are able to produce 10 | * indirect hints. 11 | * @see diuf.sudoku.solver.IndirectHint 12 | */ 13 | public interface IndirectHintProducer extends HintProducer { 14 | 15 | public String toString(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/Rule.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver; 7 | 8 | import diuf.sudoku.Grid; 9 | 10 | /** 11 | * A "classified hint" that can be used to advance one step in the 12 | * solving process of a Sudoku. 13 | * "Pseudo" hints such as warnings, analyses and informations do not 14 | * implement this interface. 15 | */ 16 | public interface Rule { 17 | 18 | /** 19 | * Get the name of this rule. 20 | *

21 | * This method will return the name of well-known rules such as 22 | * "naked pair", "X-Wing", etc. 23 | * @return the name of this rule 24 | */ 25 | public String getName(); 26 | 27 | /** 28 | * Get the short version of Rule Name 29 | */ 30 | public String getShortName(); 31 | 32 | /** 33 | * Get the difficulty rating of this rule. 34 | *

35 | * Currently, the following classification is used: 36 | *

    37 | *
  • 1.2: Hidden single (1.5 if not in block) 38 | *
  • 1.7: Direct Pointing 39 | *
  • 1.9: Direct Claiming 40 | *
  • 2.0: Direct Hidden Pair 41 | *
  • 2.3: Naked single 42 | *
  • 2.5: Direct Hidden Triplet 43 | *
  • 2.6: Pointing 44 | *
  • 2.8: Claiming 45 | *
  • 3.0, 3.2, 3.4: Naked pair, X-Wing, Hidden pair 46 | *
  • 3.6, 3.8, 4.0: Naked triplet, Swordfish, Hidden triplet 47 | *
  • 4.0, 4.1, 4.2: Skyscraper, 2-String Kite, Turbot Fish 48 | *
  • 4.2, 4.4: XY-Wing, XYZ-Wing 49 | // *
  • 4.4: W-Wing 50 | *
  • 4.5 - 5.0: Unique Rectangles and Loops 51 | *
  • 5.0, 5.2, 5.4: Naked quad, Jellyfish, Hidden quad 52 | *
  • 5.4, 5.5, 5.6: Skyscraper (3SL) , 3-String Kite, Turbot Fish (3SL) 53 | *
  • 5.5: WXYZ-Wing 54 | *
  • 5.6 - 6.0: Bivalue Universal Graves 55 | *
  • 6.2 - 6.4: VWXYZ-Wing 56 | *
  • 6.2: Aligned Pair Exclusion 57 | *
  • 6.5 - 7.5: X-Cycles, Y-Cycles 58 | *
  • 6.6 - 7.6: Forcing X-Chains 59 | *
  • 7.0 - 8.0: Forcing Chains, XY-Cycles 60 | *
  • 7.5: Aligned Triplet Exclusion 61 | *
  • 7.5 - 8.5: Nishio 62 | *
  • 8.0 - 9.0: Multiple chains 63 | *
  • 8.5 - 9.5: Dynamic chains 64 | *
  • 9.0 - 10.0: Dynamic chains (+) 65 | *
  • > 9.5: Nested Forcing Chains 66 | *
67 | * Upper bound for chains is actually unbounded: the longer chain, the higher rating. 68 | * @return the difficulty rating of this rule. 69 | */ 70 | 71 | //New changes 72 | /** 73 | * Get the difficulty rating of this rule. 74 | *

75 | * Currently, the following classification is used: 76 | *

    77 | *
  • 1.2: Hidden single (1.5 if not in block) 78 | *
  • 1.6: Naked single//2.3 ---> 1.6 79 | *
  • 1.7: Direct Pointing 80 | *
  • 1.9: Direct Claiming 81 | *
  • 2.0: Direct Hidden Pair 82 | *
  • 2.6: Pointing 83 | *
  • 2.8: Claiming 84 | *
  • 2.9: Hidden pair//3.4 ---> 2.9 85 | *
  • 3.0: Naked pair//3.0 ---> 3.1 ---> 3.0 86 | *
  • 3.1: Direct Hidden Triplet//2.5 ---> 3.0 ---> 3.1 87 | *
  • 3.2: X-Wing 88 | *
  • 3.6, 3.8, 4.0: Naked triplet, Hidden triplet, Swordfish//3.8 ---> 4.0 4.0 ---> 3.8 89 | *
  • 4.0, 4.1, 4.2: Skyscraper, 2-String Kite, Turbot Fish (placed before Swordfish) 90 | *
  • 4.2, 4.4: XY-Wing, XYZ-Wing 91 | // *
  • 4.4: W-Wing 92 | *
  • 4.5 - 5.0: Unique Rectangles and Loops 93 | *
  • 5.0, 5.2, 5.4: Naked quad, Hidden quad, Jellyfish//5.2 ---> 5.4 5.4 ---> 5.2 94 | *
  • 5.4, 5.5, 5.6: Skyscraper (3SL) , 3-String Kite, Turbot Fish (3SL) 95 | *
  • 5.5: WXYZ-Wing 96 | *
  • 5.6 - 6.0: Bivalue Universal Graves 97 | *
  • 6.2 - 6.4: VWXYZ-Wing 98 | *
  • 6.2: Aligned Pair Exclusion 99 | *
  • 6.5 - 7.5: X-Cycles, Y-Cycles 100 | *
  • 6.6 - 7.6: Forcing X-Chains 101 | *
  • 7.0 - 8.0: Forcing Chains, XY-Cycles 102 | *
  • 7.5: Aligned Triplet Exclusion 103 | *
  • 7.5 - 8.5: Nishio 104 | *
  • 8.0 - 9.0: Multiple chains 105 | *
  • 8.5 - 9.5: Dynamic chains 106 | *
  • 9.0 - 10.0: Dynamic chains (+) 107 | *
  • > 9.5: Nested Forcing Chains 108 | *
109 | * Upper bound for chains is actually unbounded: the longer chain, the higher rating. 110 | * @return the difficulty rating of this rule. 111 | */ 112 | 113 | public double getDifficulty(); 114 | 115 | /** 116 | * Get a clue, or a "partial hint", as an HTML string. 117 | * @param isBig true to get a big clue, that is, a 118 | * nearly complete hint; false to get a small clue 119 | * that is, a very partial hint. 120 | * @return a clue, in HTML 121 | */ 122 | public String getClueHtml(Grid grid, boolean isBig); 123 | 124 | 125 | } 126 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/SingleHintAccumulator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver; 7 | 8 | /** 9 | * Hints accumulator, that accumulates a single hint (the first that 10 | * is received) and then stops. 11 | */ 12 | public class SingleHintAccumulator implements HintsAccumulator { 13 | 14 | private Hint result = null; 15 | 16 | public SingleHintAccumulator() { 17 | super(); 18 | } 19 | 20 | public void add(Hint hint) throws InterruptedException { 21 | if (!hint.equals(result)) { 22 | result = hint; 23 | throw new InterruptedException(); 24 | } 25 | } 26 | 27 | /** 28 | * Get the only hint that has been accumulated, or null if no 29 | * hint has been received at all. 30 | * @return the only hint that has been collected 31 | */ 32 | public Hint getHint() { 33 | return result; 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /diuf/sudoku/solver/WarningHint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | 12 | /** 13 | * A hint that is not really a hint for solving a sudoku, but rather 14 | * to give an information on the sudoku, such as the fact that the sudoku 15 | * is not valid. 16 | */ 17 | public abstract class WarningHint extends IndirectHint { 18 | 19 | public WarningHint(WarningHintProducer rule) { 20 | super(rule, new HashMap()); 21 | } 22 | 23 | // @Override 24 | // public void apply() { 25 | // } 26 | 27 | @Override 28 | public Map getGreenPotentials(Grid grid, int viewNum) { 29 | return Collections.emptyMap(); 30 | } 31 | 32 | @Override 33 | public Map getRedPotentials(Grid grid, int viewNum) { 34 | return Collections.emptyMap(); 35 | } 36 | 37 | @Override 38 | public Collection getLinks(Grid grid, int viewNum) { 39 | return null; 40 | } 41 | 42 | @Override 43 | public Cell[] getSelectedCells() { 44 | return null; 45 | } 46 | 47 | public Collection getRedCells() { 48 | return Collections.emptyList(); 49 | } 50 | 51 | @Override 52 | public int getViewCount() { 53 | return 1; 54 | } 55 | 56 | @Override 57 | public boolean isWorth() { 58 | return true; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/WarningHintProducer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver; 7 | 8 | import diuf.sudoku.*; 9 | 10 | /** 11 | * Interface for techniques that are able to produce warnings and informations. 12 | * Typically implemented by classes that check the validity of a sudoku. 13 | */ 14 | public interface WarningHintProducer extends IndirectHintProducer { 15 | 16 | public void getHints(Grid grid, HintsAccumulator accu) throws InterruptedException; 17 | 18 | public String toString(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/Analyser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.checks; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.solver.*; 12 | import diuf.sudoku.tools.*; 13 | 14 | /** 15 | * Analyze a sudoku grid. 16 | *

17 | * This class tries to fully solve the sudoku using logical rules 18 | * only and then produce a single hint with the rating of 19 | * the sudoku, and the list of hints that have been used. 20 | *

21 | * If the sudoku is not valid, an appropriate warning hint is 22 | * produced. 23 | * @see diuf.sudoku.solver.checks.AnalysisInfo 24 | */ 25 | public class Analyser implements WarningHintProducer { 26 | 27 | private final Solver solver; 28 | private final Asker asker; 29 | 30 | 31 | public Analyser(Solver solver, Asker asker) { 32 | this.solver = solver; 33 | this.asker = asker; 34 | } 35 | 36 | public void getHints(Grid grid, HintsAccumulator accu) 37 | throws InterruptedException { 38 | Map rules = solver.solve(asker); 39 | Map ruleNames = solver.toNamedList(rules); 40 | Hint hint = new AnalysisInfo(this, rules, ruleNames); 41 | accu.add(hint); 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "Analysis"; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/Analysis.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Analysis results

4 |

5 | Difficulty rating: {0} 6 |

7 |

8 | This {2} can be solved using the following logical methods:
9 | {1} 10 |

11 | 12 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/AnalysisInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.checks; 7 | 8 | import java.text.*; 9 | import java.util.*; 10 | 11 | import diuf.sudoku.Grid; 12 | import diuf.sudoku.Grid.*; 13 | import diuf.sudoku.solver.*; 14 | import diuf.sudoku.tools.*; 15 | import diuf.sudoku.*; 16 | 17 | /** 18 | * A information hint produced by the {@link diuf.sudoku.solver.checks.Analyser} 19 | * class. Contains an approximate rating of the sudoku, and the list of hints that 20 | * have been used to solve it. The actual solution is not shown, and the grid 21 | * is not modified by applying this hint. 22 | * @see diuf.sudoku.solver.checks.Analyser 23 | */ 24 | public class AnalysisInfo extends WarningHint { 25 | 26 | private final Map rules; 27 | private final Map ruleNames; 28 | 29 | 30 | public AnalysisInfo(WarningHintProducer rule, Map rules, 31 | Map ruleNames) { 32 | super(rule); 33 | this.rules = rules; 34 | this.ruleNames = ruleNames; 35 | } 36 | 37 | @Override 38 | public Region[] getRegions() { 39 | return null; 40 | } 41 | 42 | @Override 43 | public String toHtml(Grid grid) { 44 | double difficulty = getDifficulty(); 45 | //New modification to add most difficult technique to Analysis windo in GUI 46 | String difficultRuleName = getDifficultyRuleName(); 47 | DecimalFormat format = new DecimalFormat("#0.0"); 48 | StringBuilder details = new StringBuilder(); 49 | //for (String ruleName : ruleNames.keySet()) { 50 | //int count = ruleNames.get(ruleName); 51 | for (Map.Entry entry : ruleNames.entrySet()) { 52 | String ruleName = entry.getKey(); 53 | int count = entry.getValue(); 54 | details.append(Integer.toString(count)); 55 | details.append(" x "); 56 | details.append(ruleName); 57 | details.append("
\n"); 58 | } 59 | details.append("The most difficult technique (ER): "+difficultRuleName+"
\n"); 60 | String result = HtmlLoader.loadHtml(this, "Analysis.html"); 61 | result = HtmlLoader.format(result, format.format(difficulty)+" ("+difficultRuleName+")", details, Settings.getInstance().variantString + (Settings.getInstance().isBlocks() ? " Sudoku" : "")); 62 | return result; 63 | } 64 | 65 | public double getDifficulty() { 66 | double difficulty = 0; 67 | for (Rule rule : rules.keySet()) { 68 | if (rule.getDifficulty() > difficulty) 69 | difficulty = rule.getDifficulty(); 70 | } 71 | return difficulty; 72 | } 73 | 74 | public String getDifficultyRuleName() { 75 | double difficulty = 0; 76 | String RuleName = ""; 77 | for (Rule rule : rules.keySet()) { 78 | if (rule.getDifficulty() > difficulty) 79 | difficulty = rule.getDifficulty(); 80 | RuleName = rule.getName(); 81 | } 82 | return RuleName; 83 | } 84 | 85 | 86 | @Override 87 | public String toString() { 88 | return "Sudoku Rating"; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/DoubleSolution.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This {0} is not valid

4 |

5 | This {0} has multiple solutions. Two different possible solutions can be 6 | viewed by selecting the "View 1" or the "View 2" under the grid. 7 | The solutions are highlighted in orange. 8 |

9 | 10 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/DoubleSolutionWarning.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.checks; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.solver.*; 12 | import diuf.sudoku.tools.*; 13 | 14 | /** 15 | * An hint that allows the user to show two different solutions 16 | * of a sudoku having more than one solutions. 17 | * @see diuf.sudoku.solver.checks.BruteForceAnalysis 18 | */ 19 | public class DoubleSolutionWarning extends WarningHint { 20 | 21 | //private Grid grid; 22 | private Grid solution1; 23 | private Grid solution2; 24 | //private int lastViewNum = 0; 25 | 26 | public DoubleSolutionWarning(WarningHintProducer rule, Grid source, Grid solution1, 27 | Grid solution2) { 28 | super(rule); 29 | //this.grid = source; 30 | this.solution1 = solution1; 31 | this.solution2 = solution2; 32 | } 33 | 34 | // @Override 35 | // public void apply() { 36 | // if (lastViewNum == 0) 37 | // solution1.copyTo(grid); 38 | // else 39 | // solution2.copyTo(grid); 40 | // // Clear all potentials 41 | // for (int y = 0; y < 9; y++) { 42 | // for (int x = 0; x < 9; x++) { 43 | // grid.getCell(x, y).clearPotentialValues(); 44 | // } 45 | // } 46 | // } 47 | 48 | @Override 49 | public Map getGreenPotentials(Grid grid, int viewNum) { 50 | Grid solution = (viewNum == 0 ? solution1 : solution2); 51 | Map result = new HashMap(); 52 | for (int y = 0; y < 9; y++) { 53 | for (int x = 0; x < 9; x++) { 54 | int value = solution.getCellValue(x, y); 55 | Cell cell = Grid.getCell(x, y); 56 | result.put(cell, SingletonBitSet.create(value)); 57 | } 58 | } 59 | //lastViewNum = viewNum; 60 | return result; 61 | } 62 | 63 | @Override 64 | public Map getRedPotentials(Grid grid, int viewNum) { 65 | return getGreenPotentials(grid, viewNum); 66 | } 67 | 68 | @Override 69 | public int getViewCount() { 70 | return 2; 71 | } 72 | 73 | @Override 74 | public boolean isWorth() { 75 | return true; 76 | } 77 | 78 | @Override 79 | public Grid.Region[] getRegions() { 80 | return null; 81 | } 82 | 83 | @Override 84 | public String toString() { 85 | return Settings.getInstance().variantString + (Settings.getInstance().isBlocks() ? " Sudoku" : "") +" has multiple solutions"; 86 | } 87 | 88 | @Override 89 | public String toHtml(Grid grid) { 90 | String result = HtmlLoader.loadHtml(this, "DoubleSolution.html"); 91 | return HtmlLoader.format(result, Settings.getInstance().variantString + (Settings.getInstance().isBlocks() ? " Sudoku" : "")); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/DoubleValue.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This {2} is not valid

4 |

5 | The value {0} appears more than once in the same {1}. 6 |

7 | 8 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/MissingCandidates.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This {0} is not valid

4 |

5 | Some potential values that are part of the solution are missing in some empty cells. 6 | But the {0} has a solution if the missing potential values are ignored. 7 |

8 |

9 | This probably means that you have manually removed a potential value that was 10 | part of the solution. Note that the {0} Explainer takes care 11 | of every manually removed potential value. 12 |

13 |

14 | Choose the menu Tools -> Reset potential values to automatically 15 | re-compute all potential values of all empty cells. Then you can try again solving 16 | this {0}. 17 |

18 | 19 | 20 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/NoSolution.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This {0} is not valid

4 |

5 | This {0} has no solution. 6 |

7 | 8 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/NumberOfFilledCells.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.checks; 7 | 8 | import diuf.sudoku.*; 9 | import diuf.sudoku.solver.*; 10 | 11 | /** 12 | * Check for invalid sudoku based on the number of clues. 13 | * If less than 17 clues are given, an appropriate warning hint is produced. 14 | */ 15 | public class NumberOfFilledCells implements WarningHintProducer { 16 | 17 | public void getHints(Grid grid, HintsAccumulator accu) 18 | throws InterruptedException { 19 | int countEmpty = 0; 20 | for (int y = 0; y < 9; y++) { 21 | for (int x = 0; x < 9; x++) { 22 | //if (grid.getCell(x, y).getValue() == 0) 23 | if (grid.getCellValue(x, y) == 0) 24 | countEmpty++; 25 | } 26 | } 27 | if (countEmpty == 0) { 28 | WarningMessage message = new WarningMessage(this, "The sudoku has been solved", 29 | "SudokuSolved.html"); 30 | accu.add(message); 31 | } 32 | // else if (countEmpty > 81 - 17) { 33 | // int given = 81 - countEmpty; 34 | // WarningMessage message = new WarningMessage(this, 35 | // "Sudoku is not valid", 36 | // "TooFewCells.html", given); 37 | // accu.add(message); 38 | // } 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "Number of clues"; 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/NumberOfValues.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.checks; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.solver.*; 12 | 13 | /** 14 | * Check for invalid sudoku based on the number of different values 15 | * that appear at least once. 16 | * If at least two values never appear, an appropriate warning hint 17 | * is produced. 18 | */ 19 | public class NumberOfValues implements WarningHintProducer { 20 | 21 | public void getHints(Grid grid, HintsAccumulator accu) 22 | throws InterruptedException { 23 | BitSet values = new BitSet(10); 24 | for (int y = 0; y < 9; y++) { 25 | for (int x = 0; x < 9; x++) { 26 | //Cell cell = grid.getCell(x, y); 27 | //int value = cell.getValue(); 28 | int value = grid.getCellValue(x, y); 29 | if (value != 0) 30 | values.set(value); 31 | } 32 | } 33 | //The following was disabled to allow Sukaku puzzles into GUI 34 | /** 35 | if (values.cardinality() < 8) { 36 | String missingValues = ""; 37 | for (int v = 1; v <= 9; v++) { 38 | if (!values.get(v)) { 39 | if (!missingValues.equals("")) 40 | missingValues += ", "; 41 | missingValues += v; 42 | } 43 | } 44 | WarningMessage message = new WarningMessage(this, 45 | "Sudoku has multiple solutions", 46 | "TooFewValues.html", missingValues, Settings.getInstance().variantString + (Settings.getInstance().isBlocks() ? " Sudoku" : "")); 47 | accu.add(message); 48 | } 49 | */ 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "Number of different values"; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/Solution.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Solution

4 |

5 | This is the solution of the Sudoku, computed using brute-force. 6 | The correct values are highlighted in orange. Click "Apply hint" to fill all 7 | cells with these values, or choose the menu Tools->Clear hint(s) 8 | to continue with the unsolved grid. 9 |

10 | 11 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/Solution.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.checks; 7 | 8 | import diuf.sudoku.*; 9 | import diuf.sudoku.solver.*; 10 | 11 | /** 12 | * Class that computes the solution of a sudoku using brute-force, 13 | * and produces an hint that allows the user to view the solution. 14 | */ 15 | public class Solution implements WarningHintProducer { 16 | 17 | public void getHints(Grid grid, HintsAccumulator accu) 18 | throws InterruptedException { 19 | Grid solution = new Grid(); 20 | grid.copyTo(solution); 21 | 22 | // First check for no, or multiple solution 23 | BruteForceAnalysis analyser = new BruteForceAnalysis(true); 24 | analyser.getHints(grid, accu); 25 | } 26 | 27 | @Override 28 | public String toString() { 29 | return "Brute force analysis"; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/SolutionHint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.checks; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.Grid.*; 12 | import diuf.sudoku.solver.*; 13 | import diuf.sudoku.tools.*; 14 | 15 | /** 16 | * Hint that allows the user to directly view the solution of a sudoku. 17 | */ 18 | public class SolutionHint extends WarningHint { 19 | 20 | //private final Grid grid; 21 | private final Grid solution; 22 | 23 | public SolutionHint(WarningHintProducer rule, Grid grid, Grid solution) { 24 | super(rule); 25 | //this.grid = grid; 26 | this.solution = solution; 27 | } 28 | 29 | @Override 30 | public Map getGreenPotentials(Grid grid, int viewNum) { 31 | Map result = new HashMap(); 32 | for (int y = 0; y < 9; y++) { 33 | for (int x = 0; x < 9; x++) { 34 | int value = solution.getCellValue(x, y); 35 | Cell cell = Grid.getCell(x, y); 36 | result.put(cell, SingletonBitSet.create(value)); 37 | } 38 | } 39 | return result; 40 | } 41 | 42 | @Override 43 | public Map getRedPotentials(Grid grid, int viewNum) { 44 | return getGreenPotentials(grid, viewNum); 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "Solution"; 50 | } 51 | 52 | @Override 53 | public Region[] getRegions() { 54 | return null; 55 | } 56 | 57 | @Override 58 | public String toHtml(Grid grid) { 59 | return HtmlLoader.loadHtml(this, "Solution.html"); 60 | } 61 | 62 | //SudokuMonster: Returned the ability to apply Brute force solution hint 63 | @Override 64 | public void apply(Grid grid) { 65 | solution.copyTo(grid); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/SudokuSolved.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

The Sudoku has been solved !

4 | 5 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/TooFewCells.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This {1} is not valid

4 |

5 | This {1} hasn't a unique solution: the number of clues (cells with a 6 | value) is too small. 7 |

8 | The minimum number of clues must be 17. {1}s with 16 clues or 9 | less all have more than one solution. This {1} has only {0} clues. 10 |

11 | 12 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/TooFewValues.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

This {1} is not valid

4 |

5 | This {1} has multiple solutions: there are at least two values that do not 6 | appear at all in the grid. Regardless of how you put these values, you can swap 7 | them once the {1} is solved and get another solution. 8 |

9 | More precisely, the following values do not appear: {0}. 10 |

11 | 12 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/UnderConstruction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sorry, the explanation of this rule is still under construction. 4 | 5 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/checks/WarningMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.checks; 7 | 8 | import diuf.sudoku.*; 9 | import diuf.sudoku.solver.*; 10 | import diuf.sudoku.tools.*; 11 | 12 | /** 13 | * A hint that just shows an arbitrary warning or information message 14 | */ 15 | public class WarningMessage extends WarningHint { 16 | 17 | private final String message; 18 | private final String htmlFile; 19 | private final Object[] args; 20 | 21 | public WarningMessage(WarningHintProducer rule, String message, 22 | String htmlFile, Object... args) { 23 | super(rule); 24 | this.message = message; 25 | this.htmlFile = htmlFile; 26 | this.args = args; 27 | } 28 | 29 | @Override 30 | public Grid.Region[] getRegions() { 31 | return null; 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return message; 37 | } 38 | 39 | @Override 40 | public String toHtml(Grid grid) { 41 | String result = HtmlLoader.loadHtml(this, htmlFile); 42 | return HtmlLoader.format(result, args); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/AlignedExclusionHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Aligned {0} Exclusion

4 |

5 | The cells {1} can together accept various combinations of values. But 6 | some combinations of values can be excluded, because they would leave some cells with 7 | no possible values. 8 |

9 |

10 | More precisely, the following combinations of values are not possible for the 11 | cells {1}:
12 | {2} 13 |

14 |

15 | Because some potential values of {3} occur in none of 16 | the remaining combinations, they can safely be removed. 17 |

18 | 19 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/AlignedPairExclusionHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Aligned {0} Exclusion

4 |

5 | The cells {1} can together accept various combinations of values. But 6 | some combinations of values can be excluded, because they would leave some cells with 7 | no possible values. 8 |

9 |

10 | More precisely, the following combinations of values are not possible for the 11 | cells {1}:
12 | {2} 13 |

14 |

15 | Because the value {4} of the cell {3} occurs in none of 16 | the remaining combinations, it can safely be removed. 17 |

18 | 19 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/DirectHiddenSetHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{4}

4 |

5 | The {0} cells {1} are the only possible positions of the 6 | {0} values {2} in the {3}. 7 | Because the number of possible cells matches the number of values, 8 | these {0} cells must contain these {0} values in any order. 9 |

10 |

11 | Other values can therefore not be in these cells. It 12 | follows that the cell {5} is the only remaining 13 | possible position of the value {6} in the 14 | {3}. 15 |

16 | 17 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/DirectLockingHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{3}

4 |

5 | All the potential positions of the value {0} in the 6 | {1} are in the same {2}. 7 | Because the {1} must contain the value {0}, the {2} will have the 8 | value {0} in one of the intersecting cells. 9 |

10 |

11 | The other potential positions of the value {0} that are in the 12 | {2} but not in the {1} are therefore not valid. 13 |

14 |

15 | As a result, the cell {4} is the only remaining position of 16 | the value {0} in its {1}. 17 |

18 | 19 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/Grouped2LinksFishHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{0}

4 |

5 | This technique relies on Conjugate Pairs that infer a strong link 6 | and/or a special arrangement of values in a line or block also known as 7 | Grouped Strong Links (where a strong link is inferred from a groups of 8 | values). 9 |

10 |

11 | the arrangement of cells with value {1} in {6} and 12 | in {7} (Green outlined regions) form strong links (conjugate pairs shown as green values) or grouped strong 13 | links (orange cell outline or yellow cell background). These strong links are weakly linked through {8} (Red outlined region). 14 |

15 |

16 | Any occurrence of the value {1} can therefore be removed from 17 | any cells sharing a {9} with all cells with value 18 | {1} in {6} and {7} that are not part of {8}. 19 |

20 |

21 | This technique is part of more generalized technique called X- chains which in itself 22 | is part of more generalized technique called Alternating Inference Chains (AIC). 23 |

24 |

25 | This technique is similar to a Finned X-Wing technique logic. 26 |

27 |

28 | The inference of a strong link from a group of cells with value {1} in 29 | any region can be spotted when a an "Empty mini Area (EmA)" is observed. This EmA 30 | could be an empty mini-Line (EmL) in a {9}. In the case of "Empty 31 | rectangle" the EmA is a group of empty cells that can also be referred to as 32 | "Empty mini Rectangle (EmR)". Variants like "Disjoint groups" and "Windows" can 33 | infer a strong link from cell arrangements similar to blocks. 34 |

35 | 36 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/GroupedStrongLinksHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{0}

4 |

5 | This technique relies on Conjugate Pairs that infer a strong link 6 | and/or a special arrangement of values in a line or block also known as 7 | Grouped Strong Links (where a strong link is inferred from groups of 8 | cells having the same value). These use strong links in Location inferred 9 | from 1 value (L1), therefore, Grouped L1-Wing can be used to describe 10 | this pattern.(Currently, L1 is reserved for 3 strong links). 11 |

12 |

13 | the arrangement of cells with value {1} in {2} and {3} 14 | form strong links (conjugate pairs, shown as green values) or grouped strong 15 | links (orange cell outline or yellow cell background). These strong links are weakly 16 | linked to each other via {6} and {7} (Red or Green outlined region). 17 |

18 |

19 | Any occurrence of the value {1} can therefore be removed from 20 | any cells sharing a region or regions with ALL cells with 21 | value {1} in {11} and {3} that are not part of {12} or {7}. 22 |

23 |

24 | This technique is part of more generalized technique called X- chains which in itself 25 | is part of more generalized technique called Alternating Inference Chains (AIC). 26 |

27 |

28 | This technique uses logic closely related to {10} technique logic. 29 |

30 |

31 | The grouped strong link when inferred from cells in a block that are 32 | arranged in one mini-row and one mini-column is also known commonly as 33 | "Empty rectangle". 34 |

35 |

36 | The inference of a strong link from a group of cells with value {1} in 37 | any region can be spotted when a an "Empty mini Area (EmA)" is observed. This EmA 38 | could be an empty mini-Line (EmL) in a region or regions. In the case of "Empty 39 | rectangle" the EmA is a group of empty cells that can also be referred to as 40 | "Empty mini Rectangle (EmR)". 41 |

42 | 43 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/GroupedStrongLinksLoopHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{0}

4 |

5 | This technique relies on Conjugate Pairs that infer a strong link 6 | and/or a special arrangement of values in a line or block also known as 7 | Grouped Strong Links (where a strong link is inferred from groups of 8 | cells having the same value) that form a loop (an X-Loop). These use strong links 9 | in Location inferred from 1 value (L1), therefore, L1-Ring can 10 | be used to describe this loop (Currently, L1 is reserved for 3 strong links). 11 |

12 |

13 | the arrangement of cells with value {1} in {2} and {3} 14 | (Green outlined region) form strong links (conjugate pairs, shown as green values) 15 | or grouped strong links (orange cell outline or yellow cell background). These 16 | strong links are weakly linked to each other via {6}, {7} and {13} 17 | (Red outlined region) forming a loop/ring. 18 |

19 |

20 | Any occurrence of the value {1} can therefore be removed from 21 | any cells sharing a region or regions with ALL cells with 22 | value {1} in ANY of {6}, {7} or {13} outside 23 | of {2} and {3}. 24 |

25 |

26 | This technique is part of more generalized technique called X-chains which in itself 27 | is part of more generalized technique called Alternating Inference Chains (AIC). 28 |

29 |

30 | This technique can also be described as {10}. 31 |

32 |

33 | The grouped strong link when inferred from cells in a block that are 34 | arranged in one mini-row and one mini-column is also known commonly as 35 | "Empty rectangle". 36 |

37 |

38 | The inference of a strong link from a group of cells with value {1} in 39 | any region can be spotted when a an "Empty mini Area (EmA)" is observed. This EmA 40 | could be an empty mini-Line (EmL) in a region or regions. In the case of "Empty 41 | rectangle" the EmA is a group of empty cells that can also be referred to as 42 | "Empty mini Rectangle (EmR)". 43 |

44 | 45 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/GroupedTCFishHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{0}

4 |

5 | This technique relies on Conjugate Pairs and a special 6 | arrangement of values in a block also known as Empty rectangle. 7 |

8 |

9 | The value {1} in {6} forms a strong link (conjugate pair) 10 | while the arrangement of cells with value {1} in {7} forms 11 | a pattern known as Empty rectangle where all cells with value {1} 12 | (orange cell outline or yellow cell background) can be mapped on one row and one column. 13 | The bridge cell {3} shares {8} with some cell(s) that have the 14 | value {1} in {7} therefore forming a weak link. 15 |

16 |

17 | Any occurrence of the value {1} can therefore be 18 | removed from any cells sharing a {9} 19 | with both start cell {2} and {7} cells 20 | with value {1} that are not part of {8}. 21 |

22 |

23 | This technique is similar to a 2x2 Finned Franken X-Wing technique logic. It 24 | is worth noting that some variants like "Disjoint groups" and "Windows" can 25 | display the same pattern of an "Empty rectangle" 26 |

27 | 28 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/HasParentPotentialHint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.solver.rules.chaining.*; 12 | 13 | 14 | /** 15 | * Interface for indirect hints that are able to tell what 16 | * {@link diuf.sudoku.solver.rules.chaining.Potential Potential}s 17 | * have been set to off before this rule could be applied. 18 | *

19 | * Used for chaining only. See package {@link diuf.sudoku.solver.rules.chaining}. 20 | */ 21 | public interface HasParentPotentialHint { 22 | 23 | /** 24 | * Get the potentials that were removed from the initial grid 25 | * before this rule could be applied. 26 | * @param initialGrid the initial grid, on which this rule 27 | * cannot be applied. 28 | * @param currentGrid the currewnt grid, on which this rule 29 | * is revealed. 30 | * @return the potentials that were removed from the initial grid. 31 | */ 32 | public Collection getRuleParents(Grid initialGrid, Grid currentGrid); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/HiddenSetHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{4}

4 |

5 | The {0} cells {1} are the only possible positions of the 6 | {0} values {2} in the {3}. 7 | Because the number of possible cells matches the number of values, 8 | these {0} cells must contain these {0} values in any order. 9 |

10 |

11 | Other potential values can therefore be removed from these cells. 12 |

13 | 14 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/HiddenSingle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.solver.*; 12 | 13 | 14 | /** 15 | * Implementation of the Hidden Single solving technique. 16 | */ 17 | public class HiddenSingle implements DirectHintProducer { 18 | 19 | public void getHints(Grid grid, HintsAccumulator accu) throws InterruptedException { 20 | // First alone cells (last empty cell in a region) 21 | if (Settings.getInstance().isBlocks()) 22 | getHints(grid, 0, accu, true); //block 23 | getHints(grid, 2, accu, true); //column 24 | getHints(grid, 1, accu, true); //row 25 | //SudokuMonster: Variants changes 26 | if (!Settings.getInstance().isVLatin()) { 27 | if (Settings.getInstance().isDG()) 28 | getHints(grid, 3, accu, true); //DG 29 | if (Settings.getInstance().isWindows()) 30 | getHints(grid, 4, accu, true); //Windows 31 | if (Settings.getInstance().isX()) { 32 | getHints(grid, 5, accu, true); //Main diagonal 33 | getHints(grid, 6, accu, true); //Anti diagonal 34 | } 35 | if (Settings.getInstance().isGirandola()) 36 | getHints(grid, 7, accu, true); //Girandola 37 | if (Settings.getInstance().isAsterisk()) 38 | getHints(grid, 8, accu, true); //Asterisk 39 | if (Settings.getInstance().isCD()) 40 | getHints(grid, 9, accu, true); //Center Dot 41 | } 42 | // Then hidden cells 43 | if (Settings.getInstance().isBlocks()) 44 | getHints(grid, 0, accu, false); //block 45 | getHints(grid, 2, accu, false); //column 46 | getHints(grid, 1, accu, false); //row 47 | if (!Settings.getInstance().isVLatin()) { 48 | if (Settings.getInstance().isDG()) 49 | getHints(grid, 3, accu, false); //DG 50 | if (Settings.getInstance().isWindows()) 51 | getHints(grid, 4, accu, false); //Windows 52 | if (Settings.getInstance().isX()) { 53 | getHints(grid, 5, accu, false); //Main diagonal 54 | getHints(grid, 6, accu, false); //Anti diagonal 55 | } 56 | if (Settings.getInstance().isGirandola()) 57 | getHints(grid, 7, accu, false); //Girandola 58 | if (Settings.getInstance().isAsterisk()) 59 | getHints(grid, 8, accu, false); //Asterisk 60 | if (Settings.getInstance().isCD()) 61 | getHints(grid, 9, accu, false); //Center Dot 62 | } 63 | } 64 | 65 | /** 66 | * For each parts of the given type, check if a value has only one 67 | * possible potential position. 68 | * @param regionTypeIndex the type of the parts to check 69 | */ 70 | private void getHints(Grid grid, int regionTypeIndex, 71 | HintsAccumulator accu, boolean aloneOnly) throws InterruptedException { 72 | Grid.Region[] regions = Grid.getRegions(regionTypeIndex); 73 | // Iterate on parts 74 | for (Grid.Region region : regions) { 75 | // Iterate on values 76 | for (int value = 1; value <= 9; value++) { 77 | // Get value's potential position 78 | BitSet potentialIndexes = region.getPotentialPositions(grid, value); 79 | if (potentialIndexes.cardinality() == 1) { 80 | // One potential position -> solution found 81 | int uniqueIndex = potentialIndexes.nextSetBit(0); 82 | Cell cell = region.getCell(uniqueIndex); 83 | boolean isAlone = region.getEmptyCellCount(grid) == 1; 84 | if (isAlone == aloneOnly) 85 | accu.add(new HiddenSingleHint(this, region, cell, value, isAlone)); 86 | } 87 | } 88 | } 89 | } 90 | 91 | @Override 92 | public String toString() { 93 | return "Hidden Singles"; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/HiddenSingleHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hidden Single

4 |

5 | The cell {0} is the only possible position of the 6 | value {1} in the {2}. 7 |

8 | 9 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/HiddenSingleHint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import diuf.sudoku.*; 9 | import diuf.sudoku.Grid.*; 10 | import diuf.sudoku.solver.*; 11 | import diuf.sudoku.tools.*; 12 | 13 | /** 14 | * Hidden Single hint 15 | */ 16 | public class HiddenSingleHint extends DirectHint implements Rule { 17 | 18 | private final boolean isAlone; // Last empty cell in a region 19 | 20 | 21 | public HiddenSingleHint(DirectHintProducer rule, Region region, Cell cell, int value, 22 | boolean isAlone) { 23 | super(rule, region, cell, value); 24 | this.isAlone = isAlone; 25 | } 26 | 27 | public double getDifficulty() { 28 | if (isAlone) 29 | return 1.0; 30 | //else if (getRegion() instanceof Grid.Block) 31 | else if (getRegion().getRegionTypeIndex() == 0) //block 32 | return 1.2; 33 | else 34 | return 1.5; 35 | } 36 | 37 | public String getName() { 38 | return "Hidden Single"; 39 | } 40 | 41 | public String getShortName() { 42 | return "HS"; 43 | } 44 | 45 | public String getClueHtml(Grid grid, boolean isBig) { 46 | if (isBig) { 47 | return "Look for a " + getName() + 48 | " in the " + getRegion().toFullString() + ""; 49 | } else { 50 | return "Look for a " + getName(); 51 | } 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return getName() + ": " + super.toString(); 57 | } 58 | 59 | @Override 60 | public String toHtml(Grid grid) { 61 | String result; 62 | if (isAlone) 63 | result = HtmlLoader.loadHtml(this, "Single.html"); 64 | else 65 | result = HtmlLoader.loadHtml(this, "HiddenSingleHint.html"); 66 | return HtmlLoader.format(result, super.getCell().toString(), 67 | Integer.toString(super.getValue()), super.getRegions()[0].toString()); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/LockingHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{0}

4 |

5 | All the potential positions of the value {1} in {2} different 6 | {3}s are in the same {2} {4}s. Because the number of 7 | {3}s matches the number of {4}s, and because each 8 | {3} must contain the value {1}; each 9 | {4} will have a {1} in one of the intersecting cells. 10 |

11 |

12 | The other potential positions of the value {1} that are in the 13 | {4}s but not in one of the {3}s can therefore be removed. 14 |

15 | 16 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/NakedSetGenHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{4}

4 |

5 | The only possible values for the {0} cells {1} of the 6 | {3} are the {0} values {2}. 7 | Because the number of possible values matches the number of cells, 8 | these {0} values must be located in these 9 | {0} cells in any order. 10 |

11 |

12 | Other potential positions of any of these values can therefore be 13 | removed from any cell in the grid that share region/regions with all 14 | cells in that set with that specific value. 15 |

16 | 17 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/NakedSetHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{4}

4 |

5 | The only possible values for the {0} cells {1} of the 6 | {3} are the {0} values {2}. 7 | Because the number of possible values matches the number of cells, 8 | these {0} values must be located in these 9 | {0} cells in any order. 10 |

11 |

12 | Other potential positions of these values can therefore be 13 | removed from the {3}. 14 |

15 | 16 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/NakedSingle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.solver.*; 12 | 13 | 14 | /** 15 | * Implementation of the Naked Single solving techniques. 16 | */ 17 | public class NakedSingle implements DirectHintProducer { 18 | 19 | /** 20 | * Check if a cell has only one potential value, and accumulate 21 | * corresponding hints 22 | */ 23 | public void getHints(Grid grid, HintsAccumulator accu) throws InterruptedException { 24 | // Grid.Region[] parts = grid.getRegions(Grid.Row.class); 25 | // // Iterate on parts 26 | // for (Grid.Region part : parts) { 27 | // // Iterate on cells 28 | // for (int index = 0; index < 9; index++) { 29 | // Cell cell = part.getCell(index); 30 | // // Get the cell's potential values 31 | // BitSet potentialValues = cell.getPotentialValues(); 32 | // if (potentialValues.cardinality() == 1) { 33 | // // One potential value -> solution found 34 | // int uniqueValue = potentialValues.nextSetBit(0); 35 | // accu.add(new NakedSingleHint(this, null, cell, uniqueValue)); 36 | // } 37 | // } 38 | // } 39 | for (int i = 0; i < 81; i++) { 40 | if(grid.getCellValue(i) != 0) continue; 41 | //BitSet potentialValues = cell.getPotentialValues(); 42 | BitSet potentialValues = grid.getCellPotentialValues(i); 43 | if (potentialValues.cardinality() == 1) { 44 | // One potential value -> solution found 45 | int uniqueValue = potentialValues.nextSetBit(0); 46 | Cell cell = Grid.getCell(i); 47 | accu.add(new NakedSingleHint(this, null, cell, uniqueValue)); 48 | } 49 | } 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "Naked Singles"; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/NakedSingleHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Naked Single

4 |

5 | The value {0} is the only possible value for the cell {1}. 6 |

7 | 8 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/NakedSingleHint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import diuf.sudoku.*; 9 | import diuf.sudoku.Grid.*; 10 | import diuf.sudoku.solver.*; 11 | import diuf.sudoku.tools.*; 12 | //import diuf.sudoku.Settings.*; 13 | 14 | 15 | public class NakedSingleHint extends DirectHint implements Rule { 16 | 17 | public NakedSingleHint(DirectHintProducer rule, Region region, Cell cell, int value) { 18 | super(rule, region, cell, value); 19 | } 20 | 21 | public double getDifficulty() { 22 | if (Settings.getInstance().revisedRating() == 1) 23 | return 1.6;//New rating 24 | else 25 | return 2.3;//Original rating 26 | } 27 | 28 | public String getName() { 29 | return "Naked Single"; 30 | } 31 | public String getShortName() { 32 | return "NS"; 33 | } 34 | 35 | public String getClueHtml(Grid grid, boolean isBig) { 36 | if (isBig) { 37 | return "Look for a " + getName() + 38 | " in the cell " + getCell().toString() + ""; 39 | } else { 40 | return "Look for a " + getName(); 41 | } 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return getName() + ": " + super.toString(); 47 | } 48 | 49 | @Override 50 | public String toHtml(Grid grid) { 51 | String result = HtmlLoader.loadHtml(this, "NakedSingleHint.html"); 52 | return HtmlLoader.format(result, Integer.toString(super.getValue()), 53 | super.getCell().toString()); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/SimpleLockingHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{3}

4 |

5 | All the potential positions of the value {0} in the 6 | {1} are in the same {2}. 7 | Because the {1} must contain the value {0}, the {2} will have the 8 | value {0} in one of the intersecting cells. 9 |

10 |

11 | The other potential positions of the value {0} that are in the 12 | {2} but not in the {1} can therefore be removed. 13 |

14 | 15 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/Single.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Single

4 |

5 | The cell {0} is the only remaining empty cell of the {2}. 6 | Its only possible value is the value {1}. 7 |

8 | 9 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/StrongLinksHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{0}

4 |

5 | This technique relies on Conjugate Pairs. 6 | The value {1} in {2} and {3} forms 7 | {4} (regional) strong links or conjugate pairs with {5} set(s) of bridge cells 8 | in {6} and {7}. The {4} strong links are joined therefore with 9 | {5} weak link(s). 10 |

11 |

12 | Any occurrence of the value {1} can be removed from any cells sharing 13 | a region or regions with both start and end cells {8} and {9}. 14 |

15 |

16 | This technique is closely related to {10} technique logic, Coloring 17 | technique or L1-Wing technique. It is a subcategory of X-Chains group of techniques 18 | which is in itself a subgroup of Alternating Inference Chains (AIC) group of techniques. 19 | These use strong links in Location inferred from 1 value (L1), therefore, 20 | L1-Wing can be used to describe this pattern (Currently L1 is used with 3 strong 21 | links only). 22 |

23 | 24 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/TUVWXYZWing2Hint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

TUVWXYZ-Ring {12}{10}{11}

4 |

5 | The seven cells {0}, {1}, {15}, {14}, {2}, {3} and 6 | {4} form a double-linked TUVWXYZ-Wing: The six 7 | cells {0}, {1}, {15}, {14}, {2} and {3} form an almost 8 | locked set linked to the bivalue cell {4} through both 9 | values {5} and {9} therefore creating a loop (ring). Regardless 10 | of the final value of cell {4}, the double-link would force the other 6 11 | cells from the pattern to have different values to that one. The 7 different 12 | values will be in 7 cells therefore forming a locked set. 13 |

14 |

15 | Other occurrences of any of the values of the Ring 16 | pattern can therefore be removed from any cells sharing a row, 17 | column or block with all Ring cells containing that value. 18 |

19 |

20 | The largest cell of the Ring has {10} candidates and all 6 cells of 21 | the ALS have a total of {11} candidates. The pattern is similar to 22 | a 7digit-7cell Sue de Coq (SDC). 23 |

24 | 25 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/TUVWXYZWingHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

TUVWXYZ-Wing {12}{10}{11}

4 |

5 | The seven cells {0}, {1}, {15}, {14}, {2}, {3} and 6 | {4} form a TUVWXYZ-Wing: The six cells {0}, 7 | {1}, {15}, {14}, {2} and {3} form an almost locked set 8 | linked to the bivalue cell {4} through the value {9}. 9 | If the value {5} was eliminated from all 7 cells of the 10 | Wing then the remaining value {9} in cell {4} would 11 | force the remaining 6 cells to have only 5 possible candidates to 12 | fill them. That is impossible! 13 |

14 |

15 | Other occurrences of the value {5} can therefore be removed 16 | from any cells sharing a {13} with all Wing cells 17 | containing the value {5}. 18 |

19 |

20 | The largest cell of the wing has {10} candidates and the 6 cells of 21 | the ALS have a total of {11} candidates. 22 |

23 | 24 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/TurbotFishHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{0}

4 |

5 | This technique relies on Conjugate Pairs. 6 | The value {1} in {6} and {7} forms 7 | 2 strong links (conjugate pairs). The bridge cells 8 | {3} and {4} share the same region ({8}), 9 | therefore forming a weak link. 10 |

11 |

12 | Any occurrence of the value {1} can therefore be 13 | removed from any cells sharing a {9} 14 | with both start cells {2} and {5}. 15 |

16 |

17 | This technique is similar to a 2x2 Finned Sashimi X-Wing technique logic. 18 |

19 | 20 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/UVWXYZWing2Hint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

UVWXYZ-Ring {12}{10}{11}

4 |

5 | The six cells {0}, {1}, {14}, {2}, {3} and 6 | {4} form a double-linked UVWXYZ-Wing: The five 7 | cells {0}, {1}, {14}, {2} and {3} form an almost 8 | locked set linked to the bivalue cell {4} through both 9 | values {5} and {9} therefore creating a loop (ring). Regardless of the 10 | final value of cell {4}, the double-link would force the other 5 cells from 11 | the pattern to have different values to that one. The 6 different values will be 12 | in 6 cells therefore forming a locked set. 13 |

14 |

15 | Other occurrences of any of the values of the Ring 16 | pattern can therefore be removed from any cells sharing a row, 17 | column or block with all Ring cells containing that value. 18 |

19 |

20 | The largest cell of the Ring has {10} candidates and all 5 cells of 21 | the ALS have a total of {11} candidates. The pattern is similar to 22 | a 6digit-6cell Sue de Coq (SDC). 23 |

24 | 25 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/UVWXYZWingHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

UVWXYZ-Wing {12}{10}{11}

4 |

5 | The six cells {0}, {1}, {14}, {2}, {3} and 6 | {4} form a UVWXYZ-Wing: The five cells {0}, 7 | {1}, {14}, {2} and {3} form an almost locked set 8 | linked to the bivalue cell {4} through the value {9}. 9 | If the value {5} was eliminated from all 6 cells of the 10 | Wing then the remaining value {9} in cell {4} would 11 | force the remaining 5 cells to have only 4 possible candidates to 12 | fill them. That is impossible! 13 |

14 |

15 | Other occurrences of the value {5} can therefore be removed 16 | from any cells sharing a {13} with all Wing cells 17 | containing the value {5}. 18 |

19 |

20 | The largest cell of the wing has {10} candidates and the 5 cells of 21 | the ALS have a total of {11} candidates. 22 |

23 | 24 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/VLocking.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{2}

4 |

5 | {0} Must have a cell with value {1} in the solution. 6 |

7 |

8 | Other occurrences of value {1} outside {0} which 9 | intersect (share region/regions) with all cells with value {1} 10 | in {0} can be safely eliminated. This technique covers pointing & claiming and can 11 | be thought of as Generalized intersections or Locked Candidates technique that suits variant 12 | regions specifically. 13 |

14 | 15 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/VLocking.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.solver.*; 12 | import diuf.sudoku.tools.*; 13 | 14 | /** 15 | * Implementation of Generalized Intersectio technique by Tarek Maani (@SudokuMonster). 16 | */ 17 | public class VLocking implements IndirectHintProducer { 18 | public void getHints(Grid grid, HintsAccumulator accu) throws InterruptedException { 19 | if (Settings.getInstance().isBlocks()) 20 | if (Settings.getInstance().isBlocks()) 21 | getHints(grid, 0, accu); //block 22 | getHints(grid, 1, accu); //row 23 | getHints(grid, 2, accu); //column 24 | //@SudokuMonster: Added Variants 25 | if (Settings.getInstance().isDG()) { 26 | getHints(grid, 3, accu); //DG 27 | } 28 | if (Settings.getInstance().isWindows()) { 29 | getHints(grid, 4, accu); //window 30 | } 31 | if (Settings.getInstance().isX()) { 32 | getHints(grid, 5, accu); //Main diagonal 33 | getHints(grid, 6, accu); //Anti diagonal 34 | } 35 | if (Settings.getInstance().isGirandola()) 36 | getHints(grid, 7, accu); //Girandola 37 | if (Settings.getInstance().isAsterisk()) 38 | getHints(grid, 8, accu); //Asterisk 39 | if (Settings.getInstance().isCD()) 40 | getHints(grid, 9, accu); //CD 41 | } 42 | 43 | /** 44 | * Given a region with 2 - 6 cells with a potential value 45 | * locked candidates > 4 rarely result in eliminations ouside 46 | * region so limited to 6 47 | * The potential value is locked in that region 48 | * Any cell with that value outside the region that sees all 49 | * the region cells with the potential value are targets 50 | * The technique subsumes locked candidates (intersection, pointing, claiming) 51 | * designed to handle variant regions interactions. 52 | * Useless in Vanilla or Latin square only 53 | */ 54 | private void getHints(Grid grid, int regionType1Index, 55 | HintsAccumulator accu) throws InterruptedException { 56 | // Iterate on values 57 | for (int value = 1; value <= 9; value++) { 58 | // Iterate on pairs of parts 59 | int regionsNumber = Grid.getRegions(regionType1Index).length; 60 | for (int i1 = 0; i1 < regionsNumber; i1++) { 61 | Grid.Region region1 = Grid.getRegions(regionType1Index)[i1]; 62 | // Get the potential positions of the value in part1 63 | BitSet potentialPositions = region1.getPotentialPositions(grid, value); 64 | // Note: if cardinality == 1, this is Hidden Single in part1 & if cardinality = 9 then it is definitely not useful 65 | int positionsCardinality = potentialPositions.cardinality(); 66 | if (positionsCardinality < 2 || positionsCardinality > 6) continue; 67 | int setPosition = 0; 68 | Cell[] regionCells = new Cell[positionsCardinality]; 69 | for(int i = potentialPositions.nextSetBit(0); i >= 0; i = potentialPositions.nextSetBit(i + 1)) 70 | regionCells[setPosition++] = region1.getCell(i); 71 | // Potential solution found 72 | IndirectHint hint = createVLockingHint(grid, region1, regionCells, value); 73 | if (hint.isWorth()) 74 | accu.add(hint); 75 | } 76 | } // for each value 77 | } 78 | 79 | private IndirectHint createVLockingHint(Grid grid, Grid.Region p1, Cell[] hcell, int value) { 80 | // Build removable potentials 81 | Map removablePotentials = new HashMap(); 82 | CellSet victims = new CellSet (hcell[0].getVisibleCells()); 83 | for (int i = 1; i < hcell.length; i++) { 84 | victims.retainAll(hcell[i].getVisibleCells()); 85 | victims.remove(hcell[i]); 86 | } 87 | int eliminationsTotal = 0; 88 | for (Cell cell : victims) { 89 | if (grid.hasCellPotentialValue(cell.getIndex(), value)) { 90 | eliminationsTotal++; 91 | if (removablePotentials.containsKey(cell)) 92 | removablePotentials.get(cell).set(value); 93 | else 94 | removablePotentials.put(cell, SingletonBitSet.create(value)); 95 | } 96 | } 97 | // Build hint 98 | return new VLockingHint(this, hcell, value, 99 | removablePotentials, p1, eliminationsTotal); 100 | } 101 | @Override 102 | public String toString() { 103 | return "Generalized Intersections"; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/VLockingHint.java: -------------------------------------------------------------------------------- 1 | package diuf.sudoku.solver.rules; 2 | 3 | import java.util.*; 4 | 5 | import diuf.sudoku.*; 6 | import diuf.sudoku.Grid.*; 7 | import diuf.sudoku.solver.*; 8 | import diuf.sudoku.solver.rules.chaining.*; 9 | import diuf.sudoku.tools.*; 10 | 11 | 12 | /** 13 | * WXYZ-Wing hints 14 | */ 15 | public class VLockingHint extends IndirectHint implements Rule, HasParentPotentialHint { 16 | 17 | private final Cell[] regionCells; 18 | private final int Value; 19 | private final Region lockedRegion; 20 | private final int eliminationsTotal; 21 | 22 | public VLockingHint(VLocking rule, Cell[] regionCells, int Value, Map removablePotentials, Region lockedRegion, int eliminationsTotal) { 23 | super(rule, removablePotentials); 24 | this.regionCells = regionCells; 25 | this.Value = Value; 26 | this.lockedRegion = lockedRegion; 27 | this.eliminationsTotal = eliminationsTotal; 28 | } 29 | 30 | @Override 31 | public Map getGreenPotentials(Grid grid, int viewNum) { 32 | BitSet vSet = SingletonBitSet.create(Value); 33 | Map result = new HashMap<>(); 34 | for (int i = 0; i < regionCells.length; i++) { 35 | result.put(regionCells[i], vSet); 36 | } 37 | return result; 38 | } 39 | 40 | @Override 41 | public Map getRedPotentials(Grid grid, int viewNum) { 42 | Map result = new HashMap<>(super.getRemovablePotentials()); 43 | return result; 44 | } 45 | 46 | public int getEliminationsTotal() { 47 | return eliminationsTotal; 48 | } 49 | 50 | public double getDifficulty() { 51 | return 2.9; 52 | } 53 | 54 | public String getGroup() { 55 | return "Chaining"; 56 | } 57 | 58 | public String getName() { 59 | return "Generalized Intersections"; 60 | } 61 | 62 | public String getShortName() { 63 | return "gI"; 64 | } 65 | 66 | @Override 67 | public Collection getLinks(Grid grid, int viewNum) { 68 | Collection result = new ArrayList(); 69 | return result; 70 | } 71 | 72 | @Override 73 | public Region[] getRegions() { 74 | return new Grid.Region[] {this.lockedRegion}; 75 | } 76 | 77 | public Cell[] getSelectedCells() { 78 | return this.regionCells; 79 | } 80 | 81 | @Override 82 | public int getViewCount() { 83 | return 1; 84 | } 85 | 86 | public Collection getRuleParents(Grid initialGrid, Grid currentGrid) { 87 | Collection result = new ArrayList(); 88 | BitSet myPositions = new BitSet(9); 89 | myPositions.or(lockedRegion.getPotentialPositions(currentGrid, Value)); 90 | for (int i = 0; i < 9; i++) { 91 | if (!myPositions.get(i)) { 92 | Cell cell = lockedRegion.getCell(i); 93 | if (initialGrid.hasCellPotentialValue(cell.getIndex(), Value)) 94 | result.add(new Potential(cell, Value, false)); 95 | } 96 | } 97 | return result; 98 | } 99 | 100 | @Override 101 | public boolean equals(Object o) { 102 | if (!(o instanceof VLockingHint)) 103 | return false; 104 | VLockingHint other = (VLockingHint)o; 105 | if (this.regionCells != other.regionCells) 106 | return false; 107 | return true; 108 | } 109 | 110 | @Override 111 | public int hashCode() { 112 | return regionCells[0].hashCode(); 113 | } 114 | 115 | public String getClueHtml(Grid grid, boolean isBig) { 116 | if (isBig) { 117 | return "Look for a " + getName() + 118 | " on the values " + Value + " in "+ lockedRegion.toFullString() + ""; 119 | } else { 120 | return "Look for a " + getName(); 121 | } 122 | } 123 | 124 | @Override 125 | public String toString() { 126 | return Cell.toFullString(regionCells) + 127 | " on value " + 128 | Value + " in " + lockedRegion.toFullString(); 129 | } 130 | 131 | @Override 132 | public String toHtml(Grid grid) { 133 | String result; 134 | result = HtmlLoader.loadHtml(this, "VLocking.html"); 135 | String region = lockedRegion.toFullString(); 136 | String ruleName = getName(); 137 | result = HtmlLoader.format(result, region, Value, ruleName); 138 | return result; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/VWXYZWing2Hint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

VWXYZ-Ring {12}{10}{11}

4 |

5 | The five cells {0}, {1}, {2}, {3} and 6 | {4} form a double-linked VWXYZ-Wing: The four 7 | cells {0}, {1}, {2} and {3} form an almost 8 | locked set linked to the bivalue cell {4} through both 9 | values {5} and {9} therefore creating a loop (ring). 10 | Regardless of the final value of cell {4}, the double-link would 11 | force the other 4 cells from the pattern to have different values to 12 | that one. The 5 different values will be in 5 cells therefore forming a 13 | locked set. 14 |

15 |

16 | Other occurrences of any of the values of the Ring 17 | pattern can therefore be removed from any cells sharing a row, 18 | column or block with all Ring cells containing that value. 19 |

20 |

21 | The largest cell of the Ring has {10} candidates and all 4 cells of 22 | the ALS have a total of {11} candidates. The pattern is similar to 23 | a 5digit-5cell Sue de Coq (SDC). 24 |

25 | 26 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/VWXYZWingHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

VWXYZ-Wing {12}{10}{11}

4 |

5 | The five cells {0}, {1}, {2}, {3} and 6 | {4} form a VWXYZ-Wing: The four cells {0}, 7 | {1}, {2} and {3} form an almost locked set 8 | linked to the bivalue cell {4} through the value {9}. 9 | If the value {5} was eliminated from all 4 cells of the 10 | Wing then the remaining value {9} in cell {4} would 11 | force the remaining 4 cells to have only 3 possible candidates to 12 | fill them. That is impossible! 13 |

14 |

15 | Other occurrences of the value {5} can therefore be removed 16 | from any cells sharing a {13} with all Wing cells 17 | containing the value {5}. 18 |

19 |

20 | The largest cell of the wing has {10} candidates and the 4 cells of 21 | the ALS have a total of {11} candidates. 22 |

23 | 24 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/WXYZWing2Hint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

WXYZ-Ring {11}{9}{10}

4 |

5 | The four cells {0}, {1}, {2} and {3} 6 | form a double-linked WXYZ-Wing: The three cells 7 | {0}, {1}, and {2} form an almost locked set 8 | linked to the bivalue cell {3} through both values 9 | {4} and {8} therefore creating a loop (ring). 10 | Regardless of the final value of cell {3}, the double-link 11 | would force the other 3 cells from the pattern to have different 12 | values to that one. The 4 different values will be in 4 cells 13 | therefore forming a locked set. 14 |

15 |

16 | Other occurrences of any of the values of the Ring 17 | pattern can therefore be removed from any cells sharing a row, 18 | column or block with all Ring cells containing that value. 19 |

20 |

21 | The largest cell of the Ring has {9} candidates and all 4 cells of 22 | the WXYZ-Ring pattern have a total of {10} candidates. The pattern 23 | is similar to a 4digit-4cell Sue de Coq (SDC). 24 |

25 | 26 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/WXYZWingHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

WXYZ-Wing {11}{9}{10}

4 |

5 | The four cells {0}, {1}, {2} and {3} 6 | form an WXYZ-Wing: The three cells {0}, {1}, 7 | and {2} form an almost locked set linked to the bivalue 8 | cell {3} through the value {8}. If the value {4} 9 | was eliminated from all 4 cells of the Wing then the remaining 10 | value {8} in cell {3} would force the remaining 3 11 | cells to have only 2 possible candidates to fill them. 12 | That is impossible! 13 |

14 |

15 | Other occurrences of the value {4} can therefore be removed 16 | from any cells sharing a {12} with all Wing cells 17 | containing the value {4}. 18 |

19 |

20 | The largest cell of the wing has {9} candidates and all 4 cells of 21 | the WXYZ-Wing pattern have a total of {10} candidates. 22 |

23 | 24 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/XYWingHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

XY-Wing

4 |

5 | The three cells {0}, {1} and {2} form an XY-Wing 6 | pattern on the value {3}. 7 | Regardless of the correct value of the cell {0} ({4} or 8 | {5}), one of the two cells {1} or {2} will 9 | have the value {3} as its only remaining possible value. 10 | It follows that either the cell {1} or the cell {2} must 11 | contain the value {3}. 12 |

13 |

14 | Other occurrences of the value {3} can therefore be removed from any 15 | cells sharing a {6} with both {1} and {2}. 16 |

17 | 18 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/XYZWingHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

XYZ-Wing

4 |

5 | The three cells {0}, {1} and {2} form an 6 | XYZ-Wing pattern on the value {3}. 7 | Regardless of the correct value of the cell {0} ({3}, 8 | {4} or {5}), either the cell 9 | {0} contains the value {3}, or one of the two 10 | cells {1} or {2} will have the value {3} as 11 | its only remaining possible value. 12 | It follows that one of the three cells {0}, {1} or 13 | {2} must contain the value {3}. 14 |

15 |

16 | Other occurrences of the value {3} can therefore be removed from any 17 | cells sharing a {6} with all the three cells. 18 |

19 | 20 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/DynamicCellReductionHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Dynamic Cell Forcing Chains

4 |

5 | With this solving technique, we will prove the following assertions: 6 |

    7 | {0} 8 |
9 | Because the cell {1} cannot contain other values, and the results are the same, 10 | we can conclude that {2}. 11 |

12 | Each assertion is proved by a different chain of simple rules. The chains can be dynamic, 13 | which means that the conclusions of multiple sub-chains must be combined in some cases. 14 |

15 | The details of each chain are given below. Use the view selector below the grid 16 | to switch between the graphical illustrations of the different chains. 17 |

18 | {3} 19 |

20 | 21 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/DynamicContradictionHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Dynamic Contradiction Forcing Chains

4 |

5 | With this solving technique, we will prove the two following assertions: 6 |

    7 |
  • If {0}, then {1} 8 |
  • If {0}, then {2} 9 |
10 | Because the same assumption yields to contradictory results, we can conclude that the 11 | assumption is false, that is, {3}. 12 |

13 | Each assertion is proved by a different chain of simple rules. The chains can be dynamic, 14 | which means that the conclusions of multiple sub-chains must be combined in some cases. 15 |

16 | The details of each chain are given below. Use the view selector below the grid 17 | to switch between the graphical illustrations of the two different chains. 18 |

19 | Chain 1: If {0}, then {2} (View 1):
20 | {4} 21 |

22 | Chain 2: If {1}, then {2} (View 2):
23 | {5} 24 |

25 | 26 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/DynamicReductionHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Dynamic Double Forcing Chain

4 |

5 | With this solving technique, we will prove the two following assertions: 6 |

    7 |
  • If {0}, then {2} 8 |
  • If {1}, then {2} 9 |
10 | Because the two assumptions are complementary, and the results are the same, we can 11 | conclude that {2}. 12 |

13 | Each assertion is proved by a different chain of simple rules. The chains can be dynamic, 14 | which means that the conclusions of multiple sub-chains must be combined in some cases. 15 |

16 | The details of each chain are given below. Use the view selector below the grid 17 | to switch between the graphical illustrations of the two different chains. 18 |

19 | Chain 1: If {0}, then {2} (View 1):
20 | {3} 21 |

22 | Chain 2: If {1}, then {2} (View 2):
23 | {4} 24 |

25 | 26 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/DynamicRegionReductionHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Dynamic Region Forcing Chains

4 |

5 | With this solving technique, we will prove the following assertions: 6 |

    7 | {0} 8 |
9 | Because the value {1} cannot be placed elsewhere in the {2}, 10 | and the results are the same, we can conclude that {3}. 11 |

12 | Each assertion is proved by a different chain of simple rules. The chains can be dynamic, 13 | which means that the conclusions of multiple sub-chains must be combined in some cases. 14 |

15 | The details of each chain are given below. Use the view selector below the grid 16 | to switch between the graphical illustrations of the different chains. 17 |

18 | {4} 19 |

20 | 21 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/ForcingChain.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Forcing Chain

4 |

5 | If we assume that {0}, it follows, through a Forcing Chain, that {1}. 6 | Therefore we can conclude that {2}. 7 |

8 | The Forcing Chain consists of a chain of implications based on simple rules. 9 | The details of the Forcing Chain are given below. 10 |

11 | Details of the Forcing Chain:
12 | {3} 13 |

14 | 15 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/ForcingXChain.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Forcing X-Chain {4}

4 |

5 | If we assume that {0}, it follows, through a Forcing X-Chain, that {1}. 6 | Therefore we can conclude that {2}. 7 |

8 | The forcing chain consists of a chain of implications based on simple rules. 9 | The chain is an X-Chain, because it only involves a single value. 10 | The details of the Forcing Chain are given below. 11 |

12 | Details of the Forcing Chain:
13 | {3} 14 |

15 | 16 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/FullChain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules.chaining; 7 | 8 | import java.util.*; 9 | 10 | /** 11 | * Wraps a single chaining hint, and change the 12 | * behavior of equals() and hashCode 13 | * so that they consider the entire chains instead of 14 | * just the outcome. 15 | */ 16 | public class FullChain { 17 | 18 | private final ChainingHint chain; 19 | 20 | 21 | public FullChain(ChainingHint target) { 22 | this.chain = target; 23 | } 24 | 25 | //public ChainingHint get() { 26 | // return this.chain; 27 | //} 28 | 29 | @Override 30 | public boolean equals(Object o) { 31 | // Maybe we could just compare chain.toString()!? 32 | if (o instanceof FullChain) { 33 | FullChain other = (FullChain)o; 34 | /* 35 | * Some returned collections may not be lists and may not implement equals correctly. 36 | * Wrap the content in an array list. 37 | */ 38 | Collection thisTargets = new ArrayList(this.chain.getChainsTargets()); 39 | Collection otherTargets = new ArrayList(other.chain.getChainsTargets()); 40 | if (!thisTargets.equals(otherTargets)) 41 | return false; 42 | Iterator i1 = thisTargets.iterator(); 43 | Iterator i2 = otherTargets.iterator(); 44 | while (i1.hasNext() && i2.hasNext()) { 45 | Potential p1 = i1.next(); 46 | Potential p2 = i2.next(); 47 | if (!this.chain.getChain(p1).equals(other.chain.getChain(p2))) 48 | return false; 49 | } 50 | return true; 51 | } 52 | return false; 53 | } 54 | 55 | @Override 56 | public int hashCode() { 57 | int result = 0; 58 | for (Potential target : this.chain.getChainsTargets()) { 59 | for (Potential p : this.chain.getChain(target)) 60 | result ^= p.hashCode(); 61 | } 62 | return result; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/NishioHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Nishio Forcing Chains

4 |

5 | With this solving technique, we will prove the two following assertions: 6 |

    7 |
  • If {0}, then {1} 8 |
  • If {0}, then {2} 9 |
10 | Because the same assumption yields to contradictory results, we can conclude that the 11 | assumption is false, that is, {3}. 12 |

13 | Each assertion is proved by a different chain of simple rules. The chains are Nishio 14 | chains: they only involve a single value (like Forcing X-Chains), but the conclusions of 15 | multiple sub-chains must be combined in some cases. 16 |

17 | The details of each chain are given below. Use the view selector below the grid 18 | to switch between the graphical illustrations of the two different chains. 19 |

20 | Chain 1: If {0}, then {2} (View 1):
21 | {4} 22 |

23 | Chain 2: If {1}, then {2} (View 2):
24 | {5} 25 |

26 | 27 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/StaticCellReductionHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Cell Forcing Chains

4 |

5 | With this solving technique, we will prove the following assertions: 6 |

    7 | {0} 8 |
9 | Because the cell {1} cannot contain other values, and the results are the same, 10 | we can conclude that {2}. 11 |

12 | Each assertion is proved by a different chain of simple rules. The chains are 13 | static, which means that each rule of the chain can be understood without having to 14 | remember the results of previous rules. 15 |

16 | The details of each chain are given below. Use the view selector below the grid 17 | to switch between the graphical illustrations of the different chains. 18 |

19 | {3} 20 |

21 | 22 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/StaticRegionReductionHint.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Region Forcing Chains

4 |

5 | With this solving technique, we will prove the following assertions: 6 |

    7 | {0} 8 |
9 | Because the value {1} cannot be placed elsewhere in the {2}, 10 | and the results are the same, we can conclude that {3}. 11 |

12 | Each assertion is proved by a different chain of simple rules. The chains are 13 | static, which means that each rule of the chain can be understood without having to 14 | remember the results of previous rules. 15 |

16 | The details of each chain are given below. Use the view selector below the grid 17 | to switch between the graphical illustrations of the different chains. 18 |

19 | {4} 20 |

21 | 22 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/UnderConstruction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sorry, the explanation of this rule is still under construction. 4 | 5 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/X-Cycle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Bidirectional X-Cycle {2}

4 |

5 | The cells {0} form a bidirectional cycle: there are exactly two ways 6 | of placing the value {1} in these cells, forming two different possible 7 | configurations. Some {1} appear in a {5} regardless of the 8 | chosen configuration. Because one of the two configurations must be correct, other 9 | occurrences of the value {1} can be removed from these rows, columns or blocks. 10 |

11 |

12 | The cycle is an X-Cycle because only a single value is involved. Bidirectional X-Cycles 13 | are also known as Fishy Cycles or Bilocation cycles. 14 |

15 |

16 | The two configurations of the bidirectional cycle are enforced by two complementary, 17 | circular chains of implications. The details of each chain are given below. 18 |

19 |

20 | Cycle details, forward direction (View 1):
21 | {3} 22 |

23 | Cycle details, reverse direction (View 2):
24 | {4} 25 |

26 | 27 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/XY-Cycle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Bidirectional Cycle

4 |

5 | The cells {0} form a bidirectional cycle: there are exactly two ways 6 | of placing the values in these cells, forming two different possible 7 | configurations. Some values appear in a {5} regardless of the 8 | chosen configuration. Because one of the two configurations must be correct, other 9 | occurrences of these values can be removed from these rows, columns or blocks. 10 |

11 |

12 | The two configurations of the bidirectional cycle are enforced by two complementary, 13 | circular chains of implications. The details of each chain are given below. 14 |

15 |

16 | Cycle details, forward direction (View 1):
17 | {3} 18 |

19 | Cycle details, reverse direction (View 2):
20 | {4} 21 |

22 | 23 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/chaining/Y-Cycle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Bidirectional Y-Cycle

4 |

5 | The cells {0} form a bidirectional cycle: there are exactly two ways 6 | of placing the values in these cells, forming two different possible 7 | configurations. Some values appear in a {5} regardless of the 8 | chosen configuration. Because one of the two configurations must be correct, other 9 | occurrences of these values can be removed from these rows, columns or blocks. 10 |

11 |

12 | The cycle is an Y-Cycle because only cells with two potential values are involved. 13 | Bidirectional Y-Cycles are also known as Forcing Loops or Bivalue cycles. 14 |

15 |

16 | The two configurations of the bidirectional cycle are enforced by two complementary, 17 | circular chains of implications. The details of each chain are given below. 18 |

19 |

20 | Cycle details, forward direction (View 1):
21 | {3} 22 |

23 | Cycle details, reverse direction (View 2):
24 | {4} 25 |

26 | 27 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/forcingCellFNCHint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.Grid.*; 12 | import diuf.sudoku.solver.*; 13 | import diuf.sudoku.solver.rules.chaining.*; 14 | import diuf.sudoku.tools.*; 15 | 16 | 17 | /** 18 | * forcingCellFNC hints 19 | */ 20 | public class forcingCellFNCHint extends IndirectHint implements Rule, HasParentPotentialHint { 21 | 22 | private final Cell forcingCell; 23 | private final int[] Values; 24 | 25 | public forcingCellFNCHint(forcingCellFNC rule, Map removablePotentials, Cell forcingCell, int[] Values) { 26 | super(rule, removablePotentials); 27 | this.forcingCell = forcingCell; 28 | this.Values = Values; 29 | } 30 | 31 | @Override 32 | public Map getGreenPotentials(Grid grid, int viewNum) { 33 | Map result = new HashMap<>(); 34 | return result; 35 | } 36 | 37 | @Override 38 | public Map getRedPotentials(Grid grid, int viewNum) { 39 | Map result = new HashMap<>(super.getRemovablePotentials()); 40 | return result; 41 | } 42 | 43 | public double getDifficulty() { 44 | return 2.4; 45 | } 46 | 47 | public String getGroup() { 48 | return "NC_Techniques"; 49 | } 50 | 51 | public String getName() { 52 | return "Non-Consecutive Forcing Cell"; 53 | } 54 | 55 | public String getShortName() { 56 | return "kNC"; 57 | } 58 | 59 | @Override 60 | public Collection getLinks(Grid grid, int viewNum) { 61 | Collection result = new ArrayList(); 62 | return result; 63 | } 64 | 65 | @Override 66 | public Region[] getRegions() { 67 | return null; 68 | } 69 | 70 | public Cell[] getSelectedCells() { 71 | Cell[] result = {this.forcingCell}; 72 | return result; 73 | } 74 | 75 | @Override 76 | public int getViewCount() { 77 | return 1; 78 | } 79 | 80 | public Collection getRuleParents(Grid initialGrid, Grid currentGrid) { 81 | Collection result = new ArrayList(); 82 | //BitSet myPositions = new BitSet(9); 83 | return result; 84 | } 85 | 86 | @Override 87 | public boolean equals(Object o) { 88 | if (!(o instanceof forcingCellFNCHint)) 89 | return false; 90 | forcingCellFNCHint other = (forcingCellFNCHint)o; 91 | if (this.forcingCell != other.forcingCell) 92 | return false; 93 | return true; 94 | } 95 | 96 | @Override 97 | public int hashCode() { 98 | return forcingCell.hashCode(); 99 | } 100 | 101 | public String getClueHtml(Grid grid, boolean isBig) { 102 | if (isBig) { 103 | return "Look for a " + getName() + 104 | " eliminations by observing the values in "+ forcingCell.toFullString() + ""; 105 | } else { 106 | return "Look for a " + getName(); 107 | } 108 | } 109 | 110 | @Override 111 | public String toString() { 112 | StringBuilder builder = new StringBuilder(); 113 | builder.append(Cell.toFullString(forcingCell)); 114 | builder.append(" on value(s) "); 115 | for (int i = 0; i < Values.length; i++) { 116 | if (i > 0) 117 | builder.append(","); 118 | builder.append(Integer.toString(Values[i])); 119 | } 120 | return builder.toString(); 121 | } 122 | 123 | @Override 124 | public String toHtml(Grid grid) { 125 | String result = HtmlLoader.loadHtml(this, "forcingCellNC.html"); 126 | String valueList = HtmlLoader.formatValues(Values); 127 | String cellName = forcingCell.toString(); 128 | String ruleName = getName(); 129 | return HtmlLoader.format(result, cellName, valueList, 130 | ruleName); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/forcingCellNC.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{2}

4 |

5 | The arrangement of the values in {0} will force the elimination 6 | of {1} from all adjacent cells of {0}. 7 |

8 | 9 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/forcingCellNCHint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.Grid.*; 12 | import diuf.sudoku.solver.*; 13 | import diuf.sudoku.solver.rules.chaining.*; 14 | import diuf.sudoku.tools.*; 15 | 16 | 17 | /** 18 | * forcingCellNC hints 19 | */ 20 | public class forcingCellNCHint extends IndirectHint implements Rule, HasParentPotentialHint { 21 | 22 | private final Cell forcingCell; 23 | private final int[] Values; 24 | 25 | public forcingCellNCHint(forcingCellNC rule, Map removablePotentials, Cell forcingCell, int[] Values) { 26 | super(rule, removablePotentials); 27 | this.forcingCell = forcingCell; 28 | this.Values = Values; 29 | } 30 | 31 | @Override 32 | public Map getGreenPotentials(Grid grid, int viewNum) { 33 | Map result = new HashMap<>(); 34 | return result; 35 | } 36 | 37 | @Override 38 | public Map getRedPotentials(Grid grid, int viewNum) { 39 | Map result = new HashMap<>(super.getRemovablePotentials()); 40 | return result; 41 | } 42 | 43 | public double getDifficulty() { 44 | return 2.4; 45 | } 46 | 47 | public String getGroup() { 48 | return "NC_Techniques"; 49 | } 50 | 51 | public String getName() { 52 | return "Non-Consecutive Forcing Cell"; 53 | } 54 | 55 | public String getShortName() { 56 | return "kNC"; 57 | } 58 | 59 | @Override 60 | public Collection getLinks(Grid grid, int viewNum) { 61 | Collection result = new ArrayList(); 62 | return result; 63 | } 64 | 65 | @Override 66 | public Region[] getRegions() { 67 | return null; 68 | } 69 | 70 | public Cell[] getSelectedCells() { 71 | Cell[] result = {this.forcingCell}; 72 | return result; 73 | } 74 | 75 | @Override 76 | public int getViewCount() { 77 | return 1; 78 | } 79 | 80 | public Collection getRuleParents(Grid initialGrid, Grid currentGrid) { 81 | Collection result = new ArrayList(); 82 | //BitSet myPositions = new BitSet(9); 83 | return result; 84 | } 85 | 86 | @Override 87 | public boolean equals(Object o) { 88 | if (!(o instanceof forcingCellNCHint)) 89 | return false; 90 | forcingCellNCHint other = (forcingCellNCHint)o; 91 | if (this.forcingCell != other.forcingCell) 92 | return false; 93 | return true; 94 | } 95 | 96 | @Override 97 | public int hashCode() { 98 | return forcingCell.hashCode(); 99 | } 100 | 101 | public String getClueHtml(Grid grid, boolean isBig) { 102 | if (isBig) { 103 | return "Look for a " + getName() + 104 | " eliminations by observing the values in "+ forcingCell.toFullString() + ""; 105 | } else { 106 | return "Look for a " + getName(); 107 | } 108 | } 109 | 110 | @Override 111 | public String toString() { 112 | StringBuilder builder = new StringBuilder(); 113 | builder.append(Cell.toFullString(forcingCell)); 114 | builder.append(" on value(s) "); 115 | for (int i = 0; i < Values.length; i++) { 116 | if (i > 0) 117 | builder.append(","); 118 | builder.append(Integer.toString(Values[i])); 119 | } 120 | return builder.toString(); 121 | } 122 | 123 | @Override 124 | public String toHtml(Grid grid) { 125 | String result = HtmlLoader.loadHtml(this, "forcingCellNC.html"); 126 | String valueList = HtmlLoader.formatValues(Values); 127 | String cellName = forcingCell.toString(); 128 | String ruleName = getName(); 129 | return HtmlLoader.format(result, cellName, valueList, 130 | ruleName); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/lockedFNC.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.solver.*; 12 | import diuf.sudoku.tools.*; 13 | 14 | 15 | /** 16 | * Implementation of the "Ferz NC Forcing Cell" solving technique by Tarek Maani. 17 | * This covers double consecutive claiming, double Middle claiming and triple consecutive claiming 18 | * http://jcbonsai.free.fr/sudoku/JSudokuUserGuide/relationalTechniques.html 19 | * http://forum.enjoysudoku.com/sudokuncexplainer-to-solve-and-rate-sudoku-non-consecutive-t36949.html#p285476 20 | */ 21 | public class lockedFNC implements IndirectHintProducer { 22 | 23 | public void getHints(Grid grid, HintsAccumulator accu) throws InterruptedException { 24 | boolean isNCNonToroidal = Settings.getInstance().whichNC() == 3; 25 | for (int value = 1; value <= 9; value++) { 26 | for (int regionTypeIndex = 4; regionTypeIndex >= (Settings.getInstance().isBlocks() ? 0 : 1); regionTypeIndex--) { 27 | //DG doesn't have cells in proximity 28 | if (regionTypeIndex == 3) continue; 29 | if (regionTypeIndex == 4 && !Settings.getInstance().isWindows()) continue; 30 | int regionsNumber = Grid.getRegions(regionTypeIndex).length; 31 | if (regionTypeIndex == 4) { 32 | regionsNumber = Math.min(regionsNumber, 4); 33 | } 34 | // Boxes and windows can have up to 5 diagonally-adjacent cells (cross sign). 35 | int maxCardinality = (regionTypeIndex == 0 || regionTypeIndex == 4) ? 5 : 3; 36 | for (int regionIndex = 0; regionIndex < regionsNumber; regionIndex++) { 37 | Grid.Region region = Grid.getRegions(regionTypeIndex)[regionIndex]; 38 | BitSet ncValues = region.getPotentialPositions(grid, value); 39 | int ncValuesCard = ncValues.cardinality(); 40 | if (ncValuesCard <= maxCardinality) { 41 | Cell[] cells = new Cell[ncValuesCard]; 42 | int cellIndex = 0; 43 | for (int ncIndex = 0; ncIndex < 9; ncIndex++) { 44 | if (ncValues.get(ncIndex)) 45 | cells[cellIndex++] = region.getCell(ncIndex); 46 | } 47 | int value1 = isNCNonToroidal ? value - 1 : value > 1 ? value - 1 : 9; 48 | int value2 = isNCNonToroidal ? (value + 1) % 10 : value < 9 ? value + 1 : 1; 49 | lockedFNCHint hint = createHint(grid, cells, value1, value2, region, value); 50 | if (hint != null && hint.isWorth()) 51 | accu.add(hint); 52 | } 53 | } // for (int i = 0; i < regionsNumber; i++) 54 | } // for (int regionTypeIndex = 2; regionTypeIndex >=0; regionTypeIndex--) 55 | }// for (int value = 1; value <= 9; value++) 56 | } 57 | 58 | // Create a hint for NC locked pairs 59 | private lockedFNCHint createHint(Grid grid, Cell[] cells, int value1, int value2, Grid.Region region, int value) { 60 | Map removablePotentials = null; 61 | if (cells.length > 0) { 62 | int[][] cellsLookup = Settings.getInstance().isToroidal() ? Grid.ferzCellsToroidal : Grid.ferzCellsRegular; 63 | CellSet victims = new CellSet(cellsLookup[cells[0].getIndex()]); 64 | victims.add(cells[0]); 65 | for (int i = 1; i < cells.length; i++) { 66 | CellSet curVictims = new CellSet(cellsLookup[cells[i].getIndex()]); 67 | curVictims.add(cells[i]); 68 | victims.retainAll(curVictims); 69 | if (victims.isEmpty()) 70 | return null; 71 | } 72 | 73 | removablePotentials = new HashMap(); 74 | for (Cell cell : victims) { 75 | if (value1 != 0 && grid.hasCellPotentialValue(cell.getIndex(), value1)) 76 | removablePotentials.put(cell, SingletonBitSet.create(value1)); 77 | if (value2 != 0 && grid.hasCellPotentialValue(cell.getIndex(), value2)) 78 | if (removablePotentials.containsKey(cell)) 79 | removablePotentials.get(cell).set(value2); 80 | else 81 | removablePotentials.put(cell, SingletonBitSet.create(value2)); 82 | } 83 | } 84 | if (removablePotentials == null || removablePotentials.isEmpty()) 85 | return null; 86 | 87 | int[] finalValues; 88 | if (value1 == 0) 89 | finalValues = new int[] {value2}; 90 | else if (value2 == 0) 91 | finalValues = new int[] {value1}; 92 | else 93 | finalValues = new int[] {value1, value2}; 94 | return new lockedFNCHint(this, removablePotentials, cells, finalValues, region, value); 95 | } 96 | 97 | 98 | @Override 99 | public String toString() { 100 | return "Locked NC"; 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/lockedFNCHint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.Grid.*; 12 | import diuf.sudoku.solver.*; 13 | import diuf.sudoku.solver.rules.chaining.*; 14 | import diuf.sudoku.tools.*; 15 | 16 | 17 | /** 18 | * lockedFNC hints 19 | */ 20 | public class lockedFNCHint extends IndirectHint implements Rule, HasParentPotentialHint { 21 | 22 | private final Cell[] lockedCells; 23 | private final int[] Values; 24 | private final Region region; 25 | private final int value; 26 | 27 | public lockedFNCHint(lockedFNC rule, Map removablePotentials, Cell[] lockedCells, int[] Values, Region region, int value) { 28 | super(rule, removablePotentials); 29 | this.lockedCells = lockedCells; 30 | this.Values = Values; 31 | this.region = region; 32 | this.value = value; 33 | } 34 | 35 | @Override 36 | public Map getGreenPotentials(Grid grid, int viewNum) { 37 | Map result = new HashMap<>(); 38 | BitSet zSet = SingletonBitSet.create(this.value); 39 | for (Cell cell : lockedCells) { 40 | if (grid.hasCellPotentialValue(cell.getIndex(), this.value)) 41 | result.put(cell, zSet); 42 | } 43 | return result; 44 | } 45 | 46 | @Override 47 | public Map getRedPotentials(Grid grid, int viewNum) { 48 | Map result = new HashMap<>(super.getRemovablePotentials()); 49 | return result; 50 | } 51 | 52 | public double getDifficulty() { 53 | return 2.5; 54 | } 55 | 56 | public String getGroup() { 57 | return "NC_Techniques"; 58 | } 59 | 60 | public String getName() { 61 | return "Locked Non Consecutive"; 62 | } 63 | 64 | public String getShortName() { 65 | return "lNC"; 66 | } 67 | 68 | @Override 69 | public Collection getLinks(Grid grid, int viewNum) { 70 | Collection result = new ArrayList(); 71 | return result; 72 | } 73 | 74 | @Override 75 | public Region[] getRegions() { 76 | return new Grid.Region[] {this.region}; 77 | } 78 | 79 | public Cell[] getSelectedCells() { 80 | Cell[] result = this.lockedCells; 81 | return result; 82 | } 83 | 84 | @Override 85 | public int getViewCount() { 86 | return 1; 87 | } 88 | 89 | public Collection getRuleParents(Grid initialGrid, Grid currentGrid) { 90 | Collection result = new ArrayList(); 91 | //BitSet myPositions = new BitSet(9); 92 | return result; 93 | } 94 | 95 | @Override 96 | public boolean equals(Object o) { 97 | if (!(o instanceof lockedFNCHint)) 98 | return false; 99 | lockedFNCHint other = (lockedFNCHint)o; 100 | if (this.lockedCells != other.lockedCells) 101 | return false; 102 | return true; 103 | } 104 | 105 | @Override 106 | public int hashCode() { 107 | return lockedCells.hashCode(); 108 | } 109 | 110 | public String getClueHtml(Grid grid, boolean isBig) { 111 | if (isBig) { 112 | return "Look for a " + getName() + 113 | " eliminations by observing the values " + this.value + " in "+ region.toFullString() + ""; 114 | } else { 115 | return "Look for a " + getName(); 116 | } 117 | } 118 | 119 | @Override 120 | public String toString() { 121 | StringBuilder builder = new StringBuilder(); 122 | builder.append(this.value + ": "); 123 | builder.append(Cell.toFullString(lockedCells)); 124 | builder.append(" on value(s) "); 125 | for (int i = 0; i < Values.length; i++) { 126 | if (i > 0) 127 | builder.append(","); 128 | builder.append(Integer.toString(Values[i])); 129 | } 130 | return builder.toString(); 131 | } 132 | 133 | @Override 134 | public String toHtml(Grid grid) { 135 | String result = HtmlLoader.loadHtml(this, "lockedNC.html"); 136 | String valueList = HtmlLoader.formatValues(Values); 137 | String cellList = HtmlLoader.formatList(lockedCells); 138 | String mainValue = "" + this.value; 139 | String regionName = region.toFullString();; 140 | String ruleName = getName(); 141 | return HtmlLoader.format(result, cellList, valueList, 142 | ruleName, mainValue, regionName); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/lockedNC.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

{2}

4 |

5 | In {4} (outlined), all the cells with value {3} (highlighted) are close 6 | enough to each other in a way that forces the elimination of value(s) {1} 7 | (marked in red if present). 8 |

9 |

10 | These values can be safely eliminated through non consecutive rules, otherwise 11 | (if any were true), the value {3} would be completely removed from {4} 12 | which can't happen in a valid puzzle. 13 |

14 | 15 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/lockedNC.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.solver.*; 12 | import diuf.sudoku.tools.*; 13 | 14 | 15 | /** 16 | * Implementation of the "NC Forcing Cell" solving technique by Tarek Maani. 17 | * This covers double consecutive claiming, double Middle claiming and triple consecutive claiming 18 | * http://jcbonsai.free.fr/sudoku/JSudokuUserGuide/relationalTechniques.html 19 | * http://forum.enjoysudoku.com/sudokuncexplainer-to-solve-and-rate-sudoku-non-consecutive-t36949.html#p285476 20 | */ 21 | public class lockedNC implements IndirectHintProducer { 22 | 23 | public void getHints(Grid grid, HintsAccumulator accu) throws InterruptedException { 24 | boolean isNCNonToroidal = Settings.getInstance().whichNC() == 1; 25 | for (int value = 1; value <= 9; value++) { 26 | for (int regionTypeIndex = 4; regionTypeIndex >= (Settings.getInstance().isBlocks() ? 0 : 1); regionTypeIndex--) { 27 | //DG doesn't have cells in proximity 28 | if (regionTypeIndex == 3) continue; 29 | if (regionTypeIndex == 4 && !Settings.getInstance().isWindows()) continue; 30 | int regionsNumber = Grid.getRegions(regionTypeIndex).length; 31 | if (regionTypeIndex == 4) { 32 | regionsNumber = Math.min(regionsNumber, 4); 33 | } 34 | // Boxes and windows can have up to 5 adjacent cells (plus sign). 35 | int maxCardinality = (regionTypeIndex == 0 || regionTypeIndex == 4) ? 5 : 3; 36 | for (int regionIndex = 0; regionIndex < regionsNumber; regionIndex++) { 37 | Grid.Region region = Grid.getRegions(regionTypeIndex)[regionIndex]; 38 | BitSet ncValues = region.getPotentialPositions(grid, value); 39 | int ncValuesCard = ncValues.cardinality(); 40 | if (ncValuesCard <= maxCardinality) { 41 | Cell[] cells = new Cell[ncValuesCard]; 42 | int cellIndex = 0; 43 | for (int ncIndex = 0; ncIndex < 9; ncIndex++) { 44 | if (ncValues.get(ncIndex)) 45 | cells[cellIndex++] = region.getCell(ncIndex); 46 | } 47 | int value1 = isNCNonToroidal ? value - 1 : value > 1 ? value - 1 : 9; 48 | int value2 = isNCNonToroidal ? (value + 1) % 10 : value < 9 ? value + 1 : 1; 49 | lockedNCHint hint = createHint(grid, cells, value1, value2, region, value); 50 | if (hint != null && hint.isWorth()) 51 | accu.add(hint); 52 | } 53 | } // for (int i = 0; i < regionsNumber; i++) 54 | } // for (int regionTypeIndex = 2; regionTypeIndex >=0; regionTypeIndex--) 55 | }// for (int value = 1; value <= 9; value++) 56 | } 57 | 58 | // Create a hint for NC locked pairs 59 | private lockedNCHint createHint(Grid grid, Cell[] cells, int value1, int value2, Grid.Region region, int value) { 60 | Map removablePotentials = null; 61 | if (cells.length > 0) { 62 | int[][] cellsLookup = Settings.getInstance().isToroidal() ? Grid.wazirCellsToroidal : Grid.wazirCellsRegular; 63 | CellSet victims = new CellSet(cellsLookup[cells[0].getIndex()]); 64 | victims.add(cells[0]); 65 | for (int i = 1; i < cells.length; i++) { 66 | CellSet curVictims = new CellSet(cellsLookup[cells[i].getIndex()]); 67 | curVictims.add(cells[i]); 68 | victims.retainAll(curVictims); 69 | if (victims.isEmpty()) 70 | return null; 71 | } 72 | 73 | removablePotentials = new HashMap(); 74 | for (Cell cell : victims) { 75 | if (value1 != 0 && grid.hasCellPotentialValue(cell.getIndex(), value1)) 76 | removablePotentials.put(cell, SingletonBitSet.create(value1)); 77 | if (value2 != 0 && grid.hasCellPotentialValue(cell.getIndex(), value2)) 78 | if (removablePotentials.containsKey(cell)) 79 | removablePotentials.get(cell).set(value2); 80 | else 81 | removablePotentials.put(cell, SingletonBitSet.create(value2)); 82 | } 83 | } 84 | if (removablePotentials == null || removablePotentials.isEmpty()) 85 | return null; 86 | 87 | int[] finalValues; 88 | if (value1 == 0) 89 | finalValues = new int[] {value2}; 90 | else if (value2 == 0) 91 | finalValues = new int[] {value1}; 92 | else 93 | finalValues = new int[] {value1, value2}; 94 | return new lockedNCHint(this, removablePotentials, cells, finalValues, region, value); 95 | } 96 | 97 | @Override 98 | public String toString() { 99 | return "Locked NC"; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/lockedNCHint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.Grid.*; 12 | import diuf.sudoku.solver.*; 13 | import diuf.sudoku.solver.rules.chaining.*; 14 | import diuf.sudoku.tools.*; 15 | 16 | 17 | /** 18 | * lockedNC hints 19 | */ 20 | public class lockedNCHint extends IndirectHint implements Rule, HasParentPotentialHint { 21 | 22 | private final Cell[] lockedCells; 23 | private final int[] Values; 24 | private final Region region; 25 | private final int value; 26 | 27 | public lockedNCHint(lockedNC rule, Map removablePotentials, Cell[] lockedCells, int[] Values, Region region, int value) { 28 | super(rule, removablePotentials); 29 | this.lockedCells = lockedCells; 30 | this.Values = Values; 31 | this.region = region; 32 | this.value = value; 33 | } 34 | 35 | @Override 36 | public Map getGreenPotentials(Grid grid, int viewNum) { 37 | Map result = new HashMap<>(); 38 | BitSet zSet = SingletonBitSet.create(this.value); 39 | for (Cell cell : lockedCells) { 40 | if (grid.hasCellPotentialValue(cell.getIndex(), this.value)) 41 | result.put(cell, zSet); 42 | } 43 | return result; 44 | } 45 | 46 | @Override 47 | public Map getRedPotentials(Grid grid, int viewNum) { 48 | Map result = new HashMap<>(super.getRemovablePotentials()); 49 | return result; 50 | } 51 | 52 | public double getDifficulty() { 53 | return 2.5; 54 | } 55 | 56 | public String getGroup() { 57 | return "NC_Techniques"; 58 | } 59 | 60 | public String getName() { 61 | return "Locked Non Consecutive"; 62 | } 63 | 64 | public String getShortName() { 65 | return "lNC"; 66 | } 67 | 68 | @Override 69 | public Collection getLinks(Grid grid, int viewNum) { 70 | Collection result = new ArrayList(); 71 | return result; 72 | } 73 | 74 | @Override 75 | public Region[] getRegions() { 76 | return new Grid.Region[] {this.region}; 77 | } 78 | 79 | public Cell[] getSelectedCells() { 80 | Cell[] result = this.lockedCells; 81 | return result; 82 | } 83 | 84 | @Override 85 | public int getViewCount() { 86 | return 1; 87 | } 88 | 89 | public Collection getRuleParents(Grid initialGrid, Grid currentGrid) { 90 | Collection result = new ArrayList(); 91 | //BitSet myPositions = new BitSet(9); 92 | return result; 93 | } 94 | 95 | @Override 96 | public boolean equals(Object o) { 97 | if (!(o instanceof lockedNCHint)) 98 | return false; 99 | lockedNCHint other = (lockedNCHint)o; 100 | if (this.lockedCells != other.lockedCells) 101 | return false; 102 | return true; 103 | } 104 | 105 | @Override 106 | public int hashCode() { 107 | return lockedCells.hashCode(); 108 | } 109 | 110 | public String getClueHtml(Grid grid, boolean isBig) { 111 | if (isBig) { 112 | return "Look for a " + getName() + 113 | " eliminations by observing the values " + this.value + " in "+ region.toFullString() + ""; 114 | } else { 115 | return "Look for a " + getName(); 116 | } 117 | } 118 | 119 | @Override 120 | public String toString() { 121 | StringBuilder builder = new StringBuilder(); 122 | builder.append(this.value + ": "); 123 | builder.append(Cell.toFullString(lockedCells)); 124 | builder.append(" on value(s) "); 125 | for (int i = 0; i < Values.length; i++) { 126 | if (i > 0) 127 | builder.append(","); 128 | builder.append(Integer.toString(Values[i])); 129 | } 130 | return builder.toString(); 131 | } 132 | 133 | @Override 134 | public String toHtml(Grid grid) { 135 | String result = HtmlLoader.loadHtml(this, "lockedNC.html"); 136 | String valueList = HtmlLoader.formatValues(Values); 137 | String cellList = HtmlLoader.formatList(lockedCells); 138 | String mainValue = "" + this.value; 139 | String regionName = region.toFullString();; 140 | String ruleName = getName(); 141 | return HtmlLoader.format(result, cellList, valueList, 142 | ruleName, mainValue, regionName); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/BivalueUniversalGrave1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Bivalue Universal Grave type 1

4 |

5 | If the value {0} were removed from the cell {1}, the 6 | Sudoku would exhibit a Bivalue Universal Grave pattern (or BUG). 7 |

8 |

9 | In a BUG, each value that remains in a {4} has exactly two 10 | possible positions in that {4}; and each empty cell has exactly two 11 | possible values. A Sudoku having a BUG has zero, two or more valid 12 | solutions. 13 |

14 |

15 | Because a valid Sudoku has exactly one solution, the BUG cannot be part of it. 16 | The only way to avoid the BUG is if the cell {1} contains the value 17 | {2}. The values {3} can therefore be removed. 18 |

19 | 20 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/BivalueUniversalGrave2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Bivalue Universal Grave type 2

4 |

5 | If the value {0} were removed from the cells {1}, the 6 | Sudoku would exhibit a Bivalue Universal Grave pattern (or BUG). 7 |

8 |

9 | In a BUG, each value that remains in a {2} has exactly two 10 | possible positions in that {2}; and each empty cell has exactly two 11 | possible values. A Sudoku having a BUG has zero, two or more valid 12 | solutions. 13 |

14 |

15 | Because a valid Sudoku has exactly one solution, the BUG cannot be part of it. 16 | The only way to avoid the BUG is if at least one of the cells {1} 17 | contains the value {0}. Other potential positions of this 18 | value can therefore be removed from other cells sharing a {2} with 19 | {1}. 20 |

21 | 22 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/BivalueUniversalGrave3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Bivalue Universal Grave type 3 (with Naked {4})

4 |

5 | If the values {0} were removed from the cells {1}, the 6 | Sudoku would exhibit a Bivalue Universal Grave pattern (or BUG). 7 |

8 |

9 | In a BUG, each value that remains in a {8} has exactly two 10 | possible positions in that {8}; and each empty cell has exactly two 11 | possible values. A Sudoku having a BUG has zero, two or more valid 12 | solutions. 13 |

14 |

15 | Because a valid Sudoku has exactly one solution, the BUG cannot be part of it. 16 | The only way to avoid the BUG is if at least one of the cells {2} 17 | contains one of the values {3}. It follows that one of {2} 18 | forms a Naked {4} with {5} on the values {6} in the 19 | {7}. 20 |

21 | Other potential positions of the values {6} can therefore be removed 22 | from the {7}. 23 |

24 | 25 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/BivalueUniversalGrave4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Bivalue Universal Grave type 4

4 |

5 | If the values {0} were removed from the cells {1}, the 6 | Sudoku would exhibit a Bivalue Universal Grave pattern (or BUG). 7 |

8 |

9 | In a BUG, each value that remains in a {7} has exactly two 10 | possible positions in that {7}; and each empty cell has exactly two 11 | possible values. A Sudoku having a BUG has zero, two or more valid 12 | solutions. 13 |

14 |

15 | Because a valid Sudoku has exactly one solution, the BUG cannot be part of it. 16 | The only possible way to avoid the BUG is if one of the two cells {2} 17 | contains one of the values {3}. If follows that the other of these two 18 | cells will contain the value {4} because this value has no other 19 | possible position in the {5}. 20 |

21 | The values {6} can therefore be removed from the cells {1}. 22 |

23 | 24 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/Bug1Hint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules.unique; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.solver.*; 12 | import diuf.sudoku.tools.*; 13 | 14 | 15 | public class Bug1Hint extends BugHint implements Rule { 16 | 17 | protected final Cell bugCell; 18 | protected final BitSet bugValues; 19 | 20 | public Bug1Hint(IndirectHintProducer rule, Map removablePotentials, 21 | Cell bugCell, BitSet bugValues) { 22 | super(rule, removablePotentials); 23 | this.bugCell = bugCell; 24 | this.bugValues = bugValues; 25 | } 26 | 27 | @Override 28 | public Collection getLinks(Grid grid, int viewNum) { 29 | return null; 30 | } 31 | 32 | @Override 33 | public int getViewCount() { 34 | return 1; 35 | } 36 | 37 | @Override 38 | public Map getGreenPotentials(Grid grid, int viewNum) { 39 | Map result = new HashMap(); 40 | result.put(bugCell, bugValues); 41 | return result; 42 | } 43 | 44 | @Override 45 | public Map getRedPotentials(Grid grid, int viewNum) { 46 | return super.getRemovablePotentials(); 47 | } 48 | 49 | @Override 50 | public Cell[] getSelectedCells() { 51 | return new Cell[] {bugCell}; 52 | } 53 | 54 | @Override 55 | public Grid.Region[] getRegions() { 56 | return null; 57 | } 58 | 59 | public double getDifficulty() { 60 | return 5.6; 61 | } 62 | 63 | public String getName() { 64 | return "BUG type 1"; 65 | } 66 | 67 | public String getShortName() { 68 | return "BUG1"; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return "BUG type 1: " + bugCell.toString(); 74 | } 75 | 76 | private String sharedRegions(){ 77 | if (Settings.getInstance().isVanilla()) 78 | return "row, column or block"; 79 | else { 80 | String res[] = new String[10]; 81 | int i = 0; 82 | String finalRes = "row"; 83 | if (Settings.getInstance().isVLatin()) 84 | return "row or column"; 85 | else 86 | res[i++]= "column"; 87 | if (Settings.getInstance().isBlocks()) 88 | res[i++]= "block"; 89 | if (Settings.getInstance().isDG()) 90 | res[i++]= "disjoint group"; 91 | if (Settings.getInstance().isWindows()) 92 | res[i++]= "window group"; 93 | if (Settings.getInstance().isX()) 94 | res[i++]= "diagonal"; 95 | if (Settings.getInstance().isGirandola()) 96 | res[i++]= "girandola group"; 97 | if (Settings.getInstance().isAsterisk()) 98 | res[i++]= "asterisk group"; 99 | if (Settings.getInstance().isCD()) 100 | res[i++]= "center dot group"; 101 | i--; 102 | for (int j = 0; j < i; j++) 103 | finalRes += ", " + res[j]; 104 | finalRes += " or " + res[i]; 105 | return finalRes; 106 | } 107 | } 108 | 109 | @Override 110 | public String toHtml(Grid grid) { 111 | String result = HtmlLoader.loadHtml(this, "BivalueUniversalGrave1.html"); 112 | String andExtra = ValuesFormatter.formatValues(bugValues, " and "); 113 | String orExtra = ValuesFormatter.formatValues(bugValues, " or "); 114 | BitSet removable = super.getRemovablePotentials().get(bugCell); 115 | String remList = ValuesFormatter.formatValues(removable, " and "); 116 | result = HtmlLoader.format(result, andExtra, bugCell, orExtra, remList, sharedRegions()); 117 | return result; 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/Bug2Hint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules.unique; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.Grid.*; 12 | import diuf.sudoku.solver.*; 13 | import diuf.sudoku.tools.*; 14 | 15 | 16 | public class Bug2Hint extends BugHint implements Rule { 17 | 18 | private final Cell[] bugCells; 19 | private final int bugValue; 20 | 21 | 22 | public Bug2Hint(IndirectHintProducer rule, Map removablePotentials, 23 | Cell[] bugCells, int bugValue) { 24 | super(rule, removablePotentials); 25 | this.bugCells = bugCells; 26 | this.bugValue = bugValue; 27 | } 28 | 29 | @Override 30 | public Map getGreenPotentials(Grid grid, int viewNum) { 31 | Map result = new HashMap(); 32 | for (Cell cell : bugCells) 33 | result.put(cell, SingletonBitSet.create(bugValue)); 34 | return result; 35 | } 36 | 37 | @Override 38 | public Map getRedPotentials(Grid grid, int viewNum) { 39 | return super.getRemovablePotentials(); 40 | } 41 | 42 | @Override 43 | public Collection getLinks(Grid grid, int viewNum) { 44 | return null; 45 | } 46 | 47 | @Override 48 | public Cell[] getSelectedCells() { 49 | return bugCells; 50 | } 51 | 52 | @Override 53 | public Region[] getRegions() { 54 | return null; 55 | } 56 | 57 | @Override 58 | public int getViewCount() { 59 | return 1; 60 | } 61 | 62 | public double getDifficulty() { 63 | return 5.7; 64 | } 65 | 66 | public String getName() { 67 | return "BUG type 2"; 68 | } 69 | 70 | public String getShortName() { 71 | return "BUG2"; 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return "BUG type 2: " + Cell.toString(bugCells) + " on " + bugValue; 77 | } 78 | 79 | private String sharedRegions(){ 80 | if (Settings.getInstance().isVanilla()) 81 | return "row, column or block"; 82 | else { 83 | String res[] = new String[10]; 84 | int i = 0; 85 | String finalRes = "row"; 86 | if (Settings.getInstance().isVLatin()) 87 | return "row or column"; 88 | else 89 | res[i++]= "column"; 90 | if (Settings.getInstance().isBlocks()) 91 | res[i++]= "block"; 92 | if (Settings.getInstance().isDG()) 93 | res[i++]= "disjoint group"; 94 | if (Settings.getInstance().isWindows()) 95 | res[i++]= "window group"; 96 | if (Settings.getInstance().isX()) 97 | res[i++]= "diagonal"; 98 | if (Settings.getInstance().isGirandola()) 99 | res[i++]= "girandola group"; 100 | if (Settings.getInstance().isAsterisk()) 101 | res[i++]= "asterisk group"; 102 | if (Settings.getInstance().isCD()) 103 | res[i++]= "center dot group"; 104 | i--; 105 | for (int j = 0; j < i; j++) 106 | finalRes += ", " + res[j]; 107 | finalRes += " or " + res[i]; 108 | return finalRes; 109 | } 110 | } 111 | 112 | @Override 113 | public String toHtml(Grid grid) { 114 | String result = HtmlLoader.loadHtml(this, "BivalueUniversalGrave2.html"); 115 | String andBugCells = ValuesFormatter.formatCells(bugCells, " and "); 116 | return HtmlLoader.format(result, bugValue, andBugCells, sharedRegions()); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/BugHint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules.unique; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.solver.*; 12 | 13 | 14 | public abstract class BugHint extends IndirectHint implements Rule { 15 | 16 | public BugHint(IndirectHintProducer rule, Map removablePotentials) { 17 | super(rule, removablePotentials); 18 | } 19 | 20 | public String getClueHtml(Grid grid, boolean isBig) { 21 | if (isBig) { 22 | return "Look for a " + getName(); 23 | } else { 24 | return "Look for a Bivalue Universal Grave (BUG)"; 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/UniqueLoopType1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Unique {0} type 1

4 |

5 | The cells {3} form a Unique {0} with the values 6 | {1} and {2}. There are exactly two ways of placing 7 | the values {1} and {2} in the cells of the 8 | Unique {0}, forming two possible configurations. 9 | In both configurations, each {5} touched by the Unique 10 | {0} contains each of the two values {1} and {2} 11 | exactly once. As a result, if one of these two configurations were part of the 12 | solution, it could then be replaced by the other one to get a second valid 13 | solution. 14 |

15 | Because a valid sudoku cannot have more than one solution, none of the two 16 | configurations of the Unique {0} can be valid. The only possible way to avoid these 17 | two configurations is if the cell {4} does not contain the values 18 | {1} and {2}. 19 |

20 | 21 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/UniqueLoopType1Hint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules.unique; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.tools.*; 12 | 13 | 14 | public class UniqueLoopType1Hint extends UniqueLoopHint { 15 | 16 | private final Cell target; 17 | 18 | 19 | public UniqueLoopType1Hint(UniqueLoops rule, List loop, int v1, int v2, 20 | Map removablePotentials, Cell target) { 21 | super(rule, loop, v1, v2, removablePotentials); 22 | this.target = target; 23 | } 24 | 25 | 26 | @Override 27 | public Map getRedPotentials(Grid grid, int viewNum) { 28 | BitSet removable = new BitSet(10); 29 | removable.set(v1); 30 | removable.set(v2); 31 | return Collections.singletonMap(target, removable); 32 | } 33 | 34 | @Override 35 | public int getType() { 36 | return 1; 37 | } 38 | 39 | private String sharedRegions(){ 40 | if (Settings.getInstance().isVanilla()) 41 | return "row, column or block"; 42 | else { 43 | String res[] = new String[10]; 44 | int i = 0; 45 | String finalRes = "row"; 46 | if (Settings.getInstance().isVLatin()) 47 | return "row or column"; 48 | else 49 | res[i++]= "column"; 50 | if (Settings.getInstance().isBlocks()) 51 | res[i++]= "block"; 52 | if (Settings.getInstance().isDG()) 53 | res[i++]= "disjoint group"; 54 | if (Settings.getInstance().isWindows()) 55 | res[i++]= "window group"; 56 | if (Settings.getInstance().isX()) 57 | res[i++]= "diagonal"; 58 | if (Settings.getInstance().isGirandola()) 59 | res[i++]= "girandola group"; 60 | if (Settings.getInstance().isAsterisk()) 61 | res[i++]= "asterisk group"; 62 | if (Settings.getInstance().isCD()) 63 | res[i++]= "center dot group"; 64 | i--; 65 | for (int j = 0; j < i; j++) 66 | finalRes += ", " + res[j]; 67 | finalRes += " or " + res[i]; 68 | return finalRes; 69 | } 70 | } 71 | 72 | 73 | @Override 74 | public String toHtml(Grid grid) { 75 | String result = HtmlLoader.loadHtml(this, "UniqueLoopType1.html"); 76 | String type = getTypeName(); 77 | String cellName = target.toString(); 78 | Cell[] cells = new Cell[loop.size()]; 79 | loop.toArray(cells); 80 | String allCells = Cell.toString(cells); 81 | result = HtmlLoader.format(result, type, v1, v2, allCells, cellName, sharedRegions()); 82 | return result; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/UniqueLoopType2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Unique {0} type 2

4 |

5 | The cells {3} form a Unique {0} with the values 6 | {1} and {2}. There are exactly two ways of placing 7 | the values {1} and {2} in the cells of the 8 | Unique {0}, forming two possible configurations. 9 | In both configurations, each {7} touched by the Unique 10 | {0} contains each of the two values {1} and {2} 11 | exactly once. As a result, if one of these two configurations were part of the 12 | solution, it could then be replaced by the other one to get a second valid 13 | solution. 14 |

15 | Because a valid sudoku cannot have more than one solution, none of the two 16 | configurations of the Unique {0} can be valid. The only possible way to avoid these 17 | two configurations is if one of {4} contains the 18 | value {6}. 19 |

20 | Other occurrences of the value {6} can therefore be removed from the 21 | cells that share a {7} with {5}. 22 |

23 | 24 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/UniqueLoopType2Hint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules.unique; 7 | 8 | import java.util.*; 9 | import diuf.sudoku.*; 10 | import diuf.sudoku.tools.*; 11 | 12 | 13 | public class UniqueLoopType2Hint extends UniqueLoopHint { 14 | 15 | private final Cell[] cells; 16 | private final int value; 17 | 18 | 19 | public UniqueLoopType2Hint(UniqueLoops rule, List loop, int v1, int v2, 20 | Map removablePotentials, Cell[] cells, int value) { 21 | super(rule, loop, v1, v2, removablePotentials); 22 | this.cells = cells; 23 | this.value = value; 24 | } 25 | 26 | @Override 27 | public Map getRedPotentials(Grid grid, int viewNum) { 28 | Map result = new HashMap(super.getRemovablePotentials()); 29 | for (Cell c : cells) 30 | result.put(c, SingletonBitSet.create(value)); // orange 31 | return result; 32 | } 33 | 34 | @Override 35 | public Map getGreenPotentials(Grid grid, int viewNum) { 36 | Map result = new HashMap(super.getGreenPotentials(grid, viewNum)); 37 | for (Cell c : cells) { 38 | BitSet b = result.get(c); 39 | b.set(value); // orange 40 | } 41 | return result; 42 | } 43 | 44 | @Override 45 | public int getType() { 46 | return 2; 47 | } 48 | 49 | private String sharedRegions(){ 50 | if (Settings.getInstance().isVanilla()) 51 | return "row, column or block"; 52 | else { 53 | String res[] = new String[10]; 54 | int i = 0; 55 | String finalRes = "row"; 56 | if (Settings.getInstance().isVLatin()) 57 | return "row or column"; 58 | else 59 | res[i++]= "column"; 60 | if (Settings.getInstance().isBlocks()) 61 | res[i++]= "block"; 62 | if (Settings.getInstance().isDG()) 63 | res[i++]= "disjoint group"; 64 | if (Settings.getInstance().isWindows()) 65 | res[i++]= "window group"; 66 | if (Settings.getInstance().isX()) 67 | res[i++]= "diagonal"; 68 | if (Settings.getInstance().isGirandola()) 69 | res[i++]= "girandola group"; 70 | if (Settings.getInstance().isAsterisk()) 71 | res[i++]= "asterisk group"; 72 | if (Settings.getInstance().isCD()) 73 | res[i++]= "center dot group"; 74 | i--; 75 | for (int j = 0; j < i; j++) 76 | finalRes += ", " + res[j]; 77 | finalRes += " or " + res[i]; 78 | return finalRes; 79 | } 80 | } 81 | 82 | @Override 83 | public String toHtml(Grid g) { 84 | String result = HtmlLoader.loadHtml(this, "UniqueLoopType2.html"); 85 | String type = getTypeName(); 86 | Cell[] loopCells = new Cell[loop.size()]; 87 | loop.toArray(loopCells); 88 | String allCells = Cell.toString(loopCells); 89 | String extraCellsOr = ValuesFormatter.formatCells(cells, " or "); 90 | String extraCellsAnd = ValuesFormatter.formatCells(cells, " and "); 91 | result = HtmlLoader.format(result, type, v1, v2, allCells, extraCellsOr, 92 | extraCellsAnd, value, sharedRegions()); 93 | return result; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/UniqueLoopType3Hidden.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Unique {0} type 3 (with Hidden {7})

4 |

5 | The cells {3} form a Unique {0} with the values 6 | {1} and {2}. There are exactly two ways of placing 7 | the values {1} and {2} in the cells of the 8 | Unique {0}, forming two possible configurations. 9 | In both configurations, each {11} touched by the Unique 10 | {0} contains each of the two values {1} and {2} 11 | exactly once. As a result, if one of these two configurations were part of the 12 | solution, it could then be replaced by the other one to get a second valid 13 | solution. 14 |

15 | Because a valid sudoku cannot have more than one solution, none of the two 16 | configurations of the Unique {0} can be valid. This implies that either 17 | {4} or {5} contains one of the values 18 | {6}. It follows that either {4} or {5} 19 | forms a Hidden {7} with {8} on the values {9} in the 20 | {10}. 21 |

22 | Other potential values can therefore be removed from {8}. 23 |

24 | 25 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/UniqueLoopType3Naked.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Unique {0} type 3 (with Naked {7})

4 |

5 | The cells {3} form a Unique {0} with the values 6 | {1} and {2}. There are exactly two ways of placing 7 | the values {1} and {2} in the cells of the 8 | Unique {0}, forming two possible configurations. 9 | In both configurations, each {11} touched by the Unique 10 | {0} contains each of the two values {1} and {2} 11 | exactly once. As a result, if one of these two configurations were part of the 12 | solution, it could then be replaced by the other one to get a second valid 13 | solution. 14 |

15 | Because a valid sudoku cannot have more than one solution, none of the two 16 | configurations of the Unique {0} can be valid. This implies that either 17 | {4} or {5} contains one of the values 18 | {6}. It follows that either {4} or {5} 19 | forms a Naked {7} with {8} on the values {9} in the 20 | {10}. 21 |

22 | Other potential positions of the values {9} can therefore be removed from the 23 | {10}. 24 |

25 | 26 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/UniqueLoopType4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Unique {0} type 4

4 |

5 | The cells {3} form a Unique {0} with the values 6 | {1} and {2}. There are exactly two ways of placing 7 | the values {1} and {2} in the cells of the 8 | Unique {0}, forming two possible configurations. 9 | In both configurations, each {7} touched by the Unique 10 | {0} contains each of the two values {1} and {2} 11 | exactly once. As a result, if one of these two configurations were part of the 12 | solution, it could then be replaced by the other one to get a second valid 13 | solution. 14 |

15 | Because a valid sudoku cannot have more than one solution, none of the two 16 | configurations of the Unique {0} can be valid. Note that the two cells 17 | {4} and {5} are the only possible positions of the 18 | value {1} in the {6}, thus, one of these two cells will 19 | contain the value {1}. Therefore, the only possible way to avoid 20 | the two configurations is if neither {4} nor {5} 21 | contains the value {2}. 22 |

23 | 24 | -------------------------------------------------------------------------------- /diuf/sudoku/solver/rules/unique/UniqueLoopType4Hint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.solver.rules.unique; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | import diuf.sudoku.tools.*; 12 | 13 | 14 | public class UniqueLoopType4Hint extends UniqueLoopHint { 15 | 16 | private final Cell c1; 17 | private final Cell c2; 18 | private final int lockValue; 19 | private final int remValue; 20 | private final Grid.Region region; 21 | 22 | 23 | public UniqueLoopType4Hint(UniqueLoops rule, List loop, int lockValue, 24 | int remValue, Map removablePotentials, Cell c1, Cell c2, 25 | Grid.Region region) { 26 | super(rule, loop, lockValue, remValue, removablePotentials); 27 | this.c1 = c1; 28 | this.c2 = c2; 29 | this.lockValue = lockValue; 30 | this.remValue = remValue; 31 | this.region = region; 32 | } 33 | 34 | @Override 35 | public Map getRedPotentials(Grid grid, int viewNum) { 36 | return super.getRemovablePotentials(); 37 | } 38 | 39 | @Override 40 | public Grid.Region[] getRegions() { 41 | return new Grid.Region[] {region}; 42 | } 43 | 44 | @Override 45 | public int getType() { 46 | return 4; 47 | } 48 | 49 | private String sharedRegions(){ 50 | if (Settings.getInstance().isVanilla()) 51 | return "row, column or block"; 52 | else { 53 | String res[] = new String[10]; 54 | int i = 0; 55 | String finalRes = "row"; 56 | if (Settings.getInstance().isVLatin()) 57 | return "row or column"; 58 | else 59 | res[i++]= "column"; 60 | if (Settings.getInstance().isBlocks()) 61 | res[i++]= "block"; 62 | if (Settings.getInstance().isDG()) 63 | res[i++]= "disjoint group"; 64 | if (Settings.getInstance().isWindows()) 65 | res[i++]= "window group"; 66 | if (Settings.getInstance().isX()) 67 | res[i++]= "diagonal"; 68 | if (Settings.getInstance().isGirandola()) 69 | res[i++]= "girandola group"; 70 | if (Settings.getInstance().isAsterisk()) 71 | res[i++]= "asterisk group"; 72 | if (Settings.getInstance().isCD()) 73 | res[i++]= "center dot group"; 74 | i--; 75 | for (int j = 0; j < i; j++) 76 | finalRes += ", " + res[j]; 77 | finalRes += " or " + res[i]; 78 | return finalRes; 79 | } 80 | } 81 | 82 | @Override 83 | public String toHtml(Grid grid) { 84 | String result = HtmlLoader.loadHtml(this, "UniqueLoopType4.html"); 85 | String type = getTypeName(); 86 | Cell[] cells = new Cell[loop.size()]; 87 | loop.toArray(cells); 88 | String allCells = Cell.toString(cells); 89 | String cell1 = c1.toString(); 90 | String cell2 = c2.toString(); 91 | result = HtmlLoader.format(result, type, lockValue, remValue, allCells, cell1, 92 | cell2, region.toString(), sharedRegions()); 93 | return result; 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /diuf/sudoku/tools/Asker.java: -------------------------------------------------------------------------------- 1 | package diuf.sudoku.tools; 2 | 3 | /** 4 | * Interface for a gui component that can ask a yes/no question 5 | * to the user (e.g. in the form of a modal dialog). 6 | */ 7 | public interface Asker { 8 | 9 | /** 10 | * Ask a question to the user and wait for the answer. 11 | * @param question the question to display 12 | * @return whether the "yes" option was selected by the user 13 | */ 14 | public boolean ask(String question); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /diuf/sudoku/tools/CellSet.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package diuf.sudoku.tools; 5 | 6 | import java.util.BitSet; 7 | import java.util.Collection; 8 | import java.util.Iterator; 9 | import java.util.Set; 10 | 11 | import diuf.sudoku.Cell; 12 | import diuf.sudoku.Grid; 13 | 14 | /** 15 | * @author Mladen Dobrichev, 2019 16 | * 17 | */ 18 | public class CellSet implements Set { 19 | 20 | public final BitSet bits = new BitSet(); 21 | 22 | public CellSet(CellSet c) { 23 | //bits.clear(); 24 | bits.or(c.bits); 25 | } 26 | 27 | public CellSet(BitSet b) { 28 | bits.or(b); 29 | } 30 | 31 | public CellSet(Collection c) { 32 | for(Object cell : c) { 33 | bits.set(((Cell)cell).getIndex()); 34 | } 35 | } 36 | 37 | public CellSet(int[] c) { 38 | for(int i : c) { 39 | bits.set(i); 40 | } 41 | } 42 | 43 | public CellSet(Cell[] c) { 44 | for(Cell i : c) { 45 | bits.set(i.getIndex()); 46 | } 47 | } 48 | 49 | @Override 50 | public boolean add(Cell cell) { 51 | int i = cell.getIndex(); 52 | // boolean ret = bits.get(i); 53 | // bits.set(i); 54 | // return ret; 55 | bits.set(i); 56 | return false; 57 | } 58 | 59 | @Override 60 | public boolean addAll(Collection c) { 61 | if(c instanceof CellSet) { 62 | bits.or(((CellSet) c).bits); 63 | } 64 | else { 65 | for(Cell cell : c) { 66 | bits.set(cell.getIndex()); 67 | } 68 | } 69 | return false; 70 | } 71 | 72 | @Override 73 | public void clear() { 74 | bits.clear(); 75 | } 76 | 77 | @Override 78 | public boolean contains(Object o) { 79 | if(o instanceof Cell) { 80 | return bits.get(((Cell)o).getIndex()); 81 | } 82 | else if(o instanceof CellSet) { 83 | BitSet cl = (BitSet)(((CellSet) o).bits.clone()); 84 | cl.andNot(bits); 85 | return cl.isEmpty(); 86 | } 87 | throw new ClassCastException(); 88 | //return false; 89 | } 90 | 91 | public boolean containsCell(Cell c) { 92 | return bits.get(c.getIndex()); 93 | } 94 | 95 | @Override 96 | public boolean containsAll(Collection c) { 97 | if(c instanceof CellSet) { 98 | BitSet cl = (BitSet)(((CellSet) c).bits.clone()); 99 | cl.andNot(bits); 100 | return cl.isEmpty(); 101 | } 102 | //for(Cell cell : (Collection)c) { 103 | // if(!bits.get(cell.getIndex())) return false; 104 | //} 105 | for(Object cell : c) { 106 | if(!bits.get(((Cell)cell).getIndex())) return false; 107 | } 108 | return true; 109 | } 110 | 111 | //has at least one bit set in both this and given CellSet 112 | public boolean containsAny(CellSet c) { 113 | return bits.intersects(c.bits); 114 | } 115 | 116 | @Override 117 | public boolean isEmpty() { 118 | return bits.isEmpty(); 119 | } 120 | 121 | @Override 122 | public Iterator iterator() { 123 | return new CellIterator(); 124 | } 125 | 126 | @Override 127 | public boolean remove(Object o) { 128 | if(o instanceof Cell) { 129 | bits.clear(((Cell)o).getIndex()); 130 | return false; 131 | } 132 | else if(o instanceof CellSet) { 133 | bits.andNot(((CellSet) o).bits); 134 | return false; 135 | } 136 | throw new ClassCastException(); 137 | } 138 | 139 | @Override 140 | public boolean removeAll(Collection c) { 141 | if(c instanceof CellSet) { 142 | bits.andNot(((CellSet) c).bits); 143 | return false; 144 | } 145 | //for(Cell cell : (Collection)c) { 146 | // bits.clear(cell.getIndex()); 147 | //} 148 | for(Object cell : c) { 149 | bits.clear(((Cell)cell).getIndex()); 150 | } 151 | return false; 152 | } 153 | 154 | @Override 155 | public boolean retainAll(Collection c) { 156 | if(c instanceof CellSet) { 157 | bits.and(((CellSet) c).bits); 158 | return false; 159 | } 160 | //CellSet other = new CellSet((Collection)c); 161 | CellSet other = new CellSet(c); 162 | bits.and(other.bits); 163 | return false; 164 | } 165 | 166 | @Override 167 | public int size() { 168 | return bits.size(); 169 | } 170 | 171 | @Override 172 | public Object[] toArray() { 173 | Cell[] ret = new Cell[bits.cardinality()]; 174 | int i = 0; 175 | for(Cell c : this) { 176 | ret[i] = c; 177 | } 178 | return ret; 179 | } 180 | 181 | @Override 182 | public T[] toArray(T[] a) { 183 | // TODO Auto-generated method stub 184 | return null; 185 | } 186 | public class CellIterator implements Iterator { 187 | private int previous = -1; 188 | @Override 189 | public boolean hasNext() { 190 | return bits.nextSetBit(previous + 1) != -1; 191 | } 192 | 193 | @Override 194 | public Cell next() { 195 | previous = bits.nextSetBit(previous + 1); 196 | return Grid.getCell(previous); 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /diuf/sudoku/tools/CommonTuples.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.tools; 7 | 8 | import java.util.*; 9 | 10 | /** 11 | * Heart engine for the Naked Sets, Hidden Sets and N-Fishes rules. 12 | */ 13 | public class CommonTuples { 14 | 15 | /** 16 | * Given an array of bitsets, and a degree, check if 17 | *
    18 | *
  • All bitsets have cardinality greater than one 19 | *
  • The union of all bitsets has a cardinality of degree 20 | *
  • (Implied) all bitsets have a cardinality less than or equal to degree 21 | *
22 | * If this is the case, the union of all bitsets is returned. 23 | * If this is not the case, null is returned. 24 | * @param candidates the array of bitsets 25 | * @param degree the degree 26 | * @return the intersection of all bitsets, or null 27 | */ 28 | public static BitSet searchCommonTuple(BitSet[] candidates, int degree) { 29 | BitSet result = new BitSet(9); 30 | for (BitSet candidate : candidates) { 31 | if (candidate.cardinality() <= 1) 32 | return null; 33 | result.or(candidate); 34 | } 35 | if (result.cardinality() == degree) 36 | return result; 37 | return null; 38 | } 39 | 40 | /** 41 | * Same as before, but all bitsets must only have non-zero 42 | * cardinality instead of grater than one. 43 | * (Used for Unique Loops and BUGs type 3) 44 | */ 45 | public static BitSet searchCommonTupleLight(BitSet[] candidates, int degree) { 46 | BitSet result = new BitSet(9); 47 | for (BitSet candidate : candidates) { 48 | result.or(candidate); 49 | if (candidate.cardinality() == 0) 50 | return null; 51 | } 52 | if (result.cardinality() == degree) 53 | return result; 54 | return null; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /diuf/sudoku/tools/HtmlLoader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.tools; 7 | 8 | import java.io.*; 9 | 10 | 11 | public class HtmlLoader { 12 | 13 | private static final String LOAD_ERROR = "" + 14 | "Error while loading resource: {0}"; 15 | 16 | /** 17 | * Load the specified HTML file. 18 | * Custom color tags are automatically replaced by regular HTML color tags. 19 | * @param caller the caller object (used to fetch the class loader) 20 | * @param fileName the name of the HTML file 21 | * @return the content of the HTML file 22 | */ 23 | public static String loadHtml(Object caller, String fileName) { 24 | Class callerClass = caller.getClass(); 25 | InputStream input = callerClass.getResourceAsStream(fileName); 26 | if (input == null) { 27 | System.err.println("Resource not found: " + fileName); 28 | return LOAD_ERROR.replace("{0}", fileName); 29 | } 30 | try { 31 | Reader reader0 = new InputStreamReader(input, "ISO-8859-1"); 32 | Reader reader = new BufferedReader(reader0); 33 | char[] buffer = new char[4096]; 34 | StringBuilder builder = new StringBuilder(); 35 | int read = reader.read(buffer); 36 | while (read > 0) { 37 | builder.append(buffer, 0, read); 38 | read = reader.read(buffer); 39 | } 40 | reader.close(); 41 | String result = builder.toString(); 42 | // Replace generic coloring HTML tags 43 | return formatColors(result); 44 | } catch (IOException ex) { 45 | ex.printStackTrace(); 46 | return LOAD_ERROR.replace("{0}", fileName); 47 | } 48 | } 49 | 50 | /** 51 | * Replace custom color tags by regular HTML color tags. 52 | * @param html the HTML to convert 53 | * @return the converted HTML 54 | */ 55 | public static String formatColors(String html) { 56 | String result = html; 57 | result = result.replace("", ""); // red 58 | result = result.replace("", ""); 59 | result = result.replace("", ""); // green (candidate) 60 | result = result.replace("", ""); 61 | result = result.replace("", ""); // orange 62 | result = result.replace("", ""); 63 | result = result.replace("", ""); // blue (region) 64 | result = result.replace("", ""); 65 | result = result.replace("", ""); // green (region) 66 | result = result.replace("", ""); 67 | result = result.replace("", ""); // cyan (cell) 68 | result = result.replace("", ""); 69 | return result; 70 | } 71 | 72 | /** 73 | * Format a string. Replace the patterns "{0}", "{1}", etc 74 | * by args[0], args[1], etc. 75 | */ 76 | public static String format(String source, Object... args) { 77 | for (int i = 0; i < args.length; i++) { 78 | String pattern = "{" + i + "}"; 79 | source = source.replace(pattern, args[i].toString()); 80 | } 81 | return source; 82 | } 83 | 84 | public static String formatList(Object[] elements) { 85 | StringBuilder result = new StringBuilder(); 86 | for (int i = 0; i < elements.length; i++) { 87 | if (i > 0) { 88 | if (i < elements.length - 1) 89 | result.append(", "); 90 | else 91 | result.append(" and "); 92 | } 93 | result.append(elements[i].toString()); 94 | } 95 | return result.toString(); 96 | } 97 | 98 | public static String formatValues(int[] values) { 99 | StringBuilder result = new StringBuilder(); 100 | for (int i = 0; i < values.length; i++) { 101 | if (i > 0) { 102 | if (i < values.length - 1) 103 | result.append(", "); 104 | else 105 | result.append(" and "); 106 | } 107 | result.append(Integer.toString(values[i])); 108 | } 109 | return result.toString(); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /diuf/sudoku/tools/LinkedSet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.tools; 7 | 8 | import java.util.*; 9 | 10 | /** 11 | * Reflective set implementation. Like a LinkedHashSet 12 | * but with a weird method: 13 | * {@link #get(Object)}, which returns the element 14 | * of the set that is equal to the given element. 15 | * This is especially usefull when the implementation 16 | * of equals does not compare all fields. 17 | */ 18 | public class LinkedSet extends AbstractSet { 19 | 20 | /* 21 | * This implementation uses the wrapper pattern on the following map: 22 | */ 23 | private final LinkedHashMap target = new LinkedHashMap(); 24 | 25 | 26 | @Override 27 | public boolean add(T o) { 28 | return (target.put(o, o) != null); 29 | } 30 | 31 | @Override 32 | public void clear() { 33 | target.clear(); 34 | } 35 | 36 | @Override 37 | public boolean contains(Object o) { 38 | return target.containsKey(o); 39 | } 40 | 41 | public T get(T o) { 42 | return target.get(o); 43 | } 44 | 45 | @Override 46 | public Iterator iterator() { 47 | return target.keySet().iterator(); 48 | } 49 | 50 | @Override 51 | public boolean remove(Object o) { 52 | return (target.remove(o) != null); 53 | } 54 | 55 | @Override 56 | public int size() { 57 | return target.size(); 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | int ret = 0; 63 | for(Map.Entry e : target.entrySet()) { 64 | ret ^= e.getKey().hashCode(); 65 | } 66 | return ret; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /diuf/sudoku/tools/Pair.java: -------------------------------------------------------------------------------- 1 | package diuf.sudoku.tools; 2 | 3 | /** 4 | * An object pair. 5 | * @param the type of the first value 6 | * @param the type of the second value 7 | */ 8 | public class Pair { 9 | 10 | private final T1 value1; 11 | private final T2 value2; 12 | 13 | public Pair(T1 value1, T2 value2) { 14 | this.value1 = value1; 15 | this.value2 = value2; 16 | } 17 | 18 | public T1 getValue1() { 19 | return this.value1; 20 | } 21 | 22 | public T2 getValue2() { 23 | return this.value2; 24 | } 25 | 26 | private boolean eq(Object o1, Object o2) { 27 | if (o1 == null) 28 | return (o2 == null); 29 | else 30 | return o1.equals(o2); 31 | } 32 | 33 | @Override 34 | public boolean equals(Object o) { 35 | if (o instanceof Pair) { 36 | Pair other = (Pair)o; 37 | return (eq(this.value1, other.value1) && eq(this.value2, other.value2)); 38 | } 39 | return false; 40 | } 41 | 42 | @Override 43 | public int hashCode() { 44 | int result = 0; 45 | if (value1 != null) 46 | result = value1.hashCode(); 47 | if (value2 != null) 48 | result^= value2.hashCode(); 49 | return result; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /diuf/sudoku/tools/Permutations.java: -------------------------------------------------------------------------------- 1 | package diuf.sudoku.tools; 2 | 3 | /** 4 | * Generator of binary permutations. 5 | *

6 | * Given a length countBits and a 7 | * degree countOnes with 8 | * countOnes <= countBits, this class will compute 9 | * all binary numbers of length countBits that have 10 | * exactly countOnes bits equal to 1. 11 | *

12 | * The binary numbers are generated in increasing order. 13 | *

14 | * Example: with countBits = 5 and countOnes = 3 15 | * the following binary numbers are generated: 16 | *

    17 | *
  • 00111 18 | *
  • 01011 19 | *
  • 01101 20 | *
  • 01110 21 | *
  • 10011 22 | *
  • 10101 23 | *
  • 10110 24 | *
  • 11001 25 | *
  • 11010 26 | *
  • 11100 27 | *
28 | * Code adapted from "Hacker's Delight" by Henry S. Warren, 29 | * ISBN 0-201-91465-4 30 | */ 31 | public class Permutations { 32 | 33 | private final int countBits; 34 | private final int countOnes; 35 | 36 | private final long mask; 37 | 38 | private long value; 39 | private boolean isLast; 40 | 41 | 42 | /** 43 | * Create a new binary permutations generator. 44 | *

45 | * The maximum supported value for countBits 46 | * is 64. countOnes must be equal or less than 47 | * countBits. 48 | * @param countOnes the number of bits equal to one 49 | * @param countBits the length of the binary numbers in bits 50 | */ 51 | public Permutations(int countOnes, int countBits) { 52 | if (countOnes < 0) 53 | throw new IllegalArgumentException("countOnes < 0"); 54 | if (countBits < 0) 55 | throw new IllegalArgumentException("countBits < 0"); 56 | if (countOnes > countBits) 57 | throw new IllegalArgumentException("countOnes > countBits"); 58 | if (countBits > 64) 59 | throw new IllegalArgumentException("countBits > 64"); 60 | this.countBits = countBits; 61 | this.countOnes = countOnes; 62 | this.value = (1 << countOnes) - 1; 63 | this.mask = (1L << (countBits - countOnes)) - 1; 64 | this.isLast = (countBits == 0); 65 | } 66 | 67 | /** 68 | * Test if there are more permutations available 69 | * @return whether there are more permutations available 70 | */ 71 | public boolean hasNext() { 72 | boolean result = !isLast; 73 | isLast = ((value & -value) & mask) == 0; 74 | return result; 75 | } 76 | 77 | /** 78 | * Get the next binary permutation. 79 | * @return the next binary permutation 80 | */ 81 | public long next() { 82 | long result = value; 83 | if (!isLast) { 84 | long smallest = value & -value; 85 | long ripple = value + smallest; 86 | long ones = value ^ ripple; 87 | ones = (ones >>> 2) / smallest; 88 | value = ripple | ones; 89 | } 90 | return result; 91 | } 92 | 93 | /** 94 | * Get the next binary permutation as an array 95 | * of bit indexes. 96 | * @return the 0-based indexes of the bits that are set 97 | * to one. 98 | */ 99 | public int[] nextBitNums() { 100 | long mask = next(); 101 | int[] result = new int[countOnes]; 102 | int dst = 0; 103 | for (int src = 0; src < countBits; src++) { 104 | if ((mask & (1L << src)) != 0) // Bit number 'src' is set 105 | result[dst++] = src; 106 | } 107 | return result; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /diuf/sudoku/tools/SingletonBitSet.java: -------------------------------------------------------------------------------- 1 | package diuf.sudoku.tools; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Factory for BitSets containing only 7 | * one element. 8 | */ 9 | public class SingletonBitSet { 10 | 11 | public static BitSet create(int value) { 12 | BitSet result = new BitSet(10); 13 | result.set(value); 14 | return result; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /diuf/sudoku/tools/StrongReference.java: -------------------------------------------------------------------------------- 1 | package diuf.sudoku.tools; 2 | 3 | /** 4 | * A typed reference to an arbitrary object. 5 | */ 6 | public class StrongReference { 7 | 8 | private T value; 9 | 10 | 11 | public StrongReference() { 12 | } 13 | 14 | public StrongReference(T value) { 15 | this.value = value; 16 | } 17 | 18 | public T getValue() { 19 | return this.value; 20 | } 21 | 22 | public void setValue(T value) { 23 | this.value = value; 24 | } 25 | 26 | public boolean isValueSet() { 27 | return this.value != null; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /diuf/sudoku/tools/Twomutations.java: -------------------------------------------------------------------------------- 1 | package diuf.sudoku.tools; 2 | 3 | /** 4 | * Generator of binary permutations. 5 | *

6 | * Given a length countBits and a 7 | * degree countOnes with 8 | * countOnes <= countBits, this class will compute 9 | * all binary numbers of length countBits that have 10 | * exactly countOnes bits equal to 1. 11 | *

12 | * The binary numbers are generated in increasing order. 13 | *

14 | * Example: with countBits = 5 and countOnes = 3 15 | * the following binary numbers are generated: 16 | *

    17 | *
  • 00111 18 | *
  • 01011 19 | *
  • 01101 20 | *
  • 01110 21 | *
  • 10011 22 | *
  • 10101 23 | *
  • 10110 24 | *
  • 11001 25 | *
  • 11010 26 | *
  • 11100 27 | *
28 | * Code adapted from "Hacker's Delight" by Henry S. Warren, 29 | * ISBN 0-201-91465-4 30 | */ 31 | public class Twomutations { 32 | 33 | private final int countBits; 34 | private final int countOnes; 35 | 36 | private int n1; 37 | private int n2; 38 | 39 | private boolean isLast; 40 | 41 | /** 42 | * Create a new binary permutations generator. 43 | *

44 | * The maximum supported value for countBits 45 | * is 64. countOnes must be equal or less than 46 | * countBits. 47 | * @param countOnes the number of bits equal to one 48 | * @param countBits the length of the binary numbers in bits 49 | */ 50 | public Twomutations(int countOnes, int countBits) { 51 | if (countOnes < 0) 52 | throw new IllegalArgumentException("countOnes < 0"); 53 | if (countBits < 0) 54 | throw new IllegalArgumentException("countBits < 0"); 55 | if (countOnes > countBits) 56 | throw new IllegalArgumentException("countOnes > countBits"); 57 | if (countBits > 81) 58 | throw new IllegalArgumentException("countBits > 81"); 59 | this.countBits = countBits; 60 | this.countOnes = countOnes; 61 | this.n1 = 0; 62 | this.n2 = 1; 63 | this.isLast = (countBits == n2); 64 | } 65 | 66 | /** 67 | * Test if there are more permutations available 68 | * @return whether there are more permutations available 69 | */ 70 | public boolean hasNext() { 71 | return !isLast; 72 | } 73 | 74 | /** 75 | * Get the next binary permutation. 76 | * @return the next binary permutation 77 | */ 78 | public void next() { 79 | n1++; 80 | if ( n1 == n2 ) 81 | { 82 | n1 = 0; 83 | n2++; 84 | } 85 | isLast = (countBits == n2); 86 | } 87 | 88 | /** 89 | * Get the next binary permutation as an array 90 | * of bit indexes. 91 | * @return the 0-based indexes of the bits that are set 92 | * to one. 93 | */ 94 | public int[] nextBitNums() { 95 | int[] result = new int[countOnes]; 96 | result[ 0] = n1; 97 | result[ 1] = n2; 98 | next(); 99 | return result; 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /diuf/sudoku/tools/ValuesFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Project: Sudoku Explainer 3 | * Copyright (C) 2006-2007 Nicolas Juillerat 4 | * Available under the terms of the Lesser General Public License (LGPL) 5 | */ 6 | package diuf.sudoku.tools; 7 | 8 | import java.util.*; 9 | 10 | import diuf.sudoku.*; 11 | 12 | /** 13 | * Formatter for arrays and bitsets. 14 | */ 15 | public class ValuesFormatter { 16 | 17 | public static String formatValues(int[] values, String lastSep) { 18 | StringBuilder result = new StringBuilder(); 19 | for (int i = 0; i < values.length; i++) { 20 | if (i > 0 && i == values.length - 1) 21 | result.append(lastSep); 22 | else if (i > 0) 23 | result.append(", "); 24 | result.append(values[i]); 25 | } 26 | return result.toString(); 27 | } 28 | 29 | public static String formatValues(BitSet values, String lastSep) { 30 | int[] array = new int[values.cardinality()]; 31 | int index = 0; 32 | for (int v = values.nextSetBit(0); v >= 0; v = values.nextSetBit(v + 1)) 33 | array[index++] = v; 34 | return formatValues(array, lastSep); 35 | } 36 | 37 | public static String formatCells(Cell[] cells, String lastSep) { 38 | StringBuilder result = new StringBuilder(); 39 | for (int i = 0; i < cells.length; i++) { 40 | if (i > 0 && i == cells.length - 1) 41 | result.append(lastSep); 42 | else if (i > 0) 43 | result.append(", "); 44 | result.append(cells[i].toString()); 45 | } 46 | return result.toString(); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /doc/SukakuExplainer01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudokuMonster/SukakuExplainer/362854eea4e983017726d406ac9ee8a28909bcc7/doc/SukakuExplainer01.png -------------------------------------------------------------------------------- /doc/SukakuExplainer02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudokuMonster/SukakuExplainer/362854eea4e983017726d406ac9ee8a28909bcc7/doc/SukakuExplainer02.png -------------------------------------------------------------------------------- /doc/SukakuExplainer03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudokuMonster/SukakuExplainer/362854eea4e983017726d406ac9ee8a28909bcc7/doc/SukakuExplainer03.png -------------------------------------------------------------------------------- /doc/SukakuExplainer04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudokuMonster/SukakuExplainer/362854eea4e983017726d406ac9ee8a28909bcc7/doc/SukakuExplainer04.png -------------------------------------------------------------------------------- /doc/generateDiff.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudokuMonster/SukakuExplainer/362854eea4e983017726d406ac9ee8a28909bcc7/doc/generateDiff.ods -------------------------------------------------------------------------------- /doc/rules.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SudokuMonster/SukakuExplainer/362854eea4e983017726d406ac9ee8a28909bcc7/doc/rules.ods --------------------------------------------------------------------------------