From 251aedbda99ef4afc4641d2b9b1bcc392336efcf Mon Sep 17 00:00:00 2001 From: "dobrichev@gmail.com" Date: Sat, 10 Aug 2019 14:49:35 +0300 Subject: [PATCH] +Pencilmark-only functionalities by 1to9only --- Make-Jar.jardesc | 54 ++--- diuf/sudoku/solver/Solver.java | 227 +++++++++++++++++- .../sudoku/solver/rules/AlignedExclusion.java | 3 +- diuf/sudoku/test/hints.java | 202 ++++++++++++++++ diuf/sudoku/tools/Twomutations.java | 102 ++++++++ 5 files changed, 549 insertions(+), 39 deletions(-) create mode 100644 diuf/sudoku/test/hints.java create mode 100644 diuf/sudoku/tools/Twomutations.java diff --git a/Make-Jar.jardesc b/Make-Jar.jardesc index a0e12cc..95e85cd 100644 --- a/Make-Jar.jardesc +++ b/Make-Jar.jardesc @@ -1,27 +1,27 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/diuf/sudoku/solver/Solver.java b/diuf/sudoku/solver/Solver.java index 57bbfb7..1159d6f 100644 --- a/diuf/sudoku/solver/Solver.java +++ b/diuf/sudoku/solver/Solver.java @@ -137,6 +137,7 @@ public Solver(Grid grid) { experimentalHintProducers = new ArrayList(); // Two levels of nesting !? addIfWorth(SolvingTechnique.NestedForcingChain, experimentalHintProducers, new Chaining(true, true, false, 4)); addIfWorth(SolvingTechnique.NestedForcingChain, experimentalHintProducers, new Chaining(true, true, false, 5)); + addIfWorth(SolvingTechnique.NestedForcingChain, experimentalHintProducers, new Chaining(true, true, false, 6)); } /** @@ -366,7 +367,7 @@ public int compare(Rule r1, Rule r2) { return -1; else if (d1 > d2) return 1; - else + else return r1.getName().compareTo(r2.getName()); } @@ -499,7 +500,7 @@ public void getDifficulty() { } catch (InterruptedException willHappen) {} Hint hint = accu.getHint(); if (hint == null) { - difficulty = 20.0; + difficulty = 20.0; break; } assert hint instanceof Rule; @@ -508,19 +509,19 @@ public void getDifficulty() { if (ruleDiff > difficulty) difficulty = ruleDiff; hint.apply(); - if (pearl == 0.0) { - if (diamond == 0.0) - diamond = difficulty; - if (hint.getCell() != null) { + if (pearl == 0.0) { + if (diamond == 0.0) + diamond = difficulty; + if (hint.getCell() != null) { if (want == 'd' && difficulty > diamond) { - difficulty = 20.0; + difficulty = 20.0; break; } - pearl = difficulty; - } + pearl = difficulty; + } } - else if (want != 0 && difficulty > pearl) { - difficulty = 20.0; + else if (want != 0 && difficulty > pearl) { + difficulty = 20.0; break; } } @@ -529,6 +530,210 @@ else if (want != 0 && difficulty > pearl) { } } +// public void getHintsHint() { +// Grid backup = new Grid(); +// grid.copyTo(backup); +// try { +// difficulty = Double.NEGATIVE_INFINITY; +// pearl = 0.0; +// diamond = 0.0; +// while (!isSolved()) { +// SingleHintAccumulator accu = new SingleHintAccumulator(); +// try { +// for (HintProducer producer : directHintProducers) +// producer.getHints(grid, accu); +// for (IndirectHintProducer producer : indirectHintProducers) +// producer.getHints(grid, accu); +// for (IndirectHintProducer producer : chainingHintProducers) +// producer.getHints(grid, accu); +// for (IndirectHintProducer producer : chainingHintProducers2) +// producer.getHints(grid, accu); +// for (IndirectHintProducer producer : advancedHintProducers) +// producer.getHints(grid, accu); +// for (IndirectHintProducer producer : experimentalHintProducers) +// producer.getHints(grid, accu); +// } catch (InterruptedException willHappen) {} +// Hint hint = accu.getHint(); +// if (hint == null) { +// difficulty = 20.0; +// break; +// } +// assert hint instanceof Rule; +// Rule rule = (Rule)hint; +// double ruleDiff = rule.getDifficulty(); +// if (ruleDiff > difficulty) +// difficulty = ruleDiff; +// hint.apply(); +// +// String s = ""; +// for (int i = 0; i < 81; i++) { +// int n = grid.getCellValue(i % 9, i / 9); +// s += (n==0)?".":n; +// } +// s += " "; +// int w = (int)((ruleDiff + 0.05) * 10); +// int p = w % 10; +// w /= 10; +// s += w + "." + p; +// s += ", " + hint.toString(); +// System.out.println(s); +// System.out.flush(); +// +// if (pearl == 0.0) { +// if (diamond == 0.0) +// diamond = difficulty; +// if (hint.getCell() != null) { +// if (want == 'd' && difficulty > diamond) { +// difficulty = 20.0; +// break; +// } +// pearl = difficulty; +// } +// } +// else if (want != 0 && difficulty > pearl) { +// difficulty = 20.0; +// break; +// } +// } +// } finally { +// backup.copyTo(grid); +// } +// } + public void getHintsHint() { + Grid backup = new Grid(); + grid.copyTo(backup); + try { + difficulty = Double.NEGATIVE_INFINITY; + pearl = 0.0; + diamond = 0.0; + while (!isSolved()) { + String s = ""; + + int crd = 1; + for (int i = 0; i < 81; i++) { + int n = grid.getCell(i % 9, i / 9).getPotentialValues().cardinality(); + if ( n > crd ) { crd = n; } + } + if ( crd > 1 ) + { + for (int i=0; i<3; i++ ) { + s = "+"; + for (int j=0; j<3; j++ ) { + for (int k=0; k<3; k++ ) { s += "-"; + for (int l=0; l difficulty) + difficulty = ruleDiff; + hint.apply(); + + s = ""; + for (int i = 0; i < 81; i++) { + int n = grid.getCellValue(i % 9, i / 9); + s += (n==0)?".":n; + } + s += " "; + int w = (int)((ruleDiff + 0.05) * 10); + int p = w % 10; + w /= 10; + s += w + "." + p; + s += ", " + hint.toString(); + System.out.println(s); + System.out.flush(); + + if (pearl == 0.0) { + if (diamond == 0.0) + diamond = difficulty; + if (hint.getCell() != null) { + if (want == 'd' && difficulty > diamond) { + difficulty = 20.0; + break; + } + pearl = difficulty; + } + } + else if (want != 0 && difficulty > pearl) { + difficulty = 20.0; + break; + } + } + } finally { + backup.copyTo(grid); + } + } + public Map toNamedList(Map rules) { Map hints = new LinkedHashMap(); for (Rule rule : rules.keySet()) { diff --git a/diuf/sudoku/solver/rules/AlignedExclusion.java b/diuf/sudoku/solver/rules/AlignedExclusion.java index 720bb17..e9db268 100644 --- a/diuf/sudoku/solver/rules/AlignedExclusion.java +++ b/diuf/sudoku/solver/rules/AlignedExclusion.java @@ -72,7 +72,8 @@ else if (exclCardinality >= 2 && exclCardinality <= degree) * continue the iteration on these remaining cells. */ // First iterate on the first two cells - Permutations cellSetPerm2 = new Permutations(2, candidateList.size()); + //Permutations cellSetPerm2 = new Permutations(2, candidateList.size()); + Twomutations cellSetPerm2 = new Twomutations(2, candidateList.size()); while (cellSetPerm2.hasNext()) { int[] indexes = cellSetPerm2.nextBitNums(); // Setup the first two cells diff --git a/diuf/sudoku/test/hints.java b/diuf/sudoku/test/hints.java new file mode 100644 index 0000000..ca848a5 --- /dev/null +++ b/diuf/sudoku/test/hints.java @@ -0,0 +1,202 @@ +/* + * Project: Sudoku Explainer + * Copyright (C) 2006-2009 Nicolas Juillerat + * Available under the terms of the Lesser General Public License (LGPL) + * this entry point by gsf @ www.sudoku.com/boards (The Player's Forum) + * this entry point by 1to9only @ http://forum.enjoysudoku.com/post280335.html#p280335 + */ +package diuf.sudoku.test; + +import java.io.*; +import java.util.*; + +import diuf.sudoku.*; +import diuf.sudoku.solver.*; + +public class hints { + static String FORMAT = "ED=%r/%p/%d"; + static String RELEASE = "2019-08-10"; + static String VERSION = "1.2.1.4"; + /** + * Solve input puzzles and print results according to the output format. + * @param args 729-char pemcilmarks + */ + public static void main(String[] args) { + String format = FORMAT; + String input = null; + String output = "-"; + String a; + String s; + String v; + String puzzle; + BufferedReader reader = null; + // PrintWriter writer = null; + int arg; + char c; + try { + for (arg = 0; arg < args.length; arg++) { + a = s = args[arg]; + if (s.charAt(0) != '-') + break; + v = null; + if (s.charAt(1) == '-') { + if (s.length() == 2) { + arg++; + break; + } + s = s.substring(2); + for (int i = 2; i < s.length(); i++) + if (s.charAt(i) == '=') { + v = s.substring(i+1); + s = s.substring(0, i); + } + if (s.equals("in") || s.equals("input")) + c = 'i'; + else if (s.equals("out") || s.equals("output")) + c = 'o'; + else + c = '?'; + } + else { + c = s.charAt(1); + if (s.length() > 2) + v = s.substring(2); + else if (++arg < args.length) + v = args[arg]; + } + switch (c) { + case 'i': + input = v; + break; + case 'o': + output = v; + break; + default: + break; + } + } + if (input != null) { + if (input.equals("-")) { + InputStreamReader reader0 = new InputStreamReader(System.in); + reader = new BufferedReader(reader0); + } + else { + Reader reader0 = new FileReader(input); + reader = new BufferedReader(reader0); + } + } + // if (output.equals("-")) { + // OutputStreamWriter writer0 = new OutputStreamWriter(System.out); + // BufferedWriter writer1 = new BufferedWriter(writer0); + // writer = new PrintWriter(writer1); + // } + // else { + // Writer writer0 = new FileWriter(output); + // BufferedWriter writer1 = new BufferedWriter(writer0); + // writer = new PrintWriter(writer1); + // } + for (;;) { + if (reader != null) { + puzzle = reader.readLine(); + if (puzzle == null) + break; + } + else if (arg < args.length) + puzzle = args[arg++]; + else + break; + if (puzzle.length() >= 81) { + Grid grid = new Grid(); + if (puzzle.length() >= 729) + { + for (int i = 0; i < 81; i++) { + grid.setCellValue(i % 9, i / 9, 0); + } + for (int i = 0; i < 729; i++) { + int cl = i / 9; // cell + char ch = puzzle.charAt(i); + + if (ch >= '1' && ch <= '9') { + int value = (ch - '0'); + Cell cell = grid.getCell(cl % 9, cl / 9); + cell.addPotentialValue(value); + } + } + } + else + { + for (int i = 0; i < 81; i++) { + char ch = puzzle.charAt(i); + if (ch >= '1' && ch <= '9') { + int value = (ch - '0'); + grid.setCellValue(i % 9, i / 9, value); + } + } + } + System.out.println(puzzle); System.out.flush(); + Solver solver = new Solver(grid); + solver.want = 0; + if (puzzle.length() >= 81 && puzzle.length() < 729) + { + solver.rebuildPotentialValues(); + } + try { + solver.getHintsHint(); + } catch (UnsupportedOperationException ex) { + solver.difficulty = solver.pearl = solver.diamond = 0.0; + } + s = ""; + for (int i = 0; i < format.length(); i++) { + int w; + int p; + char f = format.charAt(i); + if (f != '%' || ++i >= format.length()) + s += f; + else + switch (format.charAt(i)) { + case 'g': + s += puzzle; + break; + case 'r': + w = (int)((solver.difficulty + 0.05) * 10); + p = w % 10; + w /= 10; + s += w + "." + p; + break; + case 'p': + w = (int)((solver.pearl + 0.05) * 10); + p = w % 10; + w /= 10; + s += w + "." + p; + break; + case 'd': + w = (int)((solver.diamond + 0.05) * 10); + p = w % 10; + w /= 10; + s += w + "." + p; + break; + default: + s += f; + break; + } + } + System.out.println(s); + System.out.flush(); + } + } + } catch(FileNotFoundException ex) { + ex.printStackTrace(); + } catch (IOException ex) { + ex.printStackTrace(); + } finally { + try { + if (reader != null) + reader.close(); + // if (writer != null) + // writer.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } +} diff --git a/diuf/sudoku/tools/Twomutations.java b/diuf/sudoku/tools/Twomutations.java new file mode 100644 index 0000000..f66a5a8 --- /dev/null +++ b/diuf/sudoku/tools/Twomutations.java @@ -0,0 +1,102 @@ +package diuf.sudoku.tools; + +/** + * Generator of binary permutations. + *

+ * Given a length countBits and a + * degree countOnes with + * countOnes <= countBits, this class will compute + * all binary numbers of length countBits that have + * exactly countOnes bits equal to 1. + *

+ * The binary numbers are generated in increasing order. + *

+ * Example: with countBits = 5 and countOnes = 3 + * the following binary numbers are generated: + *

    + *
  • 00111 + *
  • 01011 + *
  • 01101 + *
  • 01110 + *
  • 10011 + *
  • 10101 + *
  • 10110 + *
  • 11001 + *
  • 11010 + *
  • 11100 + *
+ * Code adapted from "Hacker's Delight" by Henry S. Warren, + * ISBN 0-201-91465-4 + */ +public class Twomutations { + + private final int countBits; + private final int countOnes; + + private int n1; + private int n2; + + private boolean isLast; + + /** + * Create a new binary permutations generator. + *

+ * The maximum supported value for countBits + * is 64. countOnes must be equal or less than + * countBits. + * @param countOnes the number of bits equal to one + * @param countBits the length of the binary numbers in bits + */ + public Twomutations(int countOnes, int countBits) { + if (countOnes < 0) + throw new IllegalArgumentException("countOnes < 0"); + if (countBits < 0) + throw new IllegalArgumentException("countBits < 0"); + if (countOnes > countBits) + throw new IllegalArgumentException("countOnes > countBits"); + if (countBits > 81) + throw new IllegalArgumentException("countBits > 81"); + this.countBits = countBits; + this.countOnes = countOnes; + this.n1 = 0; + this.n2 = 1; + this.isLast = (countBits == n2); + } + + /** + * Test if there are more permutations available + * @return whether there are more permutations available + */ + public boolean hasNext() { + return !isLast; + } + + /** + * Get the next binary permutation. + * @return the next binary permutation + */ + public void next() { + n1++; + if ( n1 == n2 ) + { + n1 = 0; + n2++; + } + isLast = (countBits == n2); + } + + /** + * Get the next binary permutation as an array + * of bit indexes. + * @return the 0-based indexes of the bits that are set + * to one. + */ + public int[] nextBitNums() { + int[] result = new int[countOnes]; + result[ 0] = n1; + result[ 1] = n2; + next(); + return result; + } + +} \ No newline at end of file