diff --git a/README.md b/README.md index bd49c5d..fd56298 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,33 @@ heap size at startup. Change log ========== +Version 2.2.7 (2019-11-23) +-------------------------- + +Features: + + - The active cell selector has been made larger then the rest of the cell selection to distinguish it. + + - Cell selection is more responsive. Immediate selection on down event. + + - German translation support has been added. + +Bug Fixes: + + - Active cell selector position now updates correctly on mouse drag. + + - "Solve up to" had a bug where it would go in an endless loop on a puzle where the engine gives-up. + + - Cell selection bug when removing existing selection from previous selection. + + - Solve Puzzle now throws an appropriate message when the puzzle has multiple solutions. + + - Right click no longer clears the selection. + + - An empty config file is deleted and re-generated. + + - Internal code redesigning and refactoring. + Version 2.2.6 (2019-11-22) -------------------------- diff --git a/ToDo.txt b/ToDo.txt index 4fa87c1..6864764 100644 --- a/ToDo.txt +++ b/ToDo.txt @@ -1,16 +1,5 @@ -Crash: - -Load this puzzle into Hodoku and press "Solve up to" -600270490009080030730006002047308065300400079900007000003000907072560300004702050 - -20191117: - -I dislike having candidate highlight when pressing LCtrl to select cells. There should be an option to disable it. - Bugs: - Right click removed selection - Right click should not perform a selection Single Click Mode right click commits without showing menu 20191116: diff --git a/src/sudoku/Main.java b/src/sudoku/Main.java index 78c2743..472230b 100644 --- a/src/sudoku/Main.java +++ b/src/sudoku/Main.java @@ -76,20 +76,22 @@ public String getSrcDir() { } void searchForType(List typeList, DifficultyLevel level, String outFile) { - // Logger.getLogger(getClass().getName()).log(Level.INFO, "Starting search for " - // + type.getStepName()); + System.out.println("Starting search for:"); if (typeList.size() > 0) { for (StepType tmpType : typeList) { System.out.println(" " + tmpType); } } + if (level != null) { System.out.println(" " + level.getName()); } + SearchForTypeThread thread = new SearchForTypeThread(typeList, level, outFile); thread.start(); BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + try { // 20120112: Pressing makes readLine() return null // which leads to a NullPointerException @@ -100,31 +102,74 @@ void searchForType(List typeList, DifficultyLevel level, String outFil break; } } + } catch (IOException ex) { System.out.println("Error reading from console"); } + thread.interrupt(); + try { thread.join(); } catch (InterruptedException ex) { System.out.println("Interrupted waiting for search thread"); } + System.out.println("Gesamt: " + thread.getAnz() + " Sudoku erzeugt (" + thread.getAnzFound() + " Treffer)"); } - public void batchSolve(String fileName, String puzzleString, boolean printSolution, boolean printSolutionPath, - boolean printStatistic, ClipboardMode cMode, Set types, String outFile, + public void batchSolve( + String fileName, + String puzzleString, + boolean printSolution, + boolean printSolutionPath, + boolean printStatistic, + ClipboardMode cMode, + Set types, + String outFile, boolean findAllSteps) { - batchSolve(fileName, puzzleString, printSolution, printSolutionPath, printStatistic, cMode, types, outFile, - findAllSteps, false, null); + + batchSolve( + fileName, + puzzleString, + printSolution, + printSolutionPath, + printStatistic, + cMode, + types, + outFile, + findAllSteps, + false, + null + ); } - public void batchSolve(String fileName, String puzzleString, boolean printSolution, boolean printSolutionPath, - boolean printStatistic, ClipboardMode cMode, Set types, String outFile, boolean findAllSteps, - boolean bruteForceTest, List testTypes) { - - BatchSolveThread thread = new BatchSolveThread(fileName, puzzleString, printSolution, printSolutionPath, - printStatistic, cMode, types, outFile, findAllSteps, bruteForceTest, testTypes); + public void batchSolve( + String fileName, + String puzzleString, + boolean printSolution, + boolean printSolutionPath, + boolean printStatistic, + ClipboardMode cMode, + Set types, + String outFile, + boolean findAllSteps, + boolean bruteForceTest, + List testTypes) { + + BatchSolveThread thread = new BatchSolveThread( + fileName, + puzzleString, + printSolution, + printSolutionPath, + printStatistic, + cMode, + types, + outFile, + findAllSteps, + bruteForceTest, + testTypes + ); thread.start(); ShutDownThread st = new ShutDownThread(thread); @@ -138,8 +183,7 @@ public void batchSolve(String fileName, String puzzleString, boolean printSoluti try { Runtime.getRuntime().removeShutdownHook(st); - } catch (Exception ex) { - } + } catch (Exception ex) {} int min = (int) (thread.getTicks() / 60000); int sec = (int) (thread.getTicks() % 60000); @@ -149,9 +193,13 @@ public void batchSolve(String fileName, String puzzleString, boolean printSoluti int hours = min / 60; min -= (hours * 60); - System.out.printf("%d puzzles in %dms (%d:%02d:%02d.%03d)\r\n", thread.getCount(), thread.getTicks(), hours, - min, sec, ms); -// System.out.println(thread.getCount() + " puzzles in " + thread.getTicks() + "ms (" + hours + ":" + min + ":" + sec + "." + ms + ")"); + System.out.printf( + "%d puzzles in %dms (%d:%02d:%02d.%03d)\r\n", + thread.getCount(), + thread.getTicks(), + hours, min, sec, ms + ); + System.out.printf("%.03f ms per puzzle\r\n", (thread.getTicks() / (double) thread.getCount())); System.out.println(thread.getBruteForceAnz() + " puzzles require guessing!"); System.out.println(thread.getTemplateAnz() + " puzzles require templates!"); @@ -164,69 +212,86 @@ public void batchSolve(String fileName, String puzzleString, boolean printSoluti } if (printStatistic) { + System.out.println(); + try { thread.printStatistic(null, false); } catch (IOException ex) { ex.printStackTrace(); } + SudokuSolverFactory.getDefaultSolverInstance().getStepFinder().printStatistics(); } } void sortPuzzleFile(String fileName, List typeList, String outFileName) { + try { + if (typeList.size() > 0) { System.out.println("Filter:"); for (StepType tmpType : typeList) { System.out.println(" " + tmpType); } } + BufferedReader in = new BufferedReader(new FileReader(fileName)); BufferedWriter out = null; + if (outFileName == null) { outFileName = fileName + ".out.txt"; } + if (!outFileName.equals("stdout")) { out = new BufferedWriter(new FileWriter(outFileName)); } + List puzzleList = new ArrayList(); String line = null; + int gesAnz = 0; while ((line = in.readLine()) != null) { + gesAnz++; boolean includePuzzle = true; if (line.contains("#") && typeList.size() > 0) { + includePuzzle = false; // determine puzzle type String inputStr = line.substring(line.indexOf('#') + 1).trim(); int puzzleType = 3; String[] types = inputStr.split(" "); + for (int i = 0; i < types.length; i++) { if (types[i].equals("x")) { puzzleType = 0; break; } } -// if (inputStr.contains("x")) { -// puzzleType = 0; + if (puzzleType == 3) { + if (inputStr.startsWith("ssts")) { puzzleType = 2; } + if (inputStr.endsWith("ssts")) { puzzleType = 1; } } + String typeStr = null; int compAnz = 0; String[] parts = inputStr.split(" "); + if (parts.length > 1) { typeStr = parts[1]; } else { // invalid type in input -> nothing to apply continue; } + int index1 = typeStr.indexOf('('); int index2 = typeStr.indexOf(')'); String orgTypeStr = typeStr; @@ -239,6 +304,7 @@ void sortPuzzleFile(String fileName, List typeList, String outFileName } } } + // apply filter for (StepType actType : typeList) { if (typeStr.equals(actType.type.getArgName()) && puzzleType >= actType.puzzleType) { @@ -264,23 +330,28 @@ void sortPuzzleFile(String fileName, List typeList, String outFileName break; } } + if (includePuzzle) { break; } } } + if (includePuzzle) { puzzleList.add(line); } } + in.close(); int anz = puzzleList.size(); Collections.sort(puzzleList, new Comparator() { @Override public int compare(String s1, String s2) { + int index1 = s1.indexOf('#'); int index2 = s2.indexOf('#'); + if (index1 == -1 && index2 == -1) { return s1.compareTo(s2); } else if (index1 == -1 && index2 != -1) { @@ -292,6 +363,7 @@ public int compare(String s1, String s2) { } } }); + for (String key : puzzleList) { if (out != null) { out.write(key); @@ -300,10 +372,13 @@ public int compare(String s1, String s2) { System.out.println(key); } } + if (out != null) { out.close(); } + System.out.println(anz + " puzzles sorted (" + gesAnz + ")!"); + } catch (Exception ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Error sorting puzzle file", ex); } diff --git a/src/sudoku/MainFrame.java b/src/sudoku/MainFrame.java index dc4cda4..e604975 100644 --- a/src/sudoku/MainFrame.java +++ b/src/sudoku/MainFrame.java @@ -98,7 +98,7 @@ public class MainFrame extends javax.swing.JFrame implements FlavorListener { private static final long serialVersionUID = 1L; - public static final String VERSION = "HoDoKu - v2.2.6"; + public static final String VERSION = "HoDoKu - v2.2.7"; // public static final String BUILD = "Build 16"; public static final String BUILD;