diff --git a/diuf/sudoku/Cell.java b/diuf/sudoku/Cell.java index 11a800d..8fa5dd8 100644 --- a/diuf/sudoku/Cell.java +++ b/diuf/sudoku/Cell.java @@ -92,6 +92,21 @@ public CellSet getVisibleCells() { return Grid.visibleCellsSet[index]; } + public boolean canSeeCell(Cell other) { + return Grid.visibleCellsSet[index].contains(other); + } + + public boolean canSeeAnyOfCells(CellSet cellSet) { + CellSet currentSet = new CellSet(Grid.visibleCellsSet[index]); + currentSet.retainAll(cellSet); + int currentSetSize = currentSet.size(); + if (currentSetSize > 0) + return true; + return false; + } + + + /** * Get the cells that form the "house" of this cell. The * cell indexes have to be greater than this cell index. The "house" diff --git a/diuf/sudoku/Grid.java b/diuf/sudoku/Grid.java index 21b0400..c6b97a4 100644 --- a/diuf/sudoku/Grid.java +++ b/diuf/sudoku/Grid.java @@ -77,9 +77,9 @@ public class Grid { public static final int[][] cellRegions; public static final int[][] visibleCellIndex; public static final int[][] forwardVisibleCellIndex; - private static final Block[] blocks; - private static final Row[] rows; - private static final Column[] columns; + public static final Block[] blocks; + public static final Row[] rows; + public static final Column[] columns; public static final Region[][] regions; public static final CellSet[] visibleCellsSet; public static final CellSet[] forwardVisibleCellsSet; @@ -670,7 +670,7 @@ public String toString() { public String toFullString() { Settings settings = Settings.getInstance(); if (settings.isRCNotation()) - return toString() + " R" + (rowNum + 1); + return toString() + " " + (rowNum + 1); else return toString() + " " + (rowNum + 1); } @@ -714,7 +714,7 @@ public String toString() { public String toFullString() { Settings settings = Settings.getInstance(); if (settings.isRCNotation()) - return toString() + " C" + (columnNum + 1); + return toString() + " " + (columnNum + 1); else return toString() + " " + (char)('A' + columnNum); } diff --git a/diuf/sudoku/Settings.java b/diuf/sudoku/Settings.java index 95ec1f9..839bcb6 100644 --- a/diuf/sudoku/Settings.java +++ b/diuf/sudoku/Settings.java @@ -17,9 +17,9 @@ public class Settings { public final static int VERSION = 1; - public final static int REVISION = 4; + public final static int REVISION = 6; public final static String SUBREV = ".1"; - public final static String releaseDate = "2019-09-22"; + public final static String releaseDate = "2019-10-15"; public final static String releaseYear = "2019"; public final static String releaseLicence = "Lesser General Public License"; public final static String releaseLicenceMini = "LGPL"; diff --git a/diuf/sudoku/SolvingTechnique.java b/diuf/sudoku/SolvingTechnique.java index ee98583..0deca20 100644 --- a/diuf/sudoku/SolvingTechnique.java +++ b/diuf/sudoku/SolvingTechnique.java @@ -19,6 +19,7 @@ public enum SolvingTechnique { NakedTriplet("Naked Triplet"), Swordfish("Swordfish"), HiddenTriplet("Hidden Triplet"), + TurbotFish("Scraper, Kite, Turbot"), XYWing("XY-Wing"), XYZWing("XYZ-Wing"), // WWing("W-Wing"), @@ -27,6 +28,7 @@ public enum SolvingTechnique { NakedQuad("Naked Quad"), Jellyfish("Jellyfish"), HiddenQuad("Hidden Quad"), + ThreeStrongLinks("3 Strong-linked Fishes"), //VWXYZWing4("VWXYZ-Wing 4"), //VWXYZWing5("VWXYZ-Wing 5"), VWXYZWing("VWXYZ-Wing"), diff --git a/diuf/sudoku/gui/GenerateDialog.java b/diuf/sudoku/gui/GenerateDialog.java index 223359f..17e78d4 100644 --- a/diuf/sudoku/gui/GenerateDialog.java +++ b/diuf/sudoku/gui/GenerateDialog.java @@ -527,6 +527,168 @@ public String getnotMaxTechnique2() { public String getnotMaxTechnique3() { return ""; } + }, + TwoStringKite { + + @Override + public double getMinDifficulty() { + return 4.1; + } + + @Override + public double getMaxDifficulty() { + return 4.1; + } + @Override + public double getincludeDifficulty1() { + return 0.0; + } + @Override + public double getincludeDifficulty2() { + return 0.0; + } + @Override + public double getincludeDifficulty3() { + return 0.0; + } + @Override + public double getexcludeDifficulty1() { + return 0.0; + } + @Override + public double getexcludeDifficulty2() { + return 0.0; + } + @Override + public double getexcludeDifficulty3() { + return 0.0; + } + @Override + public double getnotMaxDifficulty1() { + return 0.0; + } + @Override + public double getnotMaxDifficulty2() { + return 0.0; + } + @Override + public double getnotMaxDifficulty3() { + return 0.0; + } + public String getexcludeTechnique1() { + return "Skyscraper"; + } + @Override + public String getexcludeTechnique2() { + return ""; + } + @Override + public String getexcludeTechnique3() { + return ""; + } + public String getincludeTechnique1() { + return ""; + } + @Override + public String getincludeTechnique2() { + return ""; + } + @Override + public String getincludeTechnique3() { + return ""; + } + public String getnotMaxTechnique1() { + return ""; + } + @Override + public String getnotMaxTechnique2() { + return ""; + } + @Override + public String getnotMaxTechnique3() { + return ""; + } + }, + ThreeStrongLinks { + + @Override + public double getMinDifficulty() { + return 5.4; + } + + @Override + public double getMaxDifficulty() { + return 5.6; + } + @Override + public double getincludeDifficulty1() { + return 0.0; + } + @Override + public double getincludeDifficulty2() { + return 0.0; + } + @Override + public double getincludeDifficulty3() { + return 0.0; + } + @Override + public double getexcludeDifficulty1() { + return 0.0; + } + @Override + public double getexcludeDifficulty2() { + return 0.0; + } + @Override + public double getexcludeDifficulty3() { + return 0.0; + } + @Override + public double getnotMaxDifficulty1() { + return 0.0; + } + @Override + public double getnotMaxDifficulty2() { + return 0.0; + } + @Override + public double getnotMaxDifficulty3() { + return 0.0; + } + public String getexcludeTechnique1() { + return ""; + } + @Override + public String getexcludeTechnique2() { + return ""; + } + @Override + public String getexcludeTechnique3() { + return ""; + } + public String getincludeTechnique1() { + return " 10"; + } + @Override + public String getincludeTechnique2() { + return ""; + } + @Override + public String getincludeTechnique3() { + return ""; + } + public String getnotMaxTechnique1() { + return ""; + } + @Override + public String getnotMaxTechnique2() { + return ""; + } + @Override + public String getnotMaxTechnique3() { + return ""; + } }, WXYZ { @@ -923,7 +1085,7 @@ public String getexcludeTechnique3() { return ""; } public String getincludeTechnique1() { - return ""; + return "ligned"; } @Override public String getincludeTechnique2() { diff --git a/diuf/sudoku/gui/ThreeStrongLinks.html b/diuf/sudoku/gui/ThreeStrongLinks.html new file mode 100644 index 0000000..c0724dc --- /dev/null +++ b/diuf/sudoku/gui/ThreeStrongLinks.html @@ -0,0 +1,6 @@ + + + 3 strong links: Requires a fish with 3 strong links (101 or 102 formation) + Rating: 5.5-5.6 + + \ No newline at end of file diff --git a/diuf/sudoku/gui/TwoStringKite.html b/diuf/sudoku/gui/TwoStringKite.html new file mode 100644 index 0000000..5c80500 --- /dev/null +++ b/diuf/sudoku/gui/TwoStringKite.html @@ -0,0 +1,6 @@ + + + 2-String Kite: The Sudoku requires a 2-String Kite to solve. + Rating: 4.1 + + \ No newline at end of file diff --git a/diuf/sudoku/solver/Rule.java b/diuf/sudoku/solver/Rule.java index 29a355c..011ed1e 100644 --- a/diuf/sudoku/solver/Rule.java +++ b/diuf/sudoku/solver/Rule.java @@ -44,10 +44,12 @@ public interface Rule { *
  • 2.8: Claiming *
  • 3.0, 3.2, 3.4: Naked pair, X-Wing, Hidden pair *
  • 3.6, 3.8, 4.0: Naked triplet, Swordfish, Hidden triplet + *
  • 4.0, 4.1, 4.2: Skyscraper, 2-String Kite, Turbot Fish *
  • 4.2, 4.4: XY-Wing, XYZ-Wing // *
  • 4.4: W-Wing *
  • 4.5 - 5.0: Unique Rectangles and Loops *
  • 5.0, 5.2, 5.4: Naked quad, Jellyfish, Hidden quad + *
  • 5.4, 5.5, 5.6: Skyscraper (3SL) , 3-String Kite, Turbot Fish (3SL) *
  • 5.5: WXYZ-Wing *
  • 5.6 - 6.0: Bivalue Universal Graves *
  • 6.2 - 6.4: VWXYZ-Wing @@ -84,10 +86,12 @@ public interface Rule { *
  • 3.1: Direct Hidden Triplet//2.5 ---> 3.0 ---> 3.1 *
  • 3.2: X-Wing *
  • 3.6, 3.8, 4.0: Naked triplet, Hidden triplet, Swordfish//3.8 ---> 4.0 4.0 ---> 3.8 + *
  • 4.0, 4.1, 4.2: Skyscraper, 2-String Kite, Turbot Fish (placed before Swordfish) *
  • 4.2, 4.4: XY-Wing, XYZ-Wing // *
  • 4.4: W-Wing *
  • 4.5 - 5.0: Unique Rectangles and Loops *
  • 5.0, 5.2, 5.4: Naked quad, Hidden quad, Jellyfish//5.2 ---> 5.4 5.4 ---> 5.2 + *
  • 5.4, 5.5, 5.6: Skyscraper (3SL) , 3-String Kite, Turbot Fish (3SL) *
  • 5.5: WXYZ-Wing *
  • 5.6 - 6.0: Bivalue Universal Graves *
  • 6.2 - 6.4: VWXYZ-Wing diff --git a/diuf/sudoku/solver/Solver.java b/diuf/sudoku/solver/Solver.java index ae43318..199a7df 100644 --- a/diuf/sudoku/solver/Solver.java +++ b/diuf/sudoku/solver/Solver.java @@ -148,6 +148,7 @@ public Solver(Grid grid) { addIfWorth(SolvingTechnique.XWing, indirectHintProducers, new Fisherman(2)); addIfWorth(SolvingTechnique.NakedTriplet, indirectHintProducers, new NakedSet(3)); addIfWorth(SolvingTechnique.HiddenTriplet, indirectHintProducers, new HiddenSet(3, false)); + addIfWorth(SolvingTechnique.TurbotFish, indirectHintProducers, new TurbotFish()); addIfWorth(SolvingTechnique.Swordfish, indirectHintProducers, new Fisherman(3)); addIfWorth(SolvingTechnique.XYWing, indirectHintProducers, new XYWing(false)); addIfWorth(SolvingTechnique.XYZWing, indirectHintProducers, new XYWing(true)); @@ -157,8 +158,6 @@ public Solver(Grid grid) { addIfWorth(SolvingTechnique.HiddenQuad, indirectHintProducers, new HiddenSet(4, false)); addIfWorth(SolvingTechnique.Jellyfish, indirectHintProducers, new Fisherman(4)); addIfWorth(SolvingTechnique.WXYZWing, indirectHintProducers, new WXYZWing()); - //addIfWorth(SolvingTechnique.VWXYZWing4, indirectHintProducers, new VWXYZWing(true)); - //addIfWorth(SolvingTechnique.VWXYZWing5, indirectHintProducers, new VWXYZWing(false)); addIfWorth(SolvingTechnique.BivalueUniversalGrave, indirectHintProducers, new BivalueUniversalGrave()); addIfWorth(SolvingTechnique.VWXYZWing, indirectHintProducers, new VWXYZWing()); addIfWorth(SolvingTechnique.AlignedPairExclusion, indirectHintProducers, new AlignedPairExclusion()); @@ -202,6 +201,7 @@ public Solver(Grid grid) { addIfWorth(SolvingTechnique.NakedTriplet, indirectHintProducers, new NakedSet(3)); addIfWorth(SolvingTechnique.Swordfish, indirectHintProducers, new Fisherman(3)); addIfWorth(SolvingTechnique.HiddenTriplet, indirectHintProducers, new HiddenSet(3, false)); + addIfWorth(SolvingTechnique.TurbotFish, indirectHintProducers, new TurbotFish()); addIfWorth(SolvingTechnique.XYWing, indirectHintProducers, new XYWing(false)); addIfWorth(SolvingTechnique.XYZWing, indirectHintProducers, new XYWing(true)); // addIfWorth(SolvingTechnique.WWing, indirectHintProducers, new WWing()); @@ -209,7 +209,8 @@ public Solver(Grid grid) { addIfWorth(SolvingTechnique.NakedQuad, indirectHintProducers, new NakedSet(4)); addIfWorth(SolvingTechnique.Jellyfish, indirectHintProducers, new Fisherman(4)); addIfWorth(SolvingTechnique.HiddenQuad, indirectHintProducers, new HiddenSet(4, false)); - addIfWorth(SolvingTechnique.WXYZWing, indirectHintProducers, new WXYZWing()); + addIfWorth(SolvingTechnique.ThreeStrongLinks, indirectHintProducers, new ThreeStrongLinks()); + addIfWorth(SolvingTechnique.WXYZWing, indirectHintProducers, new WXYZWing()); //addIfWorth(SolvingTechnique.VWXYZWing4, indirectHintProducers, new VWXYZWing(true)); //addIfWorth(SolvingTechnique.VWXYZWing5, indirectHintProducers, new VWXYZWing(false)); addIfWorth(SolvingTechnique.BivalueUniversalGrave, indirectHintProducers, new BivalueUniversalGrave()); diff --git a/diuf/sudoku/solver/rules/ThreeStrongLinks.java b/diuf/sudoku/solver/rules/ThreeStrongLinks.java new file mode 100644 index 0000000..8e254a1 --- /dev/null +++ b/diuf/sudoku/solver/rules/ThreeStrongLinks.java @@ -0,0 +1,214 @@ +package diuf.sudoku.solver.rules; + +import java.util.*; +import diuf.sudoku.*; +import diuf.sudoku.solver.*; +import diuf.sudoku.tools.*; + + +/** + * Implementation of Turbot Fish technique solver. + */ +public class ThreeStrongLinks implements IndirectHintProducer { + + @Override + public void getHints(Grid grid, HintsAccumulator accu) throws InterruptedException { + // 3 parallel strong links + getHints(grid, accu, 1, 1, 1); + getHints(grid, accu, 2, 2, 2); + // 3 strong links in same Lines + //with boxes + getHints(grid, accu, 0, 0, 0); + getHints(grid, accu, 0, 0, 1); + getHints(grid, accu, 0, 0, 2); + getHints(grid, accu, 0, 1, 1); + getHints(grid, accu, 0, 2, 2); + // 3 strong links with box(es) + //mixed lines + getHints(grid, accu, 0, 1, 2); + getHints(grid, accu, 1, 1, 2); + getHints(grid, accu, 1, 2, 2); + } + + private Grid.Region shareRegionOf(Grid grid, + Cell start, Cell bridge1, Cell bridge2, Cell end) { + if (bridge1.getX() == bridge2.getX()) { + return Grid.columns[bridge1.getX()]; + } else if (bridge1.getY() == bridge2.getY()) { + return Grid.rows[bridge1.getY()]; + } else if (bridge1.getB() == bridge2.getB()) { + return Grid.blocks[bridge1.getB()]; + } else return null; + } + +/* private boolean isSameBox (Cell boxCell1, Cell boxCell2) { + if (boxCell1.getB() == boxCell2.getB()) + return true; + return false; + }*/ + + + private boolean isSameLine (Cell lineCell1, Cell lineCell2) { + if (lineCell1.getX() == lineCell2.getX() || lineCell1.getY() == lineCell2.getY()) + return true; + return false; + } + + private void getHints(Grid grid, HintsAccumulator accu, + int baseLink1, int baseLink2, int baseLink3) + throws InterruptedException { + Cell[] cells = new Cell[6]; + for (int digit = 1; digit <= 9; digit++) { + Grid.Region[] baseLink1Regions = grid.getRegions(baseLink1); + Grid.Region[] baseLink2Regions = grid.getRegions(baseLink2); + Grid.Region[] baseLink3Regions = grid.getRegions(baseLink3); + int p1, p2, p3; + for (int i1 = 0; i1 < baseLink1Regions.length; i1++) { + Grid.Region baseLink1Region = baseLink1Regions[i1]; + BitSet baseLink1RegionPotentials = baseLink1Region.getPotentialPositions(grid, digit); + if (baseLink1RegionPotentials.cardinality() != 2) + continue; + // region 1 + Cell regionCell1 = baseLink1Region.getCell(p1 = baseLink1RegionPotentials.nextSetBit(0)); + Cell regionCell2 = baseLink1Region.getCell(baseLink1RegionPotentials.nextSetBit(p1 + 1)); + if (baseLink1 == 0 && isSameLine(regionCell1,regionCell2)) + continue; + cells[0] = regionCell1; + cells[1] = regionCell2; + for (int i2 = (baseLink1 == baseLink2 ? i1+1 : 0); i2 < baseLink2Regions.length; i2++) { + Grid.Region baseLink2Region = baseLink2Regions[i2]; + BitSet baseLink2RegionPotentials = baseLink2Region.getPotentialPositions(grid, digit); + if (baseLink2RegionPotentials.cardinality() != 2) + continue; + // region 2 + regionCell1 = baseLink2Region.getCell(p2 = baseLink2RegionPotentials.nextSetBit(0)); + regionCell2 = baseLink2Region.getCell(baseLink2RegionPotentials.nextSetBit(p2 + 1)); + if (baseLink2 == 0 && isSameLine(regionCell1,regionCell2)) + continue; + cells[2] = regionCell1; + cells[3] = regionCell2; + for (int i3 = (baseLink2 == baseLink3 ? i2+1 : (baseLink1 == baseLink3 ? i1+1 : 0)); i3 < baseLink3Regions.length; i3++) { + Grid.Region baseLink3Region = baseLink3Regions[i3]; + BitSet baseLink3RegionPotentials = baseLink3Region.getPotentialPositions(grid, digit); + if (baseLink3RegionPotentials.cardinality() != 2) + continue; + regionCell1 = baseLink3Region.getCell(p3 = baseLink3RegionPotentials.nextSetBit(0)); + regionCell2 = baseLink3Region.getCell(baseLink3RegionPotentials.nextSetBit(p3 + 1)); + if (baseLink3 == 0 && isSameLine(regionCell1,regionCell2)) + continue; + cells[4] = regionCell1; + cells[5] = regionCell2; + boolean next = false; + for (int i = 0; i < 4; i++) { + for (int j = i + 1; j < 5; j++) { + for (int k = j + 1; k < 6; k++) { + if (cells[i].equals(cells[j]) || cells[i].equals(cells[k]) || cells[j].equals(cells[k])) { + next = true; + break; + } + } + } + } + if (next) { + cells[4] = null; + cells[5] = null; + continue; + } + Grid.Region shareRegion1, shareRegion2; + Cell start1, bridge11, bridge12, end2, bridge21, bridge22; + for (int i = 0; i < 2; i++) { + for (int j = 2; j < 4; j++) { + for (int k = 4; k < 6; k++) { + if ((shareRegion1 = shareRegionOf(grid, + start1 = cells[i], + bridge11 = cells[1 - i], + bridge12 = cells[j], + bridge21 = cells[5 - j])) != null && + (shareRegion2 = shareRegionOf(grid, + bridge12 = cells[j], + bridge21 = cells[5 - j], + bridge22 = cells[k], + end2 = cells[9 - k])) != null && + !shareRegion1.equals(baseLink1Region) && !shareRegion1.equals(baseLink2Region) && !shareRegion1.equals(baseLink3Region) && + !shareRegion2.equals(baseLink1Region) && !shareRegion2.equals(baseLink2Region) && !shareRegion2.equals(baseLink3Region) ) { + // 3 strong-linked Turbot fish found + ThreeStrongLinksHint hint = createHint(grid, digit, start1, bridge11, bridge12, + baseLink1Region, baseLink2Region, shareRegion1, end2, bridge21, bridge22, baseLink3Region, shareRegion2, baseLink1, baseLink2, baseLink3); + if (hint.isWorth()) + accu.add(hint); + } + if ((shareRegion1 = shareRegionOf(grid, + start1 = cells[i], + bridge11 = cells[1 - i], + bridge12 = cells[k], + bridge21 = cells[9 - k])) != null && + (shareRegion2 = shareRegionOf(grid, + bridge12 = cells[k], + bridge21 = cells[9 - k], + bridge22 = cells[j], + end2 = cells[5 - j])) != null && + !shareRegion1.equals(baseLink1Region) && !shareRegion1.equals(baseLink2Region) && !shareRegion1.equals(baseLink3Region) && + !shareRegion2.equals(baseLink1Region) && !shareRegion2.equals(baseLink2Region) && !shareRegion2.equals(baseLink3Region) ) { + // 3 strong-linked Turbot fish found + ThreeStrongLinksHint hint = createHint(grid, digit, start1, bridge11, bridge12, + baseLink1Region, baseLink3Region, shareRegion1, end2, bridge21, bridge22, baseLink2Region, shareRegion2, baseLink1, baseLink3, baseLink2); + if (hint.isWorth()) + accu.add(hint); + } + if ((shareRegion1 = shareRegionOf(grid, + start1 = cells[j], + bridge11 = cells[5 - j], + bridge12 = cells[i], + bridge21 = cells[1 - i])) != null && + (shareRegion2 = shareRegionOf(grid, + bridge12 = cells[i], + bridge21 = cells[1 - i], + bridge22 = cells[k], + end2 = cells[9 - k])) != null && + !shareRegion1.equals(baseLink1Region) && !shareRegion1.equals(baseLink2Region) && !shareRegion1.equals(baseLink3Region) && + !shareRegion2.equals(baseLink1Region) && !shareRegion2.equals(baseLink2Region) && !shareRegion2.equals(baseLink3Region) ) { + // 3 strong-linked Turbot fish found + ThreeStrongLinksHint hint = createHint(grid, digit, start1, bridge11, bridge12, + baseLink2Region, baseLink1Region, shareRegion1, end2, bridge21, bridge22, baseLink3Region, shareRegion2, baseLink2, baseLink1, baseLink3); + if (hint.isWorth()) + accu.add(hint); + } // if sharedRegion () && sharedRegion () && sharedRegion != bbaseLink regions + } // for int k = 0..2 + } // for int j = 0..2 + } // for int i = 0..2 + cells[4] = null; + cells[5] = null; + } //i3 + cells[2] = null; + cells[3] = null; + } //i2 + cells[0] = null; + cells[1] = null; + } //i1 + } //digit + } + + private ThreeStrongLinksHint createHint(Grid grid, int value, Cell start1, Cell bridgeCell11, Cell bridgeCell12, + Grid.Region baseLink1Set, Grid.Region baseLink2Set, Grid.Region shareRegion1, Cell end2, Cell bridgeCell21, Cell bridgeCell22, + Grid.Region baseLink3Set, Grid.Region shareRegion2, int baseLink1, int baseLink2, int baseLink3) { + // Build list of removable potentials + Map removablePotentials = new HashMap<>(); + CellSet victims = new CellSet(start1.getVisibleCells()); + victims.retainAll(end2.getVisibleCells()); + victims.remove(start1); + victims.remove(end2); + for (Cell cell : victims) { + if (grid.hasCellPotentialValue(cell.getIndex(), value)) + removablePotentials.put(cell, SingletonBitSet.create(value)); + } + + // Create hint + return new ThreeStrongLinksHint(this, removablePotentials, + start1, bridgeCell11, bridgeCell12, value, baseLink1Set, baseLink2Set, shareRegion1, bridgeCell21, bridgeCell22, end2, baseLink3Set, shareRegion2, baseLink1, baseLink2, baseLink3); + } + + @Override + public String toString() { + return "3-link Turbot Fishes"; + } +} diff --git a/diuf/sudoku/solver/rules/ThreeStrongLinksHint.html b/diuf/sudoku/solver/rules/ThreeStrongLinksHint.html new file mode 100644 index 0000000..1a2cb8b --- /dev/null +++ b/diuf/sudoku/solver/rules/ThreeStrongLinksHint.html @@ -0,0 +1,23 @@ + + +

    {0}

    +

    + This technique relies on Conjugate Pairs. + The value {1} in {8}, {9} and {10} forms + 3 strong links (conjugate pairs). The 1st set of bridge cells + {3} and {4} share the same region ({11}), + therefore forming a weak link. The 2nd set of bridge cells + {5} and {6} share the same region ({12}) which + means that they also form a weak link. The 3 strong links therfore + are joined with these weak links. +

    +

    + Any occurrence of the value {1} can therefore be + removed from any cells sharing a row, column or block + with both start cells {2} and {7}. +

    +

    + This technique is similar to a 2x2x2 Finned Sashimi Swordfish technique logic. +

    + + \ No newline at end of file diff --git a/diuf/sudoku/solver/rules/ThreeStrongLinksHint.java b/diuf/sudoku/solver/rules/ThreeStrongLinksHint.java new file mode 100644 index 0000000..71b0761 --- /dev/null +++ b/diuf/sudoku/solver/rules/ThreeStrongLinksHint.java @@ -0,0 +1,395 @@ +package diuf.sudoku.solver.rules; + +import java.util.*; +import diuf.sudoku.*; +import diuf.sudoku.solver.*; +import diuf.sudoku.solver.rules.chaining.*; +import diuf.sudoku.tools.*; + + +/** + * 3-Turbot Fish hints + */ +public class ThreeStrongLinksHint extends IndirectHint implements Rule, HasParentPotentialHint { + + private final int value; + private final int baseLinkType1; + private final int baseLinkType2; + private final int baseLinkType3; + private final Cell startCell; + private final Cell bridgeCell11; + private final Cell bridgeCell12; + private final Cell bridgeCell21; + private final Cell bridgeCell22; + private final Cell endCell; + private final Grid.Region baseLink1Set; + private final Grid.Region baseLink2Set; + private final Grid.Region baseLink3Set; + private final Grid.Region shareRegion1; + private final Grid.Region shareRegion2; + + public ThreeStrongLinksHint(IndirectHintProducer rule, Map removablePotentials, + Cell startCell, Cell bridgeCell11, Cell bridgeCell12, + int value, Grid.Region baseLink1Set, Grid.Region baseLink2Set, Grid.Region shareRegion1, + Cell bridgeCell21, Cell bridgeCell22, Cell endCell, Grid.Region baseLink3Set, Grid.Region shareRegion2, int baseLinkType1, int baseLinkType2, int baseLinkType3) { + super(rule, removablePotentials); + this.value = value; + this.baseLinkType1 = baseLinkType1; + this.baseLinkType2 = baseLinkType2; + this.baseLinkType3 = baseLinkType3; + this.startCell = startCell; + this.bridgeCell11 = bridgeCell11; + this.bridgeCell12 = bridgeCell12; + this.bridgeCell21 = bridgeCell21; + this.bridgeCell22 = bridgeCell22; + this.endCell = endCell; + this.baseLink1Set = baseLink1Set; + this.baseLink2Set = baseLink2Set; + this.baseLink3Set = baseLink3Set; + this.shareRegion1 = shareRegion1; + this.shareRegion2 = shareRegion2; + } + + @Override + public int getViewCount() { + return 1; + } + + @Override + public Cell[] getSelectedCells() { + return new Cell[] { startCell, endCell }; + } + + @Override + public Map getGreenPotentials(Grid grid, int viewNum) { + Map result = new HashMap<>(); + BitSet fishDigitSet = SingletonBitSet.create(value); + result.put(startCell, fishDigitSet); // orange + result.put(bridgeCell11, fishDigitSet); + result.put(bridgeCell12, fishDigitSet); // orange + result.put(bridgeCell21, fishDigitSet); + result.put(bridgeCell22, fishDigitSet); // orange + result.put(endCell, fishDigitSet); + return result; + } + + @Override + public Map getRedPotentials(Grid grid, int viewNum) { + Map result = new HashMap<>(super.getRemovablePotentials()); + BitSet fishDigitSet = SingletonBitSet.create(value); + result.put(startCell, fishDigitSet); + result.put(bridgeCell12, fishDigitSet); + result.put(bridgeCell22, fishDigitSet); + return result; + } + + @Override + public Collection getLinks(Grid grid, int viewNum) { + Collection result = new ArrayList<>(); + result.add(new Link(startCell, value, bridgeCell11, value)); + //result.add(new Link(bridgeCell11, value, bridgeCell12, value)); + result.add(new Link(bridgeCell12, value, bridgeCell21, value)); + //result.add(new Link(bridgeCell21, value, bridgeCell22, value)); + result.add(new Link(bridgeCell22, value, endCell, value)); + return result; + } + + @Override + public Grid.Region[] getRegions() { + //return new Grid.Region[] { shareRegion1 }; + return null; + } + + @Override + public String toString() { + return getName() + + ": " + + Cell.toFullString(startCell, bridgeCell11, bridgeCell12, bridgeCell21, bridgeCell22, endCell) + + " on value " + + value; + } + + @Override + public String toHtml(Grid grid) { + String result = HtmlLoader.loadHtml(this, "ThreeStrongLinksHint.html"); + String name = getName(); + String baseLink1Name = this.baseLink1Set.toFullString(); + String baseLink2Name = this.baseLink2Set.toFullString(); + String baseLink3Name = this.baseLink3Set.toFullString(); + String shared1 = this.shareRegion1.toFullString(); + String shared2 = this.shareRegion2.toFullString(); + String value = Integer.toString(this.value); + String cell1 = startCell.toString(); + String cell2 = bridgeCell11.toString(); + String cell3 = bridgeCell12.toString(); + String cell4 = bridgeCell21.toString(); + String cell5 = bridgeCell22.toString(); + String cell6 = endCell.toString(); + result = HtmlLoader.format(result, name, value, cell1, cell2, cell3, cell4, cell5,cell6, baseLink1Name, baseLink2Name, baseLink3Name, shared1, shared2); + return result; + } + + private String getSuffix() { + String nameSuffix; + if (baseLinkType1 > 0) { + nameSuffix = "1"; + } + else { + nameSuffix = "0"; + } + if ((baseLinkType2 > 0) && (baseLinkType1 == baseLinkType2 || nameSuffix == "0")) { + nameSuffix +="1"; + } + else { + if (baseLinkType2 > 0) { + nameSuffix +="2"; + } + else { + nameSuffix +="0"; + } + } + if ((baseLinkType3 > 0) && (nameSuffix.contains("00") || baseLinkType1 == baseLinkType3 || (baseLinkType2 != baseLinkType3 && nameSuffix.indexOf("2") == 1) || (baseLinkType2 == baseLinkType3 && nameSuffix.indexOf("1") == 1))) { + nameSuffix +="1"; + } + else { + if (baseLinkType3 > 0) { + nameSuffix +="2"; + } + else { + nameSuffix +="0"; + } + } + if (nameSuffix.contains("122")) + return "112"; + if (nameSuffix.contains("120")) + return "012"; + return nameSuffix; + } + + + @Override + public String getName() { + Class region1 = baseLink1Set.getClass(); + Class region2 = baseLink2Set.getClass(); + Class region3 = baseLink3Set.getClass(); + String suffix = getSuffix(); + if (region1 == Grid.Row.class) { + if (region2 == Grid.Row.class) { + if (region3 == Grid.Row.class) { + return "3 Skyscrapers" + " " + suffix; + } + else { + if (region3 == Grid.Column.class) { + return "3-String Kite" + " " + suffix; + } + else { + return "3-Turbot Fish" + " " + suffix; + } + } + } + else { + if (region2 == Grid.Column.class) { + return "3-String Kite" + " " + suffix; + } + else { + if (region3 == Grid.Column.class) { + return "3-String Kite" + " " + suffix; + } + else { + return "3-Turbot Fish" + " " + suffix; + } + } + } + } + if (region1 == Grid.Column.class) { + if (region2 == Grid.Column.class) { + if (region3 == Grid.Column.class) { + return "3 Skyscrapers" + " " + suffix; + } + else { + if (region3 == Grid.Row.class) { + return "3-String Kite" + " " + suffix; + } + else { + return "3-Turbot Fish" + " " + suffix; + } + } + } + else { + if (region2 == Grid.Row.class) { + return "3-String Kite" + " " + suffix; + } + else { + if (region3 == Grid.Row.class) { + return "3-String Kite" + " " + suffix; + } + else { + return "3-Turbot Fish" + " " + suffix; + } + } + } + } + if (region1 == Grid.Block.class) { + if (region2 == Grid.Row.class) { + if (region3 == Grid.Column.class) { + return "3-String Kite" + " " + suffix; + } + else { + return "3-Turbot Fish" + " " + suffix; + } + } + if (region2 == Grid.Column.class) { + if (region3 == Grid.Row.class) { + return "3-String Kite" + " " + suffix; + } + else { + return "3-Turbot Fish" + " " + suffix; + } + } + if (region2 == Grid.Block.class) { + return "3-Turbot Fish" + " " + suffix; + } + } + return "3-Turbot Fish" + " " + suffix; + } + + @Override + public String getShortName() { + Class region1 = baseLink1Set.getClass(); + Class region2 = baseLink2Set.getClass(); + Class region3 = baseLink3Set.getClass(); + String suffix = getSuffix(); + if (region1 == Grid.Row.class) { + if (region2 == Grid.Row.class) { + if (region3 == Grid.Row.class) { + return "3Sk" + " " + suffix; + } + else { + if (region3 == Grid.Column.class) { + return "3SK" + " " + suffix; + } + else { + return "3TF" + " " + suffix; + } + } + } + else { + if (region2 == Grid.Column.class) { + return "3SK" + " " + suffix; + } + else { + if (region3 == Grid.Column.class) { + return "3SK" + " " + suffix; + } + else { + return "3TF" + " " + suffix; + } + } + } + } + if (region1 == Grid.Column.class) { + if (region2 == Grid.Column.class) { + if (region3 == Grid.Column.class) { + return "3Sk" + " " + suffix; + } + else { + if (region3 == Grid.Row.class) { + return "3SK" + " " + suffix; + } + else { + return "3TF" + " " + suffix; + } + } + } + else { + if (region2 == Grid.Row.class) { + return "3SK" + " " + suffix; + } + else { + if (region3 == Grid.Row.class) { + return "3SK" + " " + suffix; + } + else { + return "3TF" + " " + suffix; + } + } + } + } + if (region1 == Grid.Block.class) { + if (region2 == Grid.Row.class) { + if (region3 == Grid.Column.class) { + return "3SK" + " " + suffix; + } + else { + return "3TF" + " " + suffix; + } + } + if (region2 == Grid.Column.class) { + if (region3 == Grid.Row.class) { + return "3SK" + " " + suffix; + } + else { + return "3TF" + " " + suffix; + } + } + if (region2 == Grid.Block.class) { + return "3TF" + " " + suffix; + } + } + return "3TF" + " " + suffix; + } + + + @Override + public double getDifficulty() { + String name = getName(); + if (name.contains("Skyscraper")) { + return 5.4; + } else if (name.contains("3-String Kite")) { + return 5.6; + } else { + return 5.5; + } + } + + @Override + public int hashCode() { + return startCell.hashCode() ^ endCell.hashCode() ^ + bridgeCell11.hashCode() ^ bridgeCell12.hashCode() ^ value; + } + + + public String getClueHtml(Grid grid, boolean isBig) { + if (isBig) { + return "Look for a " + getName() + " on the value " + value; + } else { + return "Look for a " + getName(); + } + } + + + + @Override + public Collection getRuleParents(Grid initialGrid, Grid currentGrid) { + Collection result = new ArrayList<>(); + Cell startCell = Grid.getCell(this.startCell.getIndex()); + Cell endCell = Grid.getCell(this.endCell.getIndex()); + Cell bridgeCell11 = Grid.getCell(this.bridgeCell11.getIndex()); + Cell bridgeCell12 = Grid.getCell(this.bridgeCell12.getIndex()); + Cell bridgeCell21 = Grid.getCell(this.bridgeCell21.getIndex()); + Cell bridgeCell22 = Grid.getCell(this.bridgeCell22.getIndex()); + if (initialGrid.hasCellPotentialValue(startCell.getIndex(), value) && !initialGrid.hasCellPotentialValue(this.startCell.getIndex(), value)) + result.add(new Potential(this.startCell, value, false)); + if (initialGrid.hasCellPotentialValue(bridgeCell11.getIndex(), value) && !initialGrid.hasCellPotentialValue(this.bridgeCell11.getIndex(), value)) + result.add(new Potential(this.bridgeCell11, value, false)); + if (initialGrid.hasCellPotentialValue(bridgeCell12.getIndex(), value) && !initialGrid.hasCellPotentialValue(this.bridgeCell12.getIndex(), value)) + result.add(new Potential(this.bridgeCell12, value, false)); + if (initialGrid.hasCellPotentialValue(bridgeCell21.getIndex(), value) && !initialGrid.hasCellPotentialValue(this.bridgeCell21.getIndex(), value)) + result.add(new Potential(this.bridgeCell21, value, false)); + if (initialGrid.hasCellPotentialValue(bridgeCell22.getIndex(), value) && !initialGrid.hasCellPotentialValue(this.bridgeCell22.getIndex(), value)) + result.add(new Potential(this.bridgeCell22, value, false)); + if (initialGrid.hasCellPotentialValue(endCell.getIndex(), value) && !initialGrid.hasCellPotentialValue(this.endCell.getIndex(), value)) + result.add(new Potential(this.endCell, value, false)); + return result; + } +} diff --git a/diuf/sudoku/solver/rules/TurbotFish.java b/diuf/sudoku/solver/rules/TurbotFish.java new file mode 100644 index 0000000..99a449e --- /dev/null +++ b/diuf/sudoku/solver/rules/TurbotFish.java @@ -0,0 +1,130 @@ +package diuf.sudoku.solver.rules; + +import java.util.*; +import diuf.sudoku.*; +import diuf.sudoku.solver.*; +import diuf.sudoku.tools.*; + + +/** + * Implementation of Turbot Fish technique solver. + */ +public class TurbotFish implements IndirectHintProducer { + + @Override + public void getHints(Grid grid, HintsAccumulator accu) throws InterruptedException { + // Skyscrapers + getHints(grid, accu, 1, 1); + getHints(grid, accu, 2, 2); + // Two-string Kites + getHints(grid, accu, 2, 1); + //getHints(grid, accu, 1, 2);The same + // Turbot Fishes + getHints(grid, accu, 1, 0); + getHints(grid, accu, 2, 0); + //getHints(grid, accu, 0, 1);The same + //getHints(grid, accu, 0, 2);The same + // Generalized X-Wing... + //getHints(grid, accu, 0, 0); + } + + private Grid.Region shareRegionOf(Grid grid, + Cell start, Cell bridge1, Cell bridge2, Cell end) { + if (bridge1.getX() == bridge2.getX()) { + return Grid.columns[bridge1.getX()]; + } else if (bridge1.getY() == bridge2.getY()) { + return Grid.rows[bridge1.getY()]; + } else if (bridge1.getB() == bridge2.getB()) { + return Grid.blocks[bridge1.getB()]; + } else return null; + } + + private void getHints(Grid grid, HintsAccumulator accu, + int base, int cover) + throws InterruptedException { + for (int digit = 1; digit <= 9; digit++) { + Grid.Region[] baseRegions = grid.getRegions(base); + Grid.Region[] coverRegions = grid.getRegions(cover); + for (int i1 = 0; i1 < baseRegions.length; i1++) { + for (int i2 = (base == cover ? i1+1 : 0); i2 < coverRegions.length; i2++) { + // For each set in sets + Grid.Region baseRegion = baseRegions[i1]; + Grid.Region coverRegion = coverRegions[i2]; + + // Same region, skip + //if (baseRegion.getClass() == coverRegion.getClass() && i1 == i2) + //continue; + BitSet baseRegionPotentials = baseRegion.getPotentialPositions(grid, digit); + BitSet coverRegionPotentials = coverRegion.getPotentialPositions(grid, digit); + if (baseRegionPotentials.cardinality() == 2 && coverRegionPotentials.cardinality() == 2) { + // Strong links found (Conjugate pairs found) + // Check whether positions may in the same region or not (form a weak link) + int p1, p2; + Cell[] cells = new Cell[] { + // region 1 + baseRegion.getCell(p1 = baseRegionPotentials.nextSetBit(0)), + baseRegion.getCell(baseRegionPotentials.nextSetBit(p1 + 1)), + // region 2 + coverRegion.getCell(p2 = coverRegionPotentials.nextSetBit(0)), + coverRegion.getCell(coverRegionPotentials.nextSetBit(p2 + 1)) + }; + + // Cells cannot be same + boolean next = false; + for (int i = 0; i < 3; i++) { + for (int j = i + 1; j < 4; j++) { + if (cells[i].equals(cells[j])) { + next = true; + break; + } + } + } + if (next) continue; + + Grid.Region shareRegion; + Cell start, end, bridge1, bridge2; + for (int i = 0; i < 2; i++) { + for (int j = 2; j < 4; j++) { + if ((shareRegion = shareRegionOf(grid, + start = cells[i], + bridge1 = cells[1 - i], + bridge2 = cells[j], + end = cells[5 - j])) != null && + !shareRegion.equals(baseRegion) && !shareRegion.equals(coverRegion)) { + // Turbot fish found + TurbotFishHint hint = createHint(grid, digit, start, end, bridge1, bridge2, + baseRegion, coverRegion, shareRegion); + if (hint.isWorth()) + accu.add(hint); + } + } // for int j = 0..2 + } // for int i = 0..2 + } // if baseRegionPotentials.cardinality() == 2 && coverRegionPotentials.cardinality() == 2 + } + } + } + } + + private TurbotFishHint createHint(Grid grid, int value, Cell start, Cell end, Cell bridgeCell1, Cell bridgeCell2, + Grid.Region baseSet, Grid.Region coverSet, Grid.Region shareRegion) { + // Build list of removable potentials + Map removablePotentials = new HashMap<>(); + Set victims = new LinkedHashSet<>(start.getVisibleCells()); + victims.retainAll(end.getVisibleCells()); + victims.remove(start); + victims.remove(end); + for (Cell cell : victims) { + if (grid.hasCellPotentialValue(cell.getIndex(), value)) + removablePotentials.put(cell, SingletonBitSet.create(value)); + } + + // Create hint + return new TurbotFishHint(this, removablePotentials, + start, end, bridgeCell1, bridgeCell2, value, baseSet, coverSet, shareRegion); + } + + @Override + public String toString() { + return "Turbot Fishes"; + } +} diff --git a/diuf/sudoku/solver/rules/TurbotFishHint.html b/diuf/sudoku/solver/rules/TurbotFishHint.html new file mode 100644 index 0000000..27e6db8 --- /dev/null +++ b/diuf/sudoku/solver/rules/TurbotFishHint.html @@ -0,0 +1,20 @@ + + +

    {0}

    +

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

    +

    + Any occurrence of the value {1} can therefore be + removed from any cells sharing a row, column or block + with both start cells {2} and {5}. +

    +

    + This technique is similar to a 2x2 Finned Sashimi X-Wing technique logic. +

    + + \ No newline at end of file diff --git a/diuf/sudoku/solver/rules/TurbotFishHint.java b/diuf/sudoku/solver/rules/TurbotFishHint.java new file mode 100644 index 0000000..2efcd2e --- /dev/null +++ b/diuf/sudoku/solver/rules/TurbotFishHint.java @@ -0,0 +1,211 @@ +package diuf.sudoku.solver.rules; + +import java.util.*; +import diuf.sudoku.*; +import diuf.sudoku.solver.*; +import diuf.sudoku.solver.rules.chaining.*; +import diuf.sudoku.tools.*; + + +/** + * Turbot Fish hints + */ +public class TurbotFishHint extends IndirectHint implements Rule, HasParentPotentialHint { + + private final int value; + private final Cell startCell; + private final Cell endCell; + private final Cell bridgeCell1; + private final Cell bridgeCell2; + private final Grid.Region baseSet; + private final Grid.Region coverSet; + private final Grid.Region shareRegion; + + public TurbotFishHint(IndirectHintProducer rule, Map removablePotentials, + Cell startCell, Cell endCell, Cell bridgeCell1, Cell bridgeCell2, + int value, Grid.Region base, Grid.Region cover, Grid.Region shareRegion) { + super(rule, removablePotentials); + this.value = value; + this.startCell = startCell; + this.endCell = endCell; + this.bridgeCell1 = bridgeCell1; + this.bridgeCell2 = bridgeCell2; + this.baseSet = base; + this.coverSet = cover; + this.shareRegion = shareRegion; + } + + @Override + public int getViewCount() { + return 1; + } + + @Override + public Cell[] getSelectedCells() { + return new Cell[] { startCell, endCell }; + } + + @Override + public Map getGreenPotentials(Grid grid, int viewNum) { + Map result = new HashMap<>(); + BitSet fishDigitSet = SingletonBitSet.create(value); + result.put(startCell, fishDigitSet); // orange + result.put(bridgeCell1, fishDigitSet); + result.put(bridgeCell2, fishDigitSet); // orange + result.put(endCell, fishDigitSet); + return result; + } + + @Override + public Map getRedPotentials(Grid grid, int viewNum) { + Map result = new HashMap<>(super.getRemovablePotentials()); + BitSet fishDigitSet = SingletonBitSet.create(value); + result.put(startCell, fishDigitSet); + result.put(bridgeCell2, fishDigitSet); + return result; + } + + @Override + public Collection getLinks(Grid grid, int viewNum) { + Collection result = new ArrayList<>(); + result.add(new Link(startCell, value, bridgeCell1, value)); + result.add(new Link(bridgeCell1, value, bridgeCell2, value)); + result.add(new Link(bridgeCell2, value, endCell, value)); + return result; + } + + @Override + public Grid.Region[] getRegions() { + //return new Grid.Region[] { shareRegion }; + return null; + } + + @Override + public String toString() { + return getName() + + ": " + + Cell.toFullString(startCell, bridgeCell1, bridgeCell2, endCell) + + " on value " + + value; + } + + @Override + public String toHtml(Grid grid) { + String result = HtmlLoader.loadHtml(this, "TurbotFishHint.html"); + String name = getName(); + String base = this.baseSet.toFullString(); + String cover = this.coverSet.toFullString(); + String shared = this.shareRegion.toFullString(); + String value = Integer.toString(this.value); + String cell1 = startCell.toString(); + String cell2 = bridgeCell1.toString(); + String cell3 = bridgeCell2.toString(); + String cell4 = endCell.toString(); + result = HtmlLoader.format(result, name, value, cell1, cell2, cell3, cell4, base, cover, shared); + return result; + } + + @Override + public String getName() { + Class region1 = baseSet.getClass(); + Class region2 = coverSet.getClass(); + if (region1 == Grid.Row.class) { + if (region2 == Grid.Row.class) + return "Skyscraper"; + else + if (region2 == Grid.Column.class) + return "Two-string Kite"; + else + return "Turbot Fish"; + } + else { + if (region1 == Grid.Column.class) + if (region2 == Grid.Row.class) + return "Two-string Kite"; + else + if (region2 == Grid.Column.class) + return "Skyscraper"; + else + return "Turbot Fish"; + else + return "Turbot Fish"; + } + } + + @Override + public String getShortName() { + Class region1 = baseSet.getClass(); + Class region2 = coverSet.getClass(); + if (region1 == Grid.Row.class) { + if (region2 == Grid.Row.class) + return "Sky"; + else + if (region2 == Grid.Column.class) + return "2SK"; + else + return "TF"; + } + else { + if (region1 == Grid.Column.class) + if (region2 == Grid.Row.class) + return "2SK"; + else + if (region2 == Grid.Column.class) + return "Sky"; + else + return "TF"; + else + if (region2 == Grid.Block.class) + return "GXW"; + else + return "TF"; + } + } + + @Override + public double getDifficulty() { + String name = getName(); + if (name.equals("Skyscraper")) { + return 4.0; + } else if (name.equals("Two-string Kite")) { + return 4.1; + } else { + return 4.2; + } + } + + @Override + public int hashCode() { + return startCell.hashCode() ^ endCell.hashCode() ^ + bridgeCell1.hashCode() ^ bridgeCell2.hashCode() ^ value; + } + + + public String getClueHtml(Grid grid, boolean isBig) { + if (isBig) { + return "Look for a " + getName() + " on the value " + value; + } else { + return "Look for a " + getName(); + } + } + + + + @Override + public Collection getRuleParents(Grid initialGrid, Grid currentGrid) { + Collection result = new ArrayList<>(); + Cell startCell = Grid.getCell(this.startCell.getIndex()); + Cell endCell = Grid.getCell(this.endCell.getIndex()); + Cell bridgeCell1 = Grid.getCell(this.bridgeCell1.getIndex()); + Cell bridgeCell2 = Grid.getCell(this.bridgeCell2.getIndex()); + if (initialGrid.hasCellPotentialValue(startCell.getIndex(), value) && !initialGrid.hasCellPotentialValue(this.startCell.getIndex(), value)) + result.add(new Potential(this.startCell, value, false)); + if (initialGrid.hasCellPotentialValue(bridgeCell1.getIndex(), value) && !initialGrid.hasCellPotentialValue(this.bridgeCell1.getIndex(), value)) + result.add(new Potential(this.bridgeCell1, value, false)); + if (initialGrid.hasCellPotentialValue(bridgeCell2.getIndex(), value) && !initialGrid.hasCellPotentialValue(this.bridgeCell2.getIndex(), value)) + result.add(new Potential(this.bridgeCell2, value, false)); + if (initialGrid.hasCellPotentialValue(endCell.getIndex(), value) && !initialGrid.hasCellPotentialValue(this.endCell.getIndex(), value)) + result.add(new Potential(this.endCell, value, false)); + return result; + } +} diff --git a/diuf/sudoku/solver/rules/VWXYZWing.java b/diuf/sudoku/solver/rules/VWXYZWing.java index 21cc29c..b4855e5 100644 --- a/diuf/sudoku/solver/rules/VWXYZWing.java +++ b/diuf/sudoku/solver/rules/VWXYZWing.java @@ -25,19 +25,19 @@ public class VWXYZWing implements IndirectHintProducer { private boolean isVWXYZWing(BitSet vwxyzValues,BitSet vzValues, BitSet wzValues, BitSet xzValues, BitSet aBit, Cell yzCell, Cell xzCell, Cell wzCell, Cell vzCell, Cell vwxyzCell) { BitSet inter = (BitSet)aBit.clone(); inter.and(xzValues); - if (inter.cardinality() == 1 && !(yzCell.getX() == xzCell.getX() || yzCell.getY() == xzCell.getY() || yzCell.getB() == xzCell.getB())) + if (inter.cardinality() == 1 && !yzCell.canSeeCell(xzCell)) return false; inter = (BitSet)aBit.clone(); inter.and(wzValues); - if (inter.cardinality() == 1 && !(yzCell.getX() == wzCell.getX() || yzCell.getY() == wzCell.getY() || yzCell.getB() == wzCell.getB())) + if (inter.cardinality() == 1 && !yzCell.canSeeCell(wzCell)) return false; inter = (BitSet)aBit.clone(); inter.and(vzValues); - if (inter.cardinality() == 1 && !(yzCell.getX() == vzCell.getX() || yzCell.getY() == vzCell.getY() || yzCell.getB() == vzCell.getB())) + if (inter.cardinality() == 1 && !yzCell.canSeeCell(vzCell)) return false; inter = (BitSet)aBit.clone(); inter.and(vwxyzValues); - if (inter.cardinality() == 1 && !(yzCell.getX() == vwxyzCell.getX() || yzCell.getY() == vwxyzCell.getY() || yzCell.getB() == vwxyzCell.getB())) + if (inter.cardinality() == 1 && !yzCell.canSeeCell(vwxyzCell)) return false; return true; } @@ -103,13 +103,10 @@ public void getHints(Grid grid, HintsAccumulator accu) throws InterruptedExcepti biggestCardinality4 = xzValues.cardinality(); wingSize = vwxyzValues.cardinality() + vzValues.cardinality() + wzValues.cardinality() + xzValues.cardinality(); //Restrict potential yzCell to Grid Cells that are visible by one or more of the other cells - CellSet noYZ = new CellSet(new int[]{0,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,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80}); - noYZ.removeAll(vwxyzCell.getVisibleCells()); - noYZ.removeAll(vzCell.getVisibleCells()); - noYZ.removeAll(wzCell.getVisibleCells()); - noYZ.removeAll(xzCell.getVisibleCells()); - CellSet yzCellRange = new CellSet(new int[]{0,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,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80}); - yzCellRange.removeAll(noYZ); + CellSet yzCellRange = new CellSet(vwxyzCell.getVisibleCells()); + yzCellRange.addAll(vzCell.getVisibleCells()); + yzCellRange.addAll(wzCell.getVisibleCells()); + yzCellRange.addAll(xzCell.getVisibleCells()); yzCellRange.remove(vwxyzCell); yzCellRange.remove(vzCell); yzCellRange.remove(wzCell); diff --git a/diuf/sudoku/solver/rules/WXYZWing.java b/diuf/sudoku/solver/rules/WXYZWing.java index 7ea8188..47d4082 100644 --- a/diuf/sudoku/solver/rules/WXYZWing.java +++ b/diuf/sudoku/solver/rules/WXYZWing.java @@ -25,15 +25,15 @@ public class WXYZWing implements IndirectHintProducer { private boolean isWXYZWing(BitSet wxyzValues,BitSet wzValues, BitSet xzValues, BitSet aBit, Cell yzCell, Cell xzCell, Cell wzCell, Cell wxyzCell) { BitSet inter = (BitSet)aBit.clone(); inter.and(xzValues); - if (inter.cardinality() == 1 && !(yzCell.getX() == xzCell.getX() || yzCell.getY() == xzCell.getY() || yzCell.getB() == xzCell.getB())) + if (inter.cardinality() == 1 && !yzCell.canSeeCell(xzCell)) return false; inter = (BitSet)aBit.clone(); inter.and(wzValues); - if (inter.cardinality() == 1 && !(yzCell.getX() == wzCell.getX() || yzCell.getY() == wzCell.getY() || yzCell.getB() == wzCell.getB())) + if (inter.cardinality() == 1 && !yzCell.canSeeCell(wzCell)) return false; inter = (BitSet)aBit.clone(); inter.and(wxyzValues); - if (inter.cardinality() == 1 && !(yzCell.getX() == wxyzCell.getX() || yzCell.getY() == wxyzCell.getY() || yzCell.getB() == wxyzCell.getB())) + if (inter.cardinality() == 1 && !yzCell.canSeeCell(wxyzCell)) return false; return true; } @@ -81,12 +81,9 @@ public void getHints(Grid grid, HintsAccumulator accu) throws InterruptedExcepti biggestCardinality3 = xzValues.cardinality(); wingSize = wxyzValues.cardinality() + wzValues.cardinality() + xzValues.cardinality(); //Restrict potential yzCell to Grid Cells that are visible by one or more of the other cells - CellSet noYZ = new CellSet(new int[]{0,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,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80}); - noYZ.removeAll(wxyzCell.getVisibleCells()); - noYZ.removeAll(wzCell.getVisibleCells()); - noYZ.removeAll(xzCell.getVisibleCells()); - CellSet yzCellRange = new CellSet(new int[]{0,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,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80}); - yzCellRange.removeAll(noYZ); + CellSet yzCellRange = new CellSet(wxyzCell.getVisibleCells()); + yzCellRange.addAll(wzCell.getVisibleCells()); + yzCellRange.addAll(xzCell.getVisibleCells()); yzCellRange.remove(wxyzCell); yzCellRange.remove(wzCell); yzCellRange.remove(xzCell);