Add files via upload

This commit is contained in:
Peter Norvig
2026-04-24 22:10:43 -07:00
committed by GitHub
parent 5907ec9bbf
commit ec3c52f43a
7 changed files with 349599 additions and 501142 deletions

View File

@@ -1,18 +1,22 @@
import java.io.*; import java.io.*;
import java.lang.Integer.*;
import java.util.*; import java.util.*;
import java.util.stream.*;
import java.lang.StringBuilder;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
//////////////////////////////// Solve Sudoku Puzzles //////////////////////////////// //////////////////////////////// Solve Sudoku Puzzles ////////////////////////////////
//////////////////////////////// @author Peter Norvig //////////////////////////////// //////////////////////////////// @author Peter Norvig ////////////////////////////////
//////////////////////////////// 2007, 2021 ////////////////////////////////
/** There are two representations of puzzles that we will use: /** Solve Sudoku Puzzles
** @author Peter Norvig
** Mostly 2007, som2 2021, 2026
**
** There are two representations of puzzles that we will use:
** 1. A gridstring is 81 chars, with characters '0' or '.' for blank and '1' to '9' for digits. ** 1. A gridstring is 81 chars, with characters '0' or '.' for blank and '1' to '9' for digits.
** 2. A puzzle grid is an int[81] with a digit d (1-9) represented by the integer (1 << (d - 1)); ** 2. A puzzle grid is an int[81] with a digit d (1-9) represented by the integer (1 << (d - 1));
** that is, a bit pattern that has a single 1 bit representing the digit. ** that is, a bit pattern that has a single 1 bit representing the digit.
** A blank is represented by the OR of all the digits 1-9, meaning that any digit is possible. ** A blank is represented by the OR of all the digits 1-9, meaning any digit is possible.
** While solving the puzzle, some of these digits are eliminated, leaving fewer possibilities. ** While solving the puzzle, some of these digits are eliminated, leaving fewer possibilities.
** The puzzle is solved when every square has only a single possibility. ** The puzzle is solved when every square has only a single possibility.
** **
@@ -29,60 +33,59 @@ import java.util.concurrent.CountDownLatch;
public class Sudoku { public class Sudoku {
//////////////////////////////// main; command line options ////////////////////////////// //////////////////////////////// main; command line options //////////////////////////////
static final String usage = String.join("\n", static final String USAGE = String.join("\n",
"usage: java Sudoku -(no)[fghnprstuv] | -[RT]<number> | <filename> ...", "usage: java Sudoku -(no)[fghnprstuv] | -[RT]<number> | <filename> ...",
"E.g., -v turns verify flag on, -nov turns it off. -R and -T require a number. The options:\n", "Options and filenames are processed left-to-right. Use '-no' to turn an option off\n",
" -f(ile) Print summary stats for each file (default on)", "E.g.: -v turns verify flag on, -nov turns it off. -R and -T require a number. The options:\n",
" -g(rid) Print each puzzle grid and solution grid (default off)", " -g(rid) Print each puzzle grid and solution grid (default off)",
" -h(elp) Print this usage message", " -h(elp) Print this usage message",
" -n(aked) Run naked pairs (default on)", " -n(aked) Run the naked pairs strategy (default on)",
" -p(uzzle) Print summary stats for each puzzle (default off)", " -p(uzzle) Print summary stats for each puzzle (default off)",
" -r(everse) Solve the reverse of each puzzle as well as each puzzle itself (default off)", " -r(everse) Solve the reverse of each puzzle as well as each puzzle itself (default off)",
" -s(earch) Run search (default on, but some puzzles can be solved with CSP methods alone)", " -s(ummary) Print per-file summary stats (default on)",
" -t(hread) Print summary stats for each thread (default off)", " -t(hread) Print summary stats for each thread (default off)",
" -u(nitTest)Run a suite of unit tests (default off)", " -u(nitTest) Run a suite of unit tests (default off)",
" -v(erify) Verify each solution is valid (default on)", " -v(erify) Verify each solution is valid (default on)",
" -T<number> Concurrently run <number> threads (default 26)", " -T<number> Concurrently run <number> threads (default 25)",
" -R<number> Repeat each puzzle <number> times (default 1)", " -R<number> Repeat the solving of each puzzle <number> times (default 1)",
" <filename> Solve all puzzles in filename, which has one puzzle per line"); " <filename> Solve all puzzles in filename, which has one puzzle per line");
boolean printFileStats = true; // -f
boolean printGrid = false; // -g boolean printGrid = false; // -g
boolean runNakedPairs = true; // -n boolean runNakedPairs = true; // -n
boolean printPuzzleStats = false; // -p boolean printPuzzleStats = false; // -p
boolean reversePuzzle = false; // -r boolean reversePuzzle = false; // -r
boolean runSearch = true; // -s boolean printFileStats = true; // -s
boolean printThreadStats = false; // -t boolean printThreadStats = false; // -t
boolean verifySolution = true; // -v boolean verifySolution = true; // -v
int nThreads = 26; // -T int nThreads = 25; // -T
int repeat = 1; // -R int repeat = 1; // -R
int backtracks = 0; // count total backtracks private final AtomicInteger backtracks = new AtomicInteger(0);
private volatile boolean headerPrinted = false;
/** Parse command line args and solve puzzles in files. **/ /** Parse command line args and solve puzzles in files. **/
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
Sudoku s = new Sudoku(); Sudoku s = new Sudoku();
for (String arg: args) { for (String arg : args) {
if (!arg.startsWith("-")) { if (!arg.startsWith("-")) {
s.solveFile(arg); s.solveFile(arg);
} else { } else {
boolean value = !arg.startsWith("-no"); boolean value = !arg.startsWith("-no");
switch(arg.charAt(value ? 1 : 3)) { switch (arg.charAt(value ? 1 : 3)) {
case 'f': s.printFileStats = value; break; case 'g' -> s.printGrid = value;
case 'g': s.printGrid = value; break; case 'h' -> System.out.println(USAGE);
case 'h': System.out.println(usage); break; case 'n' -> s.runNakedPairs = value;
case 'n': s.runNakedPairs = value; break; case 'p' -> s.printPuzzleStats = value;
case 'p': s.printPuzzleStats = value; break; case 'r' -> s.reversePuzzle = value;
case 'r': s.reversePuzzle = value; break; case 's' -> s.printFileStats = value;
case 's': s.runSearch = value; break; case 't' -> s.printThreadStats = value;
case 't': s.printThreadStats = value; break; case 'u' -> s.runUnitTests();
case 'u': s.runUnitTests(); break; case 'v' -> s.verifySolution = value;
case 'v': s.verifySolution = value; break; case 'T' -> s.nThreads = Integer.parseInt(arg.substring(2));
case 'T': s.nThreads = Integer.parseInt(arg.substring(2)); break; case 'R' -> s.repeat = Integer.parseInt(arg.substring(2));
case 'R': s.repeat = Integer.parseInt(arg.substring(2)); break; default -> System.out.println("Unrecognized option: " + arg + "\n" + USAGE);
default: System.out.println("Unrecognized option: " + arg + "\n" + usage);
} }
} }
} }
@@ -91,30 +94,31 @@ public class Sudoku {
//////////////////////////////// Handling Lists of Puzzles //////////////////////////////// //////////////////////////////// Handling Lists of Puzzles ////////////////////////////////
/** Solve all the puzzles in a file. Report timing statistics. **/ /** Solve all the puzzles in a file. Report timing statistics. **/
void solveFile(String filename) throws IOException { void solveFile(String filename) throws IOException {
List<int[]> grids = readFile(filename); List<int[]> grids = readPuzzlesFromFile(filename);
long startFileTime = System.nanoTime(); long startFileTime = System.nanoTime();
switch(nThreads) { if (nThreads == 1) {
case 1: solveList(grids); break; solveList(grids);
default: solveListThreaded(grids, nThreads); break; } else {
solveListThreaded(grids, nThreads);
} }
if (printFileStats) printStats(grids.size() * repeat, startFileTime, filename); if (printFileStats) printStats(grids.size() * repeat, startFileTime, filename);
} }
/** Solve a list of puzzles in a single thread. /** Solve a list of puzzles in a single thread.
** repeat -R<number> times; print each puzzle's stats if -p; print grid if -g; verify if -v. **/ ** repeat -R<number> times; print each puzzle's stats if -p; print grid if -g; verify if -v. **/
void solveList(List<int[]> grids) { void solveList(List<int[]> grids) {
int[] puzzle = new int[N * N]; // Used to save a copy of the original grid int[] puzzle = new int[N * N];
int[][] gridpool = new int[N * N][N * N]; // Reuse grids during the search int[][] gridpool = new int[N * N][N * N];
for (int g=0; g<grids.size(); ++g) { for (int g = 0; g < grids.size(); ++g) {
int grid[] = grids.get(g); int[] grid = grids.get(g);
System.arraycopy(grid, 0, puzzle, 0, grid.length); System.arraycopy(grid, 0, puzzle, 0, grid.length);
for (int i = 0; i < repeat; ++i) { for (int i = 0; i < repeat; ++i) {
long startTime = printPuzzleStats ? System.nanoTime() : 0; long startTime = printPuzzleStats ? System.nanoTime() : 0;
int[] solution = initialize(grid); // All the real work is int[] solution = initialize(grid);
if (runSearch) solution = search(solution, gridpool, 0); // on these 2 lines. solution = search(solution, gridpool, 0);
if (printPuzzleStats) { if (printPuzzleStats) {
printStats(1, startTime, "Puzzle " + (g + 1)); printStats(1, startTime, "Puzzle " + (g + 1));
} }
@@ -129,26 +133,25 @@ public class Sudoku {
/** Break a list of puzzles into nThreads sublists and solve each sublist in a separate thread. **/ /** Break a list of puzzles into nThreads sublists and solve each sublist in a separate thread. **/
void solveListThreaded(List<int[]> grids, int nThreads) { void solveListThreaded(List<int[]> grids, int nThreads) {
try { try {
final long startTime = System.nanoTime(); final long startTime = System.nanoTime();
int nGrids = grids.size(); int nGrids = grids.size();
final CountDownLatch latch = new CountDownLatch(nThreads); final CountDownLatch latch = new CountDownLatch(nThreads);
int size = nGrids / nThreads; int size = nGrids / nThreads;
for (int c = 0; c < nThreads; ++c) { for (int c = 0; c < nThreads; ++c) {
int end = c == nThreads - 1 ? nGrids : (c + 1) * size; int end = (c == nThreads - 1) ? nGrids : (c + 1) * size;
final List<int[]> sublist = grids.subList(c * size, end); final List<int[]> sublist = grids.subList(c * size, end);
new Thread() { new Thread(() -> {
public void run() { solveList(sublist);
solveList(sublist); latch.countDown();
latch.countDown(); if (printThreadStats) {
if (printThreadStats) { printStats(repeat * sublist.size(), startTime, "Thread");
printStats(repeat * sublist.size(), startTime, "Thread");
}
} }
}.start(); }).start();
} }
latch.await(); // Wait for all threads to finish latch.await();
} catch (InterruptedException e) { } catch (InterruptedException e) {
System.err.println("And you may ask yourself, 'Well, how did I get here?'"); Thread.currentThread().interrupt();
System.err.println("Solver thread was interrupted.");
} }
} }
@@ -159,56 +162,60 @@ public class Sudoku {
int[] cross(int[] rows, int[] cols) { int[] cross(int[] rows, int[] cols) {
int[] result = new int[rows.length * cols.length]; int[] result = new int[rows.length * cols.length];
int i = 0; int i = 0;
for (int r: rows) { for (int c: cols) { result[i++] = N * r + c; } } for (int r : rows) {
for (int c : cols) {
result[i++] = N * r + c;
}
}
return result; return result;
} }
/** Return true iff item is an element of array. **/
/** Return true iff item is an element of array, or of array[0:end]. **/
boolean member(int item, int[] array) { return member(item, array, array.length); } boolean member(int item, int[] array) { return member(item, array, array.length); }
/** Return true iff item appears within array[0..end). **/
boolean member(int item, int[] array, int end) { boolean member(int item, int[] array, int end) {
for (int i = 0; i<end; ++i) { for (int i = 0; i < end; ++i) {
if (array[i] == item) { return true; } if (array[i] == item) return true;
} }
return false; return false;
} }
//////////////////////////////// Constants //////////////////////////////// //////////////////////////////// Constants ////////////////////////////////
final int N = 9; // Number of cells on a side of grid. final int N = 9;
final int[] DIGITS = {1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 1<<8}; final int[] DIGITS = {1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7, 1<<8};
final int ALL_DIGITS = Integer.parseInt("111111111", 2); final int ALL_DIGITS = Integer.parseInt("111111111", 2);
final int[] ROWS = IntStream.range(0, N).toArray(); final int[] ROWS = IntStream.range(0, N).toArray();
final int[] COLS = ROWS; final int[] COLS = ROWS;
final int[] SQUARES = IntStream.range(0, N * N).toArray(); final int[] SQUARES = IntStream.range(0, N * N).toArray();
final int[][] BLOCKS = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}}; final int[][] BLOCKS = {{0,1,2},{3,4,5},{6,7,8}};
final int[][] ALL_UNITS = new int[3 * N][]; final int[][] ALL_UNITS = new int[3 * N][];
final int[][][] UNITS = new int[N * N][3][N]; final int[][][] UNITS = new int[N * N][3][N];
final int[][] PEERS = new int[N * N][20]; final int[][] PEERS = new int[N * N][20];
final int[] NUM_DIGITS = new int[ALL_DIGITS + 1]; final int[] NUM_DIGITS = new int[ALL_DIGITS + 1];
final int[] HIGHEST_DIGIT = new int[ALL_DIGITS + 1]; final int[] HIGHEST_DIGIT = new int[ALL_DIGITS + 1];
{ {
// Initialize ALL_UNITS to be an array of the 27 units: rows, columns, and blocks
int i = 0; int i = 0;
for (int r: ROWS) {ALL_UNITS[i++] = cross(new int[] {r}, COLS); } for (int r : ROWS) { ALL_UNITS[i++] = cross(new int[]{r}, COLS); }
for (int c: COLS) {ALL_UNITS[i++] = cross(ROWS, new int[] {c}); } for (int c : COLS) { ALL_UNITS[i++] = cross(ROWS, new int[]{c}); }
for (int[] rb: BLOCKS) {for (int[] cb: BLOCKS) {ALL_UNITS[i++] = cross(rb, cb); } } for (int[] rb : BLOCKS) {
for (int[] cb : BLOCKS) { ALL_UNITS[i++] = cross(rb, cb); }
}
// Initialize each UNITS[s] to be an array of the 3 units for square s. for (int s : SQUARES) {
for (int s: SQUARES) {
i = 0; i = 0;
for (int[] u: ALL_UNITS) { for (int[] u : ALL_UNITS) {
if (member(s, u)) UNITS[s][i++] = u; if (member(s, u)) UNITS[s][i++] = u;
} }
} }
// Initialize each PEERS[s] to be an array of the 20 squares that are peers of square s. for (int s : SQUARES) {
for (int s: SQUARES) {
i = 0; i = 0;
for (int[] u: UNITS[s]) { for (int[] u : UNITS[s]) {
for (int s2: u) { for (int s2 : u) {
if (s2 != s && !member(s2, PEERS[s], i)) { if (s2 != s && !member(s2, PEERS[s], i)) {
PEERS[s][i++] = s2; PEERS[s][i++] = s2;
} }
@@ -216,10 +223,8 @@ public class Sudoku {
} }
} }
// Initialize NUM_DIGITS[val] to be the number of 1 bits in the bitset val
// and HIGHEST_DIGIT[val] to the highest bit set in the bitset val
for (int val = 0; val <= ALL_DIGITS; val++) { for (int val = 0; val <= ALL_DIGITS; val++) {
NUM_DIGITS[val] = Integer.bitCount(val); NUM_DIGITS[val] = Integer.bitCount(val);
HIGHEST_DIGIT[val] = Integer.highestOneBit(val); HIGHEST_DIGIT[val] = Integer.highestOneBit(val);
} }
} }
@@ -230,61 +235,48 @@ public class Sudoku {
/** Search for a solution to grid. If there is an unfilled square, select one /** Search for a solution to grid. If there is an unfilled square, select one
** and try--that is, search recursively--every possible digit for the square. **/ ** and try--that is, search recursively--every possible digit for the square. **/
int[] search(int[] grid, int[][] gridpool, int level) { int[] search(int[] grid, int[][] gridpool, int level) {
if (grid == null) { if (grid == null) return null;
return null;
}
int s = select_square(grid); int s = select_square(grid);
if (s == -1) { if (s == -1) return grid; // All squares filled — puzzle is solved.
return grid; // No squares to select means we are done! for (int d : DIGITS) {
}
for (int d: DIGITS) {
// For each possible digit d that could fill square s, try it
if ((d & grid[s]) > 0) { if ((d & grid[s]) > 0) {
// Copy grid's contents into gridpool[level], and use that at the next level
System.arraycopy(grid, 0, gridpool[level], 0, grid.length); System.arraycopy(grid, 0, gridpool[level], 0, grid.length);
int[] result = search(fill(gridpool[level], s, d), gridpool, level + 1); int[] result = search(fill(gridpool[level], s, d), gridpool, level + 1);
if (result != null) { if (result != null) return result;
return result; backtracks.incrementAndGet(); // thread-safe
}
backtracks += 1;
} }
} }
return null; return null;
} }
/** Verify that grid is a solution to the puzzle. **/ /** Verify that grid is a valid solution to puzzle. **/
boolean verify(int[] grid, int[] puzzle) { boolean verify(int[] grid, int[] puzzle) {
if (grid == null) { return false; } if (grid == null) return false;
// Check that all squares have a single digit, and for (int s : SQUARES) {
// no filled square in the puzzle was changed in the solution. if (NUM_DIGITS[grid[s]] != 1
for (int s: SQUARES) { || (NUM_DIGITS[puzzle[s]] == 1 && grid[s] != puzzle[s])) {
if ((NUM_DIGITS[grid[s]] != 1) || (NUM_DIGITS[puzzle[s]] == 1 && grid[s] != puzzle[s])) {
return false; return false;
} }
} }
// Check that each unit is a permutation of digits for (int[] u : ALL_UNITS) {
for (int[] u: ALL_UNITS) { int unitDigits = 0;
int unit_digits = 0; // All the digits in a unit. for (int s : u) { unitDigits |= grid[s]; }
for (int s : u) {unit_digits |= grid[s]; } if (unitDigits != ALL_DIGITS) return false;
if (unit_digits != ALL_DIGITS) {
return false;
}
} }
return true; return true;
} }
/** Choose an unfilled square with the minimum number of possible values. /** Choose the unfilled square with the fewest possible values.
** If all squares are filled, return -1 (which means the puzzle is complete). **/ ** Return -1 if all squares are filled (puzzle complete). **/
int select_square(int[] grid) { int select_square(int[] grid) {
int square = -1; int square = -1;
int min = N + 1; int min = N + 1;
for (int s: SQUARES) { for (int s : SQUARES) {
int c = NUM_DIGITS[grid[s]]; int c = NUM_DIGITS[grid[s]];
if (c == 2) { if (c == 2) return s; // Can't do better than 2
return s; // Can't get fewer than 2 possible digits if (c > 1 && c < min) {
} else if (c > 1 && c < min) {
square = s; square = s;
min = c; min = c;
} }
@@ -293,24 +285,22 @@ public class Sudoku {
} }
/** fill grid[s] = d. If this leads to contradiction, return null. **/ /** Fill grid[s] = d. Return null if this creates a contradiction. **/
int[] fill(int[] grid, int s, int d) { int[] fill(int[] grid, int s, int d) {
if ((grid == null) || ((grid[s] & d) == 0)) { return null; } // d not possible for grid[s] if (grid == null || (grid[s] & d) == 0) return null;
grid[s] = d; grid[s] = d;
for (int p: PEERS[s]) { for (int p : PEERS[s]) {
if (!eliminate(grid, p, d)) { // If we can't eliminate d from all peers of s, then fail if (!eliminate(grid, p, d)) return null;
return null;
}
} }
return grid; return grid;
} }
/** Eliminate digit d as a possibility for grid[s]. /** Eliminate digit d as a possibility for grid[s].
** Run the 3 constraint propagation routines. ** Run all three constraint-propagation routines.
** If constraint propagation detects a contradiction, return false. **/ ** Return false if a contradiction is detected. **/
boolean eliminate(int[] grid, int s, int d) { boolean eliminate(int[] grid, int s, int d) {
if ((grid[s] & d) == 0) { return true; } // d already eliminated from grid[s] if ((grid[s] & d) == 0) return true; // Already eliminated
grid[s] -= d; grid[s] -= d;
return arc_consistent(grid, s) && dual_consistent(grid, s, d) && naked_pairs(grid, s); return arc_consistent(grid, s) && dual_consistent(grid, s, d) && naked_pairs(grid, s);
} }
@@ -318,29 +308,27 @@ public class Sudoku {
//////////////////////////////// Constraint Propagation //////////////////////////////// //////////////////////////////// Constraint Propagation ////////////////////////////////
/** Check if square s is consistent: that is, it has multiple possible values, or it has /** Check arc consistency: either s has multiple possibilities, or its single
** one possible value which we can consistently fill. **/ ** remaining value can be filled without contradiction. **/
boolean arc_consistent(int[] grid, int s) { boolean arc_consistent(int[] grid, int s) {
int count = NUM_DIGITS[grid[s]]; int count = NUM_DIGITS[grid[s]];
return count >= 2 || (count == 1 && (fill(grid, s, grid[s]) != null)); return count >= 2 || (count == 1 && fill(grid, s, grid[s]) != null);
} }
/** After we eliminate d from possibilities for grid[s], check each unit of s /** After eliminating d from grid[s], ensure d still has at least one valid
** and make sure there is some position in the unit where d can go. ** position in each of s's units. If exactly one remains, fill it. **/
** If there is only one possible place for d, fill it with d. **/
boolean dual_consistent(int[] grid, int s, int d) { boolean dual_consistent(int[] grid, int s, int d) {
for (int[] u: UNITS[s]) { for (int[] u : UNITS[s]) {
int dPlaces = 0; // The number of possible places for d within unit u int dPlaces = 0;
int dplace = -1; // Try to find a place in the unit where d can go int dPlace = -1;
for (int s2: u) { for (int s2 : u) {
if ((grid[s2] & d) > 0) { // s2 is a possible place for d if ((grid[s2] & d) > 0) {
dPlaces++; if (++dPlaces > 1) break;
if (dPlaces > 1) break; dPlace = s2;
dplace = s2;
} }
} }
if (dPlaces == 0 || (dPlaces == 1 && (fill(grid, dplace, d) == null))) { if (dPlaces == 0 || (dPlaces == 1 && fill(grid, dPlace, d) == null)) {
return false; return false;
} }
} }
@@ -348,23 +336,19 @@ public class Sudoku {
} }
/** Look for two squares in a unit with the same two possible values, and no other values. /** If two squares in a unit share exactly the same two possible values, eliminate
** For example, if s and s2 both have the possible values 8|9, then we know that 8 and 9 ** those values from every other square in that unit (naked pairs strategy). **/
** must go in those two squares. We don't know which is which, but we can eliminate
** 8 and 9 from any other square s3 that is in the unit. **/
boolean naked_pairs(int[] grid, int s) { boolean naked_pairs(int[] grid, int s) {
if (!runNakedPairs) { return true; } if (!runNakedPairs || NUM_DIGITS[grid[s]] != 2) return true;
int val = grid[s]; int val = grid[s];
if (NUM_DIGITS[val] != 2) { return true; } // Doesn't apply for (int s2 : PEERS[s]) {
for (int s2: PEERS[s]) {
if (grid[s2] == val) { if (grid[s2] == val) {
// s and s2 are a naked pair; find what unit(s) they share for (int[] u : UNITS[s]) {
for (int[] u: UNITS[s]) {
if (member(s2, u)) { if (member(s2, u)) {
for (int s3: u) { // s3 can't have either of the values in val (e.g. 8|9) int d = HIGHEST_DIGIT[val];
int d2 = val - d;
for (int s3 : u) {
if (s3 != s && s3 != s2) { if (s3 != s && s3 != s2) {
int d = HIGHEST_DIGIT[val];
int d2 = val - d;
if (!eliminate(grid, s3, d) || !eliminate(grid, s3, d2)) { if (!eliminate(grid, s3, d) || !eliminate(grid, s3, d2)) {
return false; return false;
} }
@@ -379,110 +363,114 @@ public class Sudoku {
//////////////////////////////// Input //////////////////////////////// //////////////////////////////// Input ////////////////////////////////
/** The method `readFile` reads one puzzle per file line and returns a List of puzzle grids. **/ /** Read one puzzle per line from filename and return a list of puzzle grids. **/
List<int[]> readFile(String filename) throws IOException { List<int[]> readPuzzlesFromFile(String filename) throws IOException {
BufferedReader in = new BufferedReader(new FileReader(filename)); try (BufferedReader in = new BufferedReader(new FileReader(filename))) {
List<int[]> grids = new ArrayList<int[]>(1000); List<int[]> grids = new ArrayList<>(1000);
String gridstring; String gridstring;
while ((gridstring = in.readLine()) != null) { while ((gridstring = in.readLine()) != null) {
grids.add(parseGrid(gridstring)); grids.add(parseGrid(gridstring));
if (reversePuzzle) { if (reversePuzzle) {
grids.add(parseGrid(new StringBuilder(gridstring).reverse().toString())); grids.add(parseGrid(new StringBuilder(gridstring).reverse().toString()));
}
} }
return grids;
} }
return grids;
} }
/** Parse a gridstring into a puzzle grid: an int[] with values DIGITS[0-9] or ALL_DIGITS. **/ /** Parse a gridstring into a puzzle grid. **/
int[] parseGrid(String gridstring) { int[] parseGrid(String gridstring) {
int[] grid = new int[N * N]; int[] grid = new int[N * N];
int s = 0; int s = 0;
for (int i = 0; i<gridstring.length(); ++i) { for (int i = 0; i < gridstring.length(); ++i) {
char c = gridstring.charAt(i); char c = gridstring.charAt(i);
if ('1' <= c && c <= '9') { if ('1' <= c && c <= '9') {
grid[s++] = DIGITS[c - '1']; // A single-bit set to represent a digit grid[s++] = DIGITS[c - '1'];
} else if (c == '0' || c == '.') { } else if (c == '0' || c == '.') {
grid[s++] = ALL_DIGITS; // Any digit is possible grid[s++] = ALL_DIGITS;
} }
} }
assert s == N * N; if (s != N * N) {
throw new IllegalArgumentException(
"Grid string yielded " + s + " squares; expected " + (N * N) + ": \"" + gridstring + "\"");
}
return grid; return grid;
} }
/** Initialize a grid from a puzzle. /** Initialize a fresh grid from puzzle, then fill known squares to trigger constraint propagation. **/
** First initialize every square in the new grid to ALL_DIGITS, meaning any value is possible.
** Then, call `fill` on the puzzle's filled squares to initiate constraint propagation. **/
int[] initialize(int[] puzzle) { int[] initialize(int[] puzzle) {
int[] grid = new int[N * N]; Arrays.fill(grid, ALL_DIGITS); int[] grid = new int[N * N];
for (int s: SQUARES) { if (puzzle[s] != ALL_DIGITS) { fill(grid, s, puzzle[s]); } } Arrays.fill(grid, ALL_DIGITS);
for (int s : SQUARES) {
if (puzzle[s] != ALL_DIGITS) fill(grid, s, puzzle[s]);
}
return grid; return grid;
} }
//////////////////////////////// Output and Tests //////////////////////////////// //////////////////////////////// Output and Tests ////////////////////////////////
boolean headerPrinted = false; /** Print stats: puzzles solved, average µs, KHz, threads, backtracks, and name. **/
/** Print stats on puzzles solved, average time, frequency, threads used, and name. **/
void printStats(int nGrids, long startTime, String name) { void printStats(int nGrids, long startTime, String name) {
double usecs = (System.nanoTime() - startTime) / 1000.; double usecs = (System.nanoTime() - startTime) / 1_000.0;
String line = String.format("%7d %6.1f %7.3f %7d %10.1f %s", int bt = backtracks.getAndSet(0); // thread-safe
nGrids, usecs / nGrids, 1000 * nGrids / usecs, nThreads, backtracks * 1. / nGrids, name); String line = String.format("%7d %6.1f %7.3f %7d %10.1f %s",
synchronized (this) { // So that printing from different threads doesn't get garbled nGrids, usecs / nGrids, 1_000 * nGrids / usecs, nThreads, bt * 1.0 / nGrids, name);
synchronized (this) {
if (!headerPrinted) { if (!headerPrinted) {
System.out.println("Puzzles μsec KHz Threads Backtracks Name\n" System.out.println("Puzzles μsec KHz Threads Backtracks Name\n"
+ "======= ====== ======= ======= ========== ===="); + "======= ====== ======= ======= ========== ====");
headerPrinted = true; headerPrinted = true;
} }
System.out.println(line); System.out.println(line);
backtracks = 0;
} }
} }
/** Print the original puzzle grid and the solution grid. **/ /** Print the original puzzle grid alongside the solution grid. **/
void printGrids(String name, int[] puzzle, int[] solution) { void printGrids(String name, int[] puzzle, int[] solution) {
String bar = "------+-------+------"; final String BAR = "------+-------+------";
String gap = " "; // Space between the puzzle grid and solution grid final String GAP = " ";
if (solution == null) solution = new int[N * N]; if (solution == null) solution = new int[N * N];
synchronized (this) { // So that printing from different threads doesn't get garbled synchronized (this) {
System.out.format("\n%-22s%s%s\n", name + ":", gap, System.out.format("\n%-22s%s%s\n", name + ":", GAP,
(verify(solution, puzzle) ? "Solution:" : "FAILED:")); verify(solution, puzzle) ? "Solution:" : "FAILED:");
for (int r = 0; r < N; ++r) { for (int r = 0; r < N; ++r) {
System.out.println(rowString(puzzle, r) + gap + rowString(solution, r)); System.out.println(rowString(puzzle, r) + GAP + rowString(solution, r));
if (r == 2 || r == 5) System.out.println(bar + gap + " " + bar); if (r == 2 || r == 5) System.out.println(BAR + GAP + " " + BAR);
} }
} }
} }
/** Return a String representing a row of this puzzle. **/ /** Return a String representing one row of the grid. **/
String rowString(int[] grid, int r) { String rowString(int[] grid, int r) {
String row = ""; StringBuilder row = new StringBuilder(30);
for (int s = r * 9; s < (r + 1) * 9; ++s) { for (int s = r * 9; s < (r + 1) * 9; ++s) {
row += (char) ((NUM_DIGITS[grid[s]] == 9) ? '.' : (NUM_DIGITS[grid[s]] != 1) ? '?' : int nd = NUM_DIGITS[grid[s]];
('1' + Integer.numberOfTrailingZeros(grid[s]))); char cell = nd == 9 ? '.' : nd != 1 ? '?' : (char)('1' + Integer.numberOfTrailingZeros(grid[s]));
row += (s % 9 == 2 || s % 9 == 5 ? " | " : " "); row.append(cell);
row.append(s % 9 == 2 || s % 9 == 5 ? " | " : " ");
} }
return row; return row.toString();
} }
/** Unit Tests. Just getting started with these. **/ /** Unit Tests. **/
void runUnitTests() { void runUnitTests() {
assert N == 9; assert N == 9;
assert SQUARES.length == 81; assert SQUARES.length == 81;
for (int s: SQUARES) { for (int s : SQUARES) {
assert UNITS[s].length == 3; assert UNITS[s].length == 3;
assert PEERS[s].length == 20; assert PEERS[s].length == 20;
} }
assert Arrays.equals(PEERS[19], assert Arrays.equals(PEERS[19],
new int[] {18, 20, 21, 22, 23, 24, 25, 26, 1, 10, 28, 37, 46, 55, 64, 73, 0, 2, 9, 11}); new int[]{18,20,21,22,23,24,25,26,1,10,28,37,46,55,64,73,0,2,9,11});
assert Arrays.deepToString(UNITS[19]).equals( assert Arrays.deepToString(UNITS[19]).equals(
"[[18, 19, 20, 21, 22, 23, 24, 25, 26], [1, 10, 19, 28, 37, 46, 55, 64, 73], [0, 1, 2, 9, 10, 11, 18, 19, 20]]"); "[[18, 19, 20, 21, 22, 23, 24, 25, 26], [1, 10, 19, 28, 37, 46, 55, 64, 73], [0, 1, 2, 9, 10, 11, 18, 19, 20]]");
System.out.println("Unit tests pass."); System.out.println("Unit tests pass.");
} }
} }

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

250000
ipynb/sudokus.txt Normal file

File diff suppressed because it is too large Load Diff

49151
ipynb/sudokus_17.txt Normal file

File diff suppressed because it is too large Load Diff

375
ipynb/sudokus_hard.txt Normal file
View File

@@ -0,0 +1,375 @@
........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3
........2..8.1.9..5....3.4....1.93...6..3..8...37......4......53.1.7.8..2........
..2...7...1.....6.5......18....37.......49.....41.23....3.2.9...8.....5.6.......2
........7..4.2.6..8.....31......29...4..9..3...95.6....1......8..6.5.2..7......6.
..4..3....7..8....2.81....6..3....9..8..2....1..7....3......45....8..9....9..5..8
..6..1....5..3....9..4....7..1....2..3..9....4..5....13.....68....3..2....2..8..3
........3..1..9.6..5..8.4.....9...8...867.....1....2....6..7.2..3.8..5..4.......8
........5..6..87..3......9....1.7.4...7...8...4...6....9..8...3..16..4..5...2....
.....5..3..9....4..81.4.......7.......4..2..68...14.3.......2...4...6..79...5..1.
.....5..4.9.....2...6.7.3.....7..8....86.....13..8......3.1.6...2......54......9.
........6..5..18...9...8.7....8.2.....3.1.2..4..5.3....6.....9...83..1..7.......4
........9..6.1.7.24......3......12...6..2..5...28.7....3......4..8.7.6..9..1.....
..6.....1.8...7.5.9...5.8.......4....4..3..2....2.8.....3.7...9.7.....8.1.....6.3
........2..8..91..5......4....9.7.....7.3.8.....8.1.3..4..6...5..97..3..2........
........7..2..96..8...6...3....92.....46..5...1..54.....5.4.9...3.....7.1.......8
.......24..3...7..41..7...6.....3.....5.9.8.....8.5...2...4...9..7...4..96.....1.
.......58..6...7..84..7...3.....1.....9.2.1.....6.9...5...8...2..7...8..23.....4.
..6.......3...7..11.7.............9.3....6..5.5.8..2....3..5..6...2...4..4..9.8..
........5..8..79...6..1..4....1.2.7.4...7...3.7.6......3..2..6...5...8..9.......7
........6..2....4..1...798....79.....8..5......3..8.1...6....2..9...51..4.......8
....6...9..6..35...4..5..7......2......8.......2.1.8....5...3...9..3..1.71......4
........7..2...6...8..1..9....9.1.3.3...8...4.9...5....3..9..4...7...2..6..5.....
........2..7...1...3..9..4......9.6.6...3.4.5.4...8....6..4..5...2...7..1..8.....
.....6......5.8.....8.9.5......7...4..76..2...1..2..3...2...6...4..6..9.39......1
..1.......5...6..16.7...........1..3...4.5.8..9..2..5...5..3..7....8..4..3.5..2..
........6..9...4...1..7..8....7.1.3.1.8.3.5...3...2....8..2..1...6...9..4..3.....
........7..9.5.2..1....6.8....5..6...9.....3...64.2....8.6.......4.2.9..7......61
........7..1..9.8..3.6..5.9.9..25.......6....3..9...4...7....91.2.5..3..8........
........6..5..8.9..3.4..7....491........8..4.5....42....1..9.5..6....4.37........
.....7..8.3..2..9...84..3....6.......8.5..6..5.4..........9.8.2.1.....7.8..3..4..
..9.......6...3...1...7...54...52..7.....6.4..3.9.....2......1.....2.57...48....2
.....6..5.1.....2...9.3.4......7......48.9...8..4.39....7.8.3...5.....1.2.......6
.....4.....2.3.....5.7....9..4..2.8..985....63.........8....79....8...6...5.1...8
2.......6.5..8..1...4...9...7.3.1......82.......7.5.3...9...4...8..1..5.6.......2
........6..5.3.7..2....8.1....9..8...5..8..4...87.3....1.3.......7.9.5..6.......2
.....1..7....6..2.8..9..3...954....3..3...4..4......8......7..6.1..2....5..3..9..
........8..3.9.4..6....7.1....5.97...3.....2...74........7....6..4.5.3..81.......
.....1.....7.2.....9.6.8..5..1..7....6.9....42.9.........8...59.8....4....6.3...8
........9..1...6...5..7..4....4.7.8...3.8...5.8...2....78.2..5..39...1..6........
........3..1..56...9..4..7......9.5.7.......8.5.4.2....8..2..9...35..1..6........
..4.....9.1...3.7.6.....2.......8....5..3..8....1.5.....2.....4.7.3...1.9...7.6..
.....6.....1.2...3.3.8...7...6.....5.5.3..7..2....1.......9..4...5...98..9.4....7
..8...5...4...2.9.7.......3.2...4......93.......2.1.6.5.......8.1.6...2...3...7..
.....5.....3.7...8.9.8...4...5.....2.8.6..4..7...3......2...69...4...1...1.9....4
..3...5...7...4.6.8.......9.4...7......45.......2.1.7.5.......3.1.7...2...9...8..
........2..1...7...3..5..9......6.4...3.4.8...4.5.9....9..6..3...2...1..7....3...
..8.......5...8..11.9.4........3......5..9..4.4.6...7.......6..5....4.29.2..7..3.
..5...7...8...6.4.9.......2.4...3......65.......4.8.1.2.......9.1.8...3...7...5..
..8...3...7...9.4.1.......2.6...4......98.......7.6.5.2.......1.9.4...6...3...8..
..9...4...7.3...2.8...6...71..8....6....1..7.....56...3....5..1.4.....9...2...7..
1.......9.4...3.8...2...6...7..58.......2.......7.4.5...6...2...3.8...7.9.......1
..2...6...4...5.1.3.......8.9...4......52.......1.7.4.8.......3.5.7...9...6...2..
..9...7...1...6.8.3.......4.2...5......89.......6.2.1.4.......3.8.2...5...7...9..
5.......9.2.1...7...8...3...4.6.........5.......2.7.1...3...8...6...4.2.9.......5
3.......8.7.5...1...6...4...9.2.1.......4.......97..2.4.....3...5...2.7...8.....6
9.......5.4.3...6...2...1...8.74........2.......8.6.7.1.....9...3...7.4...5.....2
.......39.....1..5..3.5.8....8.9...6.7...2...1..4.......9.8..5..2....6..4..7.....
3.......2.8..7..1...69......5.7.4...........8...51..7...9...3...1..4..8.2.......6
3.......5.7...8.6...2...9...1...6......82.......1.7.4...9...2...8.6...1.5.......3
3.......1.4...2.7...5...8......76.4.....5.....6.2.9...1.....5...7.6...9...8.....3
5.......7.8...9.3...4...2...1..36.......4.......1.8.6...2...4...9.3...1.7.......5
....9..5..1.....3...23..7....45...7.8.....2.......64...9..1.....8..6......54....7
1.......2....1..3...5..34....2..1..4....8.7..6..9.......1..5.4.8.....5..9...6....
1.........2.4...6...3...5.1.4..86.......49.8....2.....5.7...3...6.9...4.........7
........91......35..9.3.8....3.5...67....2......4.......6.8..9..2.7..6..4.....1..
1.......5.2.4...6...3...7...9...4........9.8.8.26.........5.1...6.9...2...7.....3
.......39.8......5..9.6.8....5.9...67....2......4.......3.8..5..2.7..6..4.....1..
1.......7.2.4...6...3...5...9..4........62.4....9..8....5.....3.6.2...8.7....1...
1.......2.2.....6...34..5.....8.5.....8.3.9.....9.4.....5..34...7.....1.6.......7
.2.4..7.........32.......94.9.2...7...6..5...8...1....5.1..8....3.9....7......6..
1.......2.2.....6...34..5.....8.5.....8.3.9.....9.4.....53..4...6......77......1.
....567.....1.....6..37......4.....13...6.9...8.....2.5....73....2.....8.1.....4.
.......93.8......5..3.6.8....5.9...67....2......4.......9.8..5..2.7..6..4.....1..
1.......7.2.4...6...3...5...9..82.......9..8....6..4....5...1...6.8...2.7....3...
.......1...6....23.2..3.4..8....5....3..1...4..96........9..7...1..2..4.5....8...
.......89.....1.35..3.5......5.6...8.7...2...1..4.......6.9..5..2.7..6..4........
.......12........3..23..4....18....5.6..7.8.......9.....85.....9...4.5..47...6...
...1....87.......9..3.9.5....9.3...5.....23.....4..6....6.8..5.47...1...2........
.....1.39........5..3.5.6....8.9...67....28..1..4.......9.8..5..2.......4..7.....
........7.2.4...6.1.....5...9...2.4....8..6..6..9.......5..3....3..8..2.7....4..1
...2....87.......9..3.9.5....8.3...51.....3.....4..6....6.8..5..7...1...2....4...
...1....8.7......9..3.9.5....8.3...57....23.....4..6....6.8..5.4....1...2........
.1......8.7...4..9..3.9.5....8.3...5.2....3.....4..6....6.8..5.2....1......7.....
5.......9.2.1...7...8...3...4...2.......5.......7.6.1...3...8...6...4.2.9.......5
9....4....3..6..7...5...8...2.1.6.......7...3...2...1...8...9...7..1..6.4.......5
1...5...9.......3..79........7.1...65....42..6..8.......5.6...7.....2.4....3..8..
...1....87.......9..6.9.5....8.6...5.....23...7.4..6....3.8..5..2...1....4.......
.......1......4.32.2..3.5.......7....4..2...5..89..4....78..6...3..1..5.9........
7....4..8.1......9..6.9.5....8.6...5.....23.....4..6....3.8..5.2....1......7.....
1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1
12.3.....4...5......6..17..5..2......3..1..4...1..68....9....7....1..9.......9.68
12.3.....3...4......5..23..5....61.........9...67....8.5...16.....9...4.....8...7
..3.......5.4...8.1.......7.9..8........94.6.5.62.............3.6.9...4...7.2.1..
...2....871......9..6.9.5....8.6...5.....23...4....6....9.3..5.4....1........7...
12.3.....4.....3....3.5......42..5......8...9.6...5.7...15..2......9..6......7..8
.2..5.7..4..1....68....3...2....8..3.4..2.5.....6...1...2.9.....9......57.4...9..
..3..6.8....1..2......7...4..9..8.6..3..4...1.7.2.....3....5.....5...6..98.....5.
1.......9..67...2..8....4......75.3...5..2....6.3......9....8..6...4...1..25...6.
...4...8...7..92......3...526...1.....19......7....1..5......4..1.8....3..6..29..
..34...8.......1.37........2...9.......5..8...6...7.4...51....8.7...5...9...62.5.
1...5...9..7...2...69..3.4....8........91..7.9....5..83..5....1..2...6...4.......
....56.8..5.7....3..8......2.....9...4.5....7....92.6.3.4.....15..1..4...1.....7.
1...56.....71.......9....1..4..6.3.......8..29..7...6......24..5..6...9.....3...8
...4...8...7..92......3...526...1.....19.....7.....1..5......4...6..29..9..8....3
1....6....571......9..7..1...8...........89.29..5...6.....42..3.3....4..7..6...9.
.2.4.....4...8...6..9..31..2..5...7..95.......7....3....29...4......1..3....6.8..
..3..6.8.4..1..2...9......4.7...8....35......9...3...5..9.4...7.....2.1....8..6..
.2.4..7...5...9.3.6...7......5..8.9.7...2.....4.6..3.......1..85......1...1...9.3
...4....9....8.2..6....3.1..1...7...7..6..5...3.....7.3....5.2...29..8......2...4
1....67...5.7....3..8.....4.4.5..3......2..9........45.7.9...3.6...1......2..8...
1.3.....9..71.9....9.....4..1...7..3....2.8.....5...6.....6.5..6..8...2..7...4...
..3..67...5..8.....9.1.........9..1...7..32..8..5...4.37....4.......4..6..4....72
1.......2..94...5..6....7.....89..4....3.6.....8.4.....2....1..7.......6..5.8..3.
6.......2.9.4...5...1...7...5..84.......2.......3.5.4.2.....6...3...9.8...7.....1
1.......2.9.4...5...6...7...5.3.4.......6........58.4...2...6...3...9.8.7.......1
1.......2.3.4...5...6...7...5.9.4.......23......8...9...2...6...9...8.4.7.......1
1.......2.3.4...5...6...7...5.8.4.......29......3...9...7.....1.9...8.4.2.....6..
1.......2.9.4...5...6...7...5.3.4......96.........8.4...2...6...3...9.8.7.......1
1.......2.3.4...5...6...7...5.8.3.......7.......95..8.7.....6...9...8.3...2.....1
1.......2..34...5..6....7.....85..9....3.6.....8.9.....2....1..7.......6..9.8..3.
1.......2.3.4...5...6...7...5.8.4.......73......9...8.7.....6...4...8.9...2.....1
1.......2..34...5..6....7......3..4....8.6.....954.....2....1..7.......6..5.9..8.
..1...5...2.4...6.3....7....6.28........9..2.......4.65.....1...9.8...4...7.....3
1.......2.3.4...5...6...7...5.8.3.......74......9...8.7.....6...9...8.3...2.....1
...1....9.......85..9.5..6...5.3...6.7...2...1..4.......3.8..9..2...73..4........
.......35.....2.6...3.5..8...5.9...6.7....9..1..4.......6.8..9..2.1.....4....7...
1......8......913.......5.6..7.4....3....1.5..6.2.....5....89...4..7......26.....
3....97...6..4......18.....5......93.....327.........8..4.1....2....7.5..8.6.....
.......8...6...12....2..6.5..15..9..8....3....4..7....3....8.....21....6.7..4....
1.......2..34...5..6....7......3..4....5.6.....894.....2....1..7.......6..5.8..9.
12.3....435....1....4........54..2..6...7.........8.9...31..5.......9.7.....6...8
........94....92......7..45..1.3.....7.6..9..8....7..2.3.7..8....6.1....9....5.2.
...4....9.5...9.3.......1.52..8.......6.4.....1...53....42...7.8...67....7......3
1...5..8...7.....6.8.3..5..2......1...6.....7.9...38.....9.4....4..3.9.....8...2.
..3.5.7..4....9....8.2.....2......4..9.8.......1.6.5.....5.16......7...3..6....57
..34..7.......9..6....2..1...8..4..13.....8..57.8..........8.9.8...6...2..53..1..
..3..6.8....1....6.6.72.....1...7..8..4...6..5..3..9...3...2..7..1...4..9......5.
.....6......7...2...9.3.5....5.9.84.8..2....6.......7.39..4....5.....4....4..1..8
..3...7..4...8...6.9.....1.......6..6....4..8..16..39..7...2...5..8....2....45...
1..4.......7.8...6.9....41......3....3.8..9....5.6...7..2......5....8..2.7..2.65.
..34......5...9...6...2.1..2...6...1.4.5...6...6..3.....9...6.7......8.28...7..1.
.2...67..4...8..3....2.............53...4..1...5..76...34..8.9..81.9.4..9........
......7.94....9.3..8....4.5..18......6..2....7....53..5....7..3.7.6.......2.1..7.
1....67...5.1....6..9....1...5..83..7..5...9..3...........4...25..6...7...4.2.8..
1....6....5......6..9.7..1...4.1..7.3..8......8...53.......4.2.....2.9.7..27..1..
1.....7...5..8...37..2.1....6...5...3.58....6....6.4....29..3.........9..3..4...8
....5.78...6..9..27.........3.....5....83.2....1..2..6.6......4..41..92.9....4...
.2..5..8...71.9...6...........5...4..4......8....4.3.2..1..7..6.6..2..3.9..6..8..
1.....7...5..8...37..2.1....6...5.3...58....6....6.4....29..3.........9..3..4...8
1..4..7....7..9..6.8.....5.....1....83..9......5..7.6.3.......5....4..92..2..56..
1...567.....1....6..9....1...5..83..7..5...9..3...........4...25..6...7...4.2.8..
.2......9..6.8.1..7.......5..46....8....4..1....3.84...9.....2.5....7..1..1.3.8..
....56.8..5.7....3..8......2.....9...4.5....7....92.6.3.......15..1..4...1..4..7.
..3...7..4...8..2...9.....6.1...43......2...79..8...5....5..6..5....8.9...129....
..34....9.5.......6....7.5.2..8..3....8..49...4..2...8..29....4.7..61.........1..
.2....7..4....9.3.6..723.4...85.........1....9....4.6..94....5.5.....6.3.....5...
1...5..8.......1...8...3..6..7..4.6.3...7...4.4....8..5.2....9....29.....6...7..8
..3.5...9...1..23.6..3..5....5.3.9.........2.8....7..4..2.4..9..6...1....7.8.....
..3..6...4.7....6..6....1...7...1.9....59.81.9.......2....2.9...4...3.7....8....5
..345....4....9.3..8..3....2..9...4.....2...1..7...6..3...9..5...1..28...6......7
...4.67....7..........7..15.94.6.5.........98..591.6..3.............2.6...6.4.1..
...4.67....7..........7..15.94.6.5.........98..5.1.6..3....1........2.6...6.4.1..
...4.67....71.........7..15.94.6.5.........98..5.1.6..3.............2.6...6.4.1..
..3.5.7.....7......8...2..6..4.....1.1....86.8....12..5.239...........9..4...8..2
..3.5.7.....7......8...2..62.4.....1.1....86......12..5..39......8....9..4...8..2
1.....7....71.9..6.8..7......8.9.6..5.......2...2.3.5...6.1.9...4.......9....4..3
.....67....71.9...68..7......6.1.8...3.9..........4.23..1.9.6..5..2....4.......5.
...4...8...7..9....8..3.1..2.....9..7....53...3..6..1..6..9...15....46....2....9.
..3.5.7.....7......8...2..62.4.....1.1....86.8....12..5..39...........9..4...8..2
.2.....8...71....6.6..3........4.9.......38..7..5....2..5.9....6.1..54...7.6....5
.2.4...8...6.8....7....3..6...9.....6....7..1..4.2.9...67.....55.....31..1...5...
1......8..5.1....6..9.3.4....6.........9.36......6..24.7...5...8..3....7..2.4.3..
1.3.567......8...36....74.......5.715...........64.5.........2.7....41...9......7
12....7....7....36....7..1..1...5...5..9....8..6.2.1...4..........8.4.9...1.3.6..
1....67.....18..3...9.7......4.....3.9.....2.7..6..5....2..1...8...6.1.....8.5.4.
...4.......7.8...66..7...5...1.6.3.839.........6.7...1........271..2......26.8...
...4.......7.8...66......5...1.6.3.839.........6.7...1.....7..271..2......26.8...
..3..6.8....1....6.6..7.1..2...4.5...3...1..8......34....9......8...7..39.5....2.
.2....7..4....9.3.6..2.3.4.....1......85.....9....4.6..9.....5.5...4.6.3.....54..
1....6.8....7..1....9.....4.......5..18..5...5..36.8..6.5..8.3.8....3.1.....2....
1....6.8....7..1....9.....4.......5..18..5...5..36....6.5..8.3.8....3.1.....2...8
........94....9.2..8.1..5....5..86..3.86......6..15.....2....47.3.5..8......7....
......7....71.9...68..7......1.9.8..8.......4.4.....2...8.1.6..5..2....3...9.3.5.
..3.....94...8.1...9.....4.2...1.4.......7.5..753.........6..146.......8...2.86..
.2.4..7.......9..3....3..462.7...8...8.....64...8......7.6..4....1..5...9...1..5.
1....6....5..8..3...9..2....7..4.3.....6....79....7..5..1...4...4..7...3..2..48..
1...5.7....7..9.3......7..4.8......3..4....2.6..5..9....8.1.....1..6...29....51..
1...5.7....7.....6..97...4......8..4.1..3.9.....6...2.39......25..8.......1.9.5..
1...5.7....71......6...2..4.....834..8..........62...8..52...9..3...4..29...6....
.234.......6.8....7....35....4....7....8...9.9....7..53....1.5....2....3.6....9.1
12....7....7....366...7..1......4.9.54.9.......1.3.6.......8......5....8..2.1.3..
...4.......678....7....2..4.3....6....1..5.9....2....5.9...7.1..6....3..8...2...7
1...5..8......9..6..92..5.......3..8.7.9.......4.2.3..6...4....7.......1..5..24..
....567....71....6......41.2...9.....3...5.....67...4...8...1..5...3...8.9...2...
......7....71.9...68..7......1.9.6.....3...2..4......3..8.6.1..5......4..1...2..5
...4......5...9..6..9.2..1..3......5..1..2.6.7.....8......1...78...6.3....62...4.
1.....7....71.9...68..7......1.9.6.....3...2..4......3..8.6.1..5......4......2..5
.2...6...4...8......97..1........3.5.6....9.1..13...7...59....38....2..7....4....
.2....7..4....9..3....2......1...6..8....3..4...67..1..8...5...5...3..4...98....5
.2.4...8......9..66.....5....87.....3....8.7..7..2.4....1..5.......1.9...3.8...4.
1....6..9..67...2..8..3.5.....5..3...1...8..6....2......1..4..884.......9......7.
.2....7.........3...91....4..49....83.....6...7..6..2...5.98........1..5.4.5..8..
.2.4....9......1..7...3..4..1.6..9..5....8.....7..3.1......5....4.2...9.8.....6.2
...4...8...7..92......3...526...1.....19.....7...2.1..5......4...6..29.....8....3
...4...8.4....9..2...23.....7...56..3..9....5.......1.5...9...4.6.5...7...1...8..
...4.67....7..9..6....7..5.2......1..8......3..6.4.5..3.......8..5.9.4...1.2.....
.2......9..6.8.1..7......5...46.8....6.3..8......4...1.9.....2......5..7..1.6.3..
1.....7....71.9...68..7......1.9.8...4.2....5.......2...8.6.9..5....3.4.........3
1..4...8..5...9..6....3.1...9...5..7..8.2.4.........2.7.5..3....3.69............3
.2...6..94..18......8...4....1....5....5......6...3..2.....7.9..7..9.3....26....7
.2...6..9..71........23..1....5..3..5...7.....8...2..6.....4...6.8.....4.4....92.
...4.....45.....6...8..7..1..9..8..73.........6.5..3....2.7..9......217....8....2
.2...6..9..71.........3..1....5..3..5...7.....8...2..6.....4...6.8.2...4.4....92.
1...5...9..7...2...6.....4...6..1........84..9..5....83....5..1..13...2..7.9.....
1...5.7...5...9.3..8...7....4..1........6...29....51....4.....87..5..6...3.....2.
1..4...8..5...9..2..9...4.....6..8.36............41.6...2.7.5...7.......8..3...4.
1..4.......7.8...6.9....41..3............39....5.6...7..2..8....7..2..6.8....5..2
1..4..7....6....2.....3...5.....5..6....24.3.8..1..4....7...9...149.....9....8...
........9.5.1...3...9.2.4...3.7...5.......8..8....4..2.....1.6.5.16......6..73...
........94....9.2..8.1..5.......56..3.56......6..18.....2....47.3.5..8......7....
......7...5.1....6..9.7..4.2....49...1.6....8.......2.5.13.......8.....3.3..68...
.....6....56...1..7...3..4.2............2.9.4.4.3.7.2.3...4..9...8.......1.8....5
...4.67....7.....6....7..512..........4.1.5...9.2....8.3.......8....3.9...6.4.1..
..3.5..8.4....91...8.......2...........64.2.......2.91.753...4.........79....46..
..34..7...5...9.3.6..3.....2.....8...4..9..5.7.....6.......1..8.1.9...4...2..5...
.2....7..4....9.3.6..2.3.4.....1......85.....9....4.6..94....5.5.....6.3.....5...
.2.4....9....8....6.8...1.....7..39..7.....2.....24..75.........4.9....3..1..5.6.
1.....7...5...9.3.....2...1..6....4.3.5..4....4.96......87....2.3...5.6.......8..
1....6..9.5.7...2.......5...3......8.91..8...8..36........2.47...4......9....3..1
1....6.8.....8.2...89.7.........53....4..1...7...6..1.5..6...7..4....9....2.....3
1...5.7....7..9.3..8...7....4..1........6...29....51....4.....87..5..6...3.....2.
1..4...8..5...9..2..9.........6..8.36.......4....41.6...2.7.5...7.......8..3...4.
1..4...8..5...9..2..9..........48.6....6..3.16.........72..15..8..3...1.....7....
12....7....7....36....7....2..9......4...5.9...1.2.6..........85..8.4.....2.6.1..
12....7....7....366...7...........9..4.9.5.....6.2.3..5....4..8...8.......2.6.1..
12....7....7....366...7.........8.5..4.9....8..1.2.3.....5.......2.6.1..9....4...
12....7....7....366...7.....4..........8.4.9...1.3.6.......5...5..9....8..2.1.3..
....5..8...6..91.....2....4.17.......3..4....6....73....1....5..6...39..9..8....2
..3..67..45...............4..8...........8.26...37.8....2..76...1..9..7.9..5...1.
..3..67..45...............4..8.....7.....8.26...37.8....2..76...1..9....9..5...1.
..3......4...8..36..8...1...4..6..73...9..........2..5....7..686....4...7.....54.
..3......4...8..36..8...1...4..6..73...9...1......2.....4.7..686........7....35..
..34.........8..36..8...1.4.4..6..73...9..........2..5..4.7..686........7.....5..
.2......945...9.2....1....4....6....34...52....8......59...3..2...5...3.....7..5.
.2......945...9.2....1....4..5.6....34...52....8.......9...3..2...5...3.....7..5.
.2...6.8.4.....2.6...3.......59...1..6...28..........7.7...1..85...3......9.4..7.
.2.4.......6.8.1..7....2.......4..96...8.14....4......53.........8.1.9.....5...73
1......8...67....3.8....5....53....6.4...19..9...4.......2..65...2..........73..2
1..4..7....71.9...6...7....2......5...1.6.4.....3....8..6.4.9..5......2..8......3
1.34......5...9.2.........4.....19.5.1..........27..1..7...2.9.6..3..8..8...6....
1....67......8......92...5.....4...85..3...1......76..3.2.......1.9...2.9.5.....4
......7.94...8..3......2..5.4..3..6.3.8.....7..69......1.2......3...5.....4.6..1.
......7....71.9...68..7......1.9.6..8.......4.4.....2...8.6.1..5..3..........2.53
......7....71.9...68..7......1.9.6.........2484.........8.6.1..5.......3...3.2.5.
...4.67....7..........7..152.........9.2....8..4.6.1..38.....9....6.3.....6.1.5..
..3......4...8..36..8...1...4..6..73...9..........2..5..4.7..686........7..6..5..
..3......4...8..36..8...1...4..6..73...9..........2..5..4.7..686....4...7.....5..
..3......4...8..36..8...1...4..6..73...9...1......2.....4.7..686........7..6..5..
..34......5....1..7....3..52.7....6..3...7..88..9.........4..9...8..2..7...6...1.
..34.....4...8..36..8...1...4..6..73...9..........2..5..4.7..686........7.....5..
.2.4....9..7..9.....9..31.....6...5....2..8...9...4..2.6..4...37.....5....8....1.
1.....7....71.9...68..7......8.6.9..3..2...5.........2..6.1.8...4......3.....5.4.
1.....7....6.8.1...89.....6...5.....6...9...1.3...2.5.......4....23.4...8...6...7
1....6.8....7......9..3.......56..3.3.............38.15....1.6.....2.4..8.2..5.1.
1....6.8..5.1..2....9...1...9..3......45.....6....2..8.4.....6.7....18.....3...7.
1....67...57.8......6.....4...9...4.....2...8.1...36..3.......25...9......1..75..
1....67...5.....3...8....4.....9...3..5.12...9..6..1...4.7....8.....1...7...6.9..
1....6...4....9.3..8.2..6....4.....7.9..2.8..5......1....3...7..3...29....58.....
12....7....7....36....7......6.1.3...4.8.....9....4..8..2.6.1.....9...5......5..2
.2.4.......6.8....7....35....4....7....8..39.9....7..53....1.5....2....3.6......1
.2.4.......7.8...6.....3.5...9.6...1.....23.....5...4...1...8..6...1...797.......
...4.67....7..........7..1529......8..6.1.5.....2......3.......8....3.9...5.4.6..
1...5...9......12...8...5...3...7...5...6..1...48......7...4...6...2.9.....3....6
.....6.8.4....9....6..7.1...9...73......1..2...5.3.....1.7..6....4.....2..8.....5
....5.7..4....9.3..8....4......1..9.7....23....6....2...85.......1.6....9....4..3
......7....71.9...68..7......8.6.1..5.............3.52..1.9.8...4.2....3.......4.
......7....71.9...68..7......8.6.1..5..3...2.........3..1.9.8.......2..4.4.....5.
........94....9.2..8.1..5.......56..3..6......6..18..5..2....47.3.5..8......7....
......7....71.9...68..7......1.9.6...4...2..58......2...8.6.9..5......3....3....4
......7....71.9...68..7......1.6.8...6.....42.....2.....6.9.1..54......3.1.3...5.
....5....4....92....9....1.2..7.39....6.....79....43...94...8.......8.9.8......23
...4.......6.8.1..7....3..4.3.5.......9.6.8.......7.5...2....166...2....98....2..
...4.......6.8.1..7....3..4.3.5.......9.6.8...6...7.5...2....16....2....98....2..
...45.7...5...9.3.6..3.....2....1....3.9...5.9....5..8.1..9..4...2...8..7.....6..
..34....94...8.1...9.......2...1.8.....3.7.5..75.........2.86......6..146.......8
.2...6.8....7..1....92..........83....5..4....4.6...7..7..6..2.5.......3..1...9..
.2...6.8....7..1....92..........43....5..8....4.6...7..7..6..2.5.......3..1...9..
1.....7....71.9...68..7......1.6.8...6.....42.....2.....6.9.1..54......3...3...5.
1....6.8..5.1....6..9.3.5...7.8.......5.2.9.......7...39....4......4......4....52
1....67.....18..3..9..7......4.1...3..9....2.8..6..1....2......5...6.8.......5.4.
12....7....7....36....7.........5..85..9.......6.2.3...4..........8.4.9...2.6.1..
......7....71.9...68..7......1.9.8..8.......4.4.....2...8.6.1..5..2....3...9.3.5.
......7....71.9...68..7......1.9.6...4......28......4...8.6.1..5.......3...2.3.5.
......7....71.9...68..7......1.6.9..84.....2.........43..2...5...8.1.6.......5..3
......7....71.9...68..7......1.6785.5....3.....8.1.9....6.9.1...4.....9.........2
...4.......6.8.1..7....3..4.3.5.......9.6.8.......7.5...2....16....2...898....2..
...4.67....7..........7..512..8...9...1.4.6...9........3.......8....3..2..6.1.5..
..3.....94.....2...6..7..1..1...7.5...46........5....2.8.7...6......8..39....18..
..3......4...8..36..83..1...4..6..73...9..........2..5..4.7..686........7.....5..
.2...6.8.4.......2..9...4...7............76.1...82..7.3...9...5..53......6...8.1.
.2.4...8.....8....7....2..6..49..1..6....7..3.9..1.....62.....55.....37......5...
1....6....5..8..3.7..2......4..9...3.....79..9..6..5.......2..8.9..4.3..8.1.....4
12....7....7....36....7..1..4...5.9....9.......1.3.6..5....4..8.1.8.......6.2.1..
.......8...6.8.1..7....3..4.47..5...5..32............53....4..7......9...1.9...6.
1......8....18...6..9..35.......4.....4....2593....4...7..........76......2..59..
1....6.8..5.1..2.6...3..1....9.4..7.........26....18...4.9.....7....2..8..5.3....
1..........7.8...6.9.3...1...46........7.8..4....4.62.5.........3...59....2.6...7
1.....7...5...91...89.....5......6.....26....9....1..8..234.....3.....4.8....5..7
12....7....7....36....7.........4..8.4.9.......6.2.3...7.5.8.9.5..........2.3.1..
.2..5......6..9..17..3.....2...6.8....8..4.1.....3.6....4..81..5......9..7.....4.
1....6.8.....8.2...9..7......4.....35..6...7...9.....2..5..1..48...6..5......53..
1..4......5...92......7..14..6.9.....3...59.........67......8..5.2..8....8.93....
.2......9.5.1..2..6..7...5.....1......68.5.2.....2...3.6.2.8.7.7...6.4..8........
.2......9.5.1..2..6..7.2.5.....1......68.5.2.........3.6.2.8.7.7...6.4..8........
.2...6.8....1....66..73.......3...5...9...4..7....1..8..58..9..8....3..1.4..1....
......7....71.9...68..7......1.6.85.5....3...7.8.1.9....6.9.1...4.....9.........2
..34.6....5.1..3..6...7..4.2...6..7.........8.18..5.......9.2.4...6.7.9.9........
.2......9..6.8.1..7......4.2....1.5......5.9...58..6..37.6......4.3.......1..83..
.2...6.8....7..1..7.9.1.........45.8.4..........62..4.3..9.............3.6...5.2.
1....67....71.9...68..7......8.6.9..5....2.4.........2..1.9.8...4......3...3...5.
12.3.....34....1....5......6.24..5......6..7......8..6..42..3......7...9.....9.8.
...4......5..8.2.6.....7...2...4....3......1...5.3.8.25...6.3.8..6....95..8......
1....6.8...71....66.....15..3.9.....7....184.....2........9.41.5....4..8...8..5..
.2....7..4....9.3.6..2.3.4.....1......89.....9....4.6..94....5.5.....6.3.....5...
1...5.7.9..7.......6.......2...........5.1..2....2.39.3.4.9...15...1...3...8...4.
..3......4...8..36..83..1...4..6..73...9...1......2.....4.7..686........7.....5..
...4......5...9...6...2..1.2...7.9.1..5....7......8.3...6....9.7...3.1......9.327
...4......5..8.2.6.....71..2...4....3......1...5.3.8.25...6.3.8..6....9...8......
1...5.7.9..71......6.......2...........5.1..2....2.39.3...9...15...1...3...8...4.
1...56....5718...66...7.....9.3............9.8....5..35...17..8..2....1.......4..
1....6.8...7.....66.....15..3.9.........2....7....854.....9.81.8...41..5.1....4..
...4......5..8.2.6.....7...2...4....3......1...5.3.8.25...6.3.8..6..8.9...8......
...4......5..8.2.6.....7...2........3.....51...5.3.8.25...6.3.8..6....9...8.7....
12..5...9.57...2...9..2..1....8..9.47...6.1.......4............57..9.6....6..3...
.2.4...8...7.....3.8.237.1.2.1....9..9....8.4...9......1.8...4.5............6...8
.2.4...8...7.....3...237.1.2.1....9..9....8.48..9......1.8...4.5.8..........6....
.2.4...8...7.....3.8.237.1.2.1....9..9....8.4...9......1.8...4.5.8..........6....
..3..67.........2.79.2......3....6..5....4..76.7..3.453.5..74...............1...8
..3.5.....5.1..2.66...2..4....8...9..8..1.6.5...6.....7.......4..........6...18.2
..3.56....5.1..2.6....2..4...68...9..8..1.6.5...6.....7.......4..........6...18.2
..3.....945...92....9..3.54....6....6..9..8....5..8.2..1.7................4..5.92
.....6..9..67...3.79...3....1...7.5...752......5.....2..167..2......14..8........
...4......5..8.2.6.....71..2........3.....51...5.3.8.25...6.3.8..6..8.9...8......
.2.4...8......9..2..9.3............5..8..7....4.5..82...46..21.6.21..4...1......8
........9.5.7...2.7.9..2....1.67..5.......4..8....5....7.31....6....7.3..3..6...1
....5.7..4.7..9.3..8....1..2.8..7.....4..8....7..9...3......6...4...2.97..2.....1
.2.4.6..9..7......6...2..5......15...1.64...28......1..3.26.....6..1...39....3...
1....67.9.5.........9.....4....9..3.....1....9..6..8.1..27.....7..8...4.8...6.1.7
.2..5...9...7.....7.....5..2..........4..8....6..2..91.3.2..9..6...9..13..1.6..2.
.......8...67......8...36.5.4..3..5......4..66....83.4..1.9..2..3...25..9........
..3.9....4...8..36..8...1...4..6..73...9..........2.....4.7..686........7.....5.4
..39.....4...8..36..8...1...4..6..738......1......2.....4.7..686........7.....5..
..3......4...8..36..8...1...4..6..73...9..........2.....4.7..686........7..6..59.
..39.....4...8..36..8...1...4..6..73...3...1......2.....4.7..686........7.....5..
..3......4...8..36..83..1...4..6..73...9..........2.....4.7..686........7.....59.
..3......4...8..36..8...1...4..6..73...9..........2.....4.7..686...2....7..6..5..
..3..2...4...8..36..8...1...4..6..73...9.......6........4.7..686........7.....59.
..3......4...8..36..8...1...4..6..73...9...1......2.....4.7..686....4...7.....5..
..34.....4...8..36..8...1...4..6..73...9..........2.....4.7..686........7.....59.
..3..2...4...8..36..8...1...4..6..73...9..........4.....4.7..686........7.....59.
..3.9....1...4..36..8..21...4..8..73...9................4.7..686........7.....5.4
..3......4...8..36..8...1...4..6..73...9.........42..5..4.7..686........7.....5..
..3.9....4...8..36..8...1...4..6..73...9..........2.....4.7..686..4.....7.....5..
..3......4...8..36..8...1...4..6..73...9........7.2..5..4.7..686........7.....5..
12.4..3..3...1..5...6...1..7...9.....4.6.3.....3..2...5...8.7....7.....5.......98
1...5......7..9.3...9..754...4..3.7..6........9.8........79..2......24.3..2......
.23.....94.....1...9..3..4.2..81...4.....78..9...4...23...9...1.6..........5.....
1..4.6..........2..8..3.5.6.6...48.5............5..2.......3.9...7..8....4.6..3.8
..3.5.....567....27..2...4......18..3...2...66...7...453..4...7.......9.......4..
...4.67....7.....6....7..512......9...5.6.1...91.426..3...........8.......4.1.5..
1..4....9.56..9.......1..6..6....8..5....4.9.9....5.1..7....2..6....1.5....3.....
1.....7....6.8.1...89.....6.....4.5.6..59...19...1...8..2.........3.....8...6...7
1.....78...6.8.1....9.....6.....4.5.6..59...19...1...8..2.........3.....8...6...7
12.3.....4.5...6...7.....2.6..1..3....453.........8..9...45.1.........8......2..7
5..6......2.....4...1.2.3..9..8.......7.4.1.......9..6..4.7.2...3.....1......5..8

10
ipynb/sudokus_hard10.txt Normal file
View File

@@ -0,0 +1,10 @@
........8..3...4...9..2..6.....79.......612...6.5.2.7...8...5...1.....2.4.5.....3
........2..8.1.9..5....3.4....1.93...6..3..8...37......4......53.1.7.8..2........
..2...7...1.....6.5......18....37.......49.....41.23....3.2.9...8.....5.6.......2
........7..4.2.6..8.....31......29...4..9..3...95.6....1......8..6.5.2..7......6.
..4..3....7..8....2.81....6..3....9..8..2....1..7....3......45....8..9....9..5..8
..6..1....5..3....9..4....7..1....2..3..9....4..5....13.....68....3..2....2..8..3
........3..1..9.6..5..8.4.....9...8...867.....1....2....6..7.2..3.8..5..4.......8
........5..6..87..3......9....1.7.4...7...8...4...6....9..8...3..16..4..5...2....
.....5..3..9....4..81.4.......7.......4..2..68...14.3.......2...4...6..79...5..1.
.....5..4.9.....2...6.7.3.....7..8....86.....13..8......3.1.6...2......54......9.