├── .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 |
--------------------------------------------------------------------------------