├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── README.md ├── demo ├── sudoku01.txt ├── sudoku02.txt └── sudoku03.txt └── src ├── main.cpp ├── sudoku.cpp └── sudoku.h /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *~ 3 | 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "minisat"] 2 | path = minisat 3 | url = https://github.com/lakshayg/minisat 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project(sudoku) 3 | 4 | add_subdirectory(minisat EXCLUDE_FROM_ALL) 5 | include_directories(minisat) 6 | 7 | set(CMAKE_CXX_FLAGS "-Wall -pedantic -std=c++11 -O3") 8 | add_executable(main src/main.cpp src/sudoku.cpp) 9 | target_link_libraries(main minisat-lib-static) 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sudoku solver 2 | This project solves sudoku puzzles by formulating it as a [boolean satisfiability problem](https://en.wikipedia.org/wiki/Boolean_satisfiability_problem). 3 | It uses [minisat](http://minisat.se/) to solve the SAT problem. 4 | 5 | ## Obtaining the sovler 6 | ``` 7 | git clone https://github.com/lakshayg/sudoku 8 | cd sudoku 9 | git submodule init 10 | git submodule update 11 | ``` 12 | OR 13 | ``` 14 | git clone --recursive https://github.com/lakshayg/sudoku 15 | ``` 16 | 17 | ## Building the sovler 18 | Go to the project root directory and follow these steps to compile the solver (make sure you are using g++ and not clang) 19 | ``` 20 | mkdir build 21 | cd build 22 | cmake .. && make 23 | ``` 24 | This will generate an executable named `main` in the build directory. This is the solver. 25 | 26 | ## Using the solver 27 | The solver takes a text file as the input. Some sample puzzles are provided in the demo folder. To solve a puzzle, create a text file containing the puzzle. Use `0` for blank cells. An example is shown below: 28 | ``` 29 | 0 0 0 0 0 0 1 9 0 30 | 0 0 1 7 0 0 0 0 8 31 | 0 4 9 2 0 0 0 6 0 32 | 0 0 0 3 0 0 0 0 0 33 | 8 0 0 0 0 7 3 0 0 34 | 0 0 0 6 0 5 0 0 2 35 | 0 8 0 0 0 9 0 0 0 36 | 0 0 4 0 0 0 5 0 0 37 | 5 0 0 0 0 0 2 1 4 38 | ``` 39 | Now go into the build directory and use the following command 40 | ``` 41 | ./main 42 | ``` 43 | This should print out the solved puzzle on the terminal screen. 44 | ``` 45 | +---------+---------+---------+ 46 | | 2 7 8 | 5 6 4 | 1 9 3 | 47 | | 6 5 1 | 7 9 3 | 4 2 8 | 48 | | 3 4 9 | 2 8 1 | 7 6 5 | 49 | +---------+---------+---------+ 50 | | 4 1 5 | 3 2 8 | 9 7 6 | 51 | | 8 2 6 | 9 4 7 | 3 5 1 | 52 | | 9 3 7 | 6 1 5 | 8 4 2 | 53 | +---------+---------+---------+ 54 | | 1 8 2 | 4 5 9 | 6 3 7 | 55 | | 7 6 4 | 1 3 2 | 5 8 9 | 56 | | 5 9 3 | 8 7 6 | 2 1 4 | 57 | +---------+---------+---------+ 58 | Solved the puzzle in 3.86 ms :) 59 | ``` 60 | -------------------------------------------------------------------------------- /demo/sudoku01.txt: -------------------------------------------------------------------------------- 1 | 0 0 0 0 0 0 1 9 0 2 | 0 0 1 7 0 0 0 0 8 3 | 0 4 9 2 0 0 0 6 0 4 | 0 0 0 3 0 0 0 0 0 5 | 8 0 0 0 0 7 3 0 0 6 | 0 0 0 6 0 5 0 0 2 7 | 0 8 0 0 0 9 0 0 0 8 | 0 0 4 0 0 0 5 0 0 9 | 5 0 0 0 0 0 2 1 4 10 | 11 | -------------------------------------------------------------------------------- /demo/sudoku02.txt: -------------------------------------------------------------------------------- 1 | 0 0 9 0 0 2 0 0 5 2 | 0 4 0 0 9 0 0 3 0 3 | 5 0 0 7 0 0 1 0 0 4 | 9 0 0 1 0 0 6 0 0 5 | 0 6 0 0 5 0 0 7 0 6 | 0 0 5 0 0 6 0 0 2 7 | 0 0 3 0 0 5 0 0 6 8 | 0 2 0 0 4 0 0 8 0 9 | 4 0 0 3 0 0 7 0 0 10 | 11 | -------------------------------------------------------------------------------- /demo/sudoku03.txt: -------------------------------------------------------------------------------- 1 | 8 0 0 0 0 0 0 0 0 2 | 0 0 3 6 0 0 0 0 0 3 | 0 7 0 0 9 0 2 0 0 4 | 0 5 0 0 0 7 0 0 0 5 | 0 0 0 0 4 5 7 0 0 6 | 0 0 0 1 0 0 0 3 0 7 | 0 0 1 0 0 0 0 6 8 8 | 0 0 8 5 0 0 0 1 0 9 | 0 9 0 0 0 0 4 0 0 10 | 11 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "minisat/core/Solver.h" 2 | #include "sudoku.h" 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace Minisat; 8 | using namespace std; 9 | using namespace std::chrono; 10 | 11 | void read_sudoku(std::string filename, Sudoku &s) { 12 | std::ifstream in(filename); 13 | for (int k = 0; k < 81; ++k) { 14 | int i(k / 9), j(k % 9); 15 | in >> s(i, j); 16 | } 17 | in.close(); 18 | } 19 | 20 | int main(int argc, char **argv) { 21 | if (argc < 2) { 22 | cerr << "No input file provided" << endl; 23 | exit(-1); 24 | } 25 | 26 | Sudoku sudoku; 27 | read_sudoku(argv[1], sudoku); 28 | 29 | if (not sudoku.valid()) { 30 | cerr << "The provided sudoku is invalid" << endl; 31 | exit(-1); 32 | } 33 | 34 | Solver s; 35 | 36 | Var v[9][9][10]; // i, j, d 37 | Lit x[9][9][10]; 38 | for (int k = 0; k < 81; ++k) { 39 | int i(k / 9), j(k % 9); 40 | for (int d = 1; d < 10; ++d) { 41 | v[i][j][d] = s.newVar(); 42 | x[i][j][d] = mkLit(v[i][j][d]); 43 | } 44 | } 45 | 46 | auto t1 = high_resolution_clock::now(); 47 | // add clauses to the solver 48 | // each cell must contain only one of the 9 digits 49 | for (int k = 0; k < 81; ++k) { 50 | int i(k / 9), j(k % 9); 51 | vec c; 52 | for (int d = 1; d <= 9; ++d) { 53 | c.push(x[i][j][d]); 54 | } 55 | s.addClause(c); 56 | } 57 | 58 | // only one digit can be true. others must be false 59 | for (int k = 0; k < 81; ++k) { 60 | int i(k / 9), j(k % 9); 61 | for (int d = 1; d <= 9; ++d) { 62 | for (int dp = d + 1; dp <= 9; ++dp) { 63 | s.addClause(~x[i][j][d], ~x[i][j][dp]); 64 | } 65 | } 66 | } 67 | 68 | // each digit can appear only once in a row 69 | for (int d = 1; d <= 9; ++d) { 70 | for (int i = 0; i < 9; ++i) { 71 | // only one of x[i][:][d] can be true 72 | for (int j = 0; j < 9; ++j) { 73 | for (int jp = j + 1; jp < 9; ++jp) { 74 | s.addClause(~x[i][j][d], ~x[i][jp][d]); 75 | } 76 | } 77 | } 78 | } 79 | 80 | // each digit can appear only once in a col 81 | for (int d = 1; d <= 9; ++d) { 82 | for (int j = 0; j < 9; ++j) { 83 | // only one of x[:][j][d] can be true 84 | for (int i = 0; i < 9; ++i) { 85 | for (int ip = i + 1; ip < 9; ++ip) { 86 | s.addClause(~x[i][j][d], ~x[ip][j][d]); 87 | } 88 | } 89 | } 90 | } 91 | 92 | // each digit can appear only once in a 3x3 block 93 | for (int d = 1; d <= 9; ++d) { 94 | for (int g = 0; g < 9; ++g) { 95 | int r(g / 3), c(g % 3); 96 | for (int i = 3 * r; i < 3 * r + 3; ++i) { 97 | for (int j = 3 * c; j < 3 * c + 3; ++j) { 98 | for (int ip = 3 * r; ip < 3 * r + 3; ++ip) { 99 | for (int jp = 3 * c; jp < 3 * c + 3; ++jp) { 100 | if (i != ip or j != jp) { 101 | s.addClause(~x[i][j][d], ~x[ip][jp][d]); 102 | } 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | // add clauses for numbers which are already filled 111 | for (int k = 0; k < 81; ++k) { 112 | int i(k / 9), j(k % 9), d(sudoku(i, j)); 113 | if (d != 0) { 114 | s.addClause(x[i][j][d]); 115 | } 116 | } 117 | 118 | s.solve(); 119 | 120 | auto t2 = high_resolution_clock::now(); 121 | auto duration = duration_cast(t2 - t1).count(); 122 | 123 | if (s.okay()) { 124 | // update the sudoku 125 | for (int k = 0; k < 81; ++k) { 126 | int i(k / 9), j(k % 9); 127 | if (sudoku(i, j) == 0) { 128 | for (int d = 1; d <= 9; ++d) 129 | if (s.model[v[i][j][d]] == l_True) 130 | sudoku(i, j) = d; 131 | } 132 | } 133 | sudoku.print(); 134 | } 135 | 136 | cout << "time: " << duration * 1e-3 << " ms\n"; 137 | return 0; 138 | } 139 | -------------------------------------------------------------------------------- /src/sudoku.cpp: -------------------------------------------------------------------------------- 1 | #include "sudoku.h" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | Sudoku::Sudoku() 9 | { 10 | for (int i = 0; i < 9; ++i) { 11 | for (int j = 0; j < 9; ++j) { 12 | s[i][j] = 0; 13 | } 14 | } 15 | } 16 | 17 | Sudoku::Sudoku(const Sudoku& p) 18 | { 19 | for (int i = 0; i < 9; ++i) { 20 | for (int j = 0; j < 9; ++j) { 21 | s[i][j] = p(i, j); 22 | } 23 | } 24 | } 25 | 26 | Sudoku::Sudoku(Sudoku&& s) {} 27 | 28 | int Sudoku::operator()(int row, int col) const { return s[row][col]; } 29 | 30 | int& Sudoku::operator()(int row, int col) { return s[row][col]; } 31 | 32 | bool Sudoku::valid() 33 | { 34 | // checks if the cells contain a valid entry 35 | for (int i = 0; i < 9; ++i) { 36 | for (int j = 0; j < 9; ++j) { 37 | if (s[i][j] < 0 or s[i][j] > 9) { 38 | return false; 39 | } 40 | } 41 | } 42 | 43 | for (int i = 0; i < 9; ++i) { 44 | if (not row_valid(i)) return false; 45 | if (not col_valid(i)) return false; 46 | if (not box_valid(i)) return false; 47 | } 48 | return true; 49 | } 50 | 51 | bool Sudoku::solved() 52 | { 53 | for (int i = 0; i < 9; ++i) { 54 | for (int j = 0; j < 9; ++j) { 55 | if (s[i][j] == 0) { 56 | return false; 57 | } 58 | } 59 | } 60 | return valid(); 61 | } 62 | 63 | void Sudoku::print() 64 | { 65 | for (int i = 0; i < 9; ++i) { 66 | if (i % 3 == 0) printf("+---------+---------+---------+\n"); 67 | for (int j = 0; j < 9; ++j) { 68 | if (j % 3 == 0) printf("|"); 69 | printf(" %d ", s[i][j]); 70 | } 71 | printf("|\n"); 72 | } 73 | printf("+---------+---------+---------+\n"); 74 | } 75 | 76 | bool Sudoku::row_valid(int idx) 77 | { 78 | vector count(10, 0); 79 | for (int i = 0; i < 9; ++i) { 80 | int d = s[idx][i]; 81 | if (d != 0) { // check only for non-empty cells 82 | if (count[d] > 0) { 83 | return false; 84 | } 85 | count[d]++; 86 | } 87 | } 88 | return true; 89 | } 90 | 91 | bool Sudoku::col_valid(int idx) 92 | { 93 | vector count(10, 0); 94 | for (int i = 0; i < 9; ++i) { 95 | int d = s[i][idx]; 96 | if (d != 0) { // check only for non-empty cells 97 | if (count[d] > 0) { 98 | return false; 99 | } 100 | count[d]++; 101 | } 102 | } 103 | return true; 104 | } 105 | 106 | bool Sudoku::box_valid(int idx) 107 | { 108 | vector count(10, 0); 109 | for (int i = 3 * (idx / 3); i < 3 * (idx / 3) + 3; ++i) { 110 | for (int j = 3 * (idx % 3); j < 3 * (idx % 3) + 3; ++j) { 111 | int d = s[i][j]; 112 | if (d != 0) { // check only for non-empty cells 113 | if (count[d] > 0) { 114 | return false; 115 | } 116 | count[d]++; 117 | } 118 | } 119 | } 120 | return true; 121 | } 122 | -------------------------------------------------------------------------------- /src/sudoku.h: -------------------------------------------------------------------------------- 1 | #ifndef SUDOKU_H 2 | #define SUDOKU_H 3 | 4 | class Sudoku { 5 | public: 6 | Sudoku(); ///< Default constructor 7 | Sudoku(const Sudoku& s); ///< Copy constructor 8 | Sudoku(Sudoku&& s); ///< Move constructor 9 | 10 | int operator()(int row, int col) const; ///< Access value at (row,col) 11 | int& operator()(int row, int col); ///< Modify value at (row,col) 12 | 13 | bool valid(); ///< Returns true if the sudoku is in a consistent state 14 | bool solved(); ///< Returns true if the sudoku is solved 15 | void print(); ///< Print the sudoku 16 | 17 | private: 18 | int s[9][9]; ///< Sudoku data 19 | 20 | bool row_valid(int idx); ///< Checks if the given row is consistent 21 | bool col_valid(int idx); ///< Checks if the given column is consistent 22 | bool box_valid(int idx); ///< Checks if the given 3x3 box is consistent 23 | }; 24 | 25 | #endif 26 | --------------------------------------------------------------------------------