sudoku_gem 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,206 @@
1
+ /*
2
+ * Sudoku Base - a library for solving Sudoku puzzles
3
+ * Copyright (C) 2013 Neal Patel <nealp9084@gmail.com>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #ifndef SUDOKU_H
20
+ #define SUDOKU_H
21
+
22
+ #include <cstddef>
23
+ #include <streambuf>
24
+ #include <string>
25
+ #include <vector>
26
+
27
+ #include "grid.h"
28
+
29
+ /**
30
+ * @brief This is a class designed to quickly and easily solve puzzles for the popular game Sudoku.
31
+ *
32
+ * If you want to solve a puzzle, first create a Sudoku object. Next, you have to tell the class
33
+ * what it is working with: you have to provide a puzzle to the object. You can do this by calling
34
+ * either read_puzzle_from_file() or read_puzzle_from_string(). Once you do that, you should check
35
+ * to make sure the puzzle was read in correctly by calling good(). Now that the class knows what it
36
+ * is dealing with, it can start solving the puzzle: just call one of the solver methods. These
37
+ * methods include: solve_colorability_style() and solve_bruteforce_style(). Once you call one of
38
+ * those methods, the solution to the puzzle will be saved in the object.
39
+ *
40
+ * Please note that due to memory constraints, this class can only ever hope to solve puzzles up to
41
+ * size 64*64. The actual Sudoku grid validations are performed by using bit hacks on 64-bit
42
+ * unsigned integers, so anything over 64 would cause an overflow. However, it is certainly not
43
+ * realistic to expect a solution for such a large board: if even a quarter of the cells in a 64*64
44
+ * board were unknown, there would be over 1000 unknowns, which means there would be over 1000
45
+ * recursive function calls, each one taking up a portion of the stack and each one attempting to
46
+ * allocate memory on the heap. This could lead to a stack or heap overflow (memory exhaustion).
47
+ **/
48
+ class Sudoku
49
+ {
50
+ public:
51
+ /**
52
+ * @brief Constructor for a Sudoku instance.
53
+ **/
54
+ Sudoku();
55
+ virtual ~Sudoku();
56
+
57
+ /**
58
+ * @brief Read in the puzzle from a given FILE* (this defaults to standard input) and store it in
59
+ * memory.
60
+ *
61
+ * @param f The file from which we should read the n*n Sudoku board, with rows separated by
62
+ * newlines, columns separated by spaces, integers [1-n] as the known values, and '?' for
63
+ * unknown values.
64
+ * @return bool Whether the parsing succeeded.
65
+ **/
66
+ bool read_puzzle_from_file(std::istream& f);
67
+ /**
68
+ * @brief Read in the puzzle from a given string representing the Sudoku board, and then store it
69
+ * in memory.
70
+ *
71
+ * @param s A string containing a n*n Sudoku board, with rows separated by newlines, columns
72
+ * separated by spaces, integers [1-n] as the known values, and '?' for unknown values.
73
+ * unknown values.
74
+ * @return bool Whether the parsing succeeded.
75
+ **/
76
+ bool read_puzzle_from_string(std::string const& s);
77
+
78
+ /**
79
+ * @brief Print the current state of the board to some output stream.
80
+ *
81
+ * @param out An output stream.
82
+ **/
83
+ void print(std::ostream& out) const;
84
+ /**
85
+ * @brief Return the current state of the board as a human-readable string.
86
+ *
87
+ * @return std::string The human-readable representation of the board.
88
+ **/
89
+ std::string to_s() const;
90
+
91
+ /**
92
+ * @brief Determine whether the puzzle has only a single solution by using the graph 9-coloring
93
+ * technique. If there are no solutions or multiple solutions, the method will return true.
94
+ *
95
+ * @return bool Whether the Sudoku board has only 1 solution.
96
+ **/
97
+ bool singular();
98
+
99
+ /**
100
+ * @brief Attempt to solve the puzzle using the graph 9-coloring technique. If the puzzle was
101
+ * successfully solved, then the solution will be saved to memory (overwriting the existing
102
+ * grid) and the method will return true. If the puzzle could not be solved for whatever
103
+ * reason, then this method will return false.
104
+ **/
105
+ void solve_colorability_style();
106
+ /**
107
+ * @brief Attempt to solve the puzzle by brute force. If the puzzle was successfully solved, then
108
+ * the solution will be saved to memory (overwriting the existing grid) and the method will
109
+ * return true. If the puzzle could not be solved for whatever reason, then this method
110
+ * will return false. Note that this approach will take a very, very long time. However, it
111
+ * will EVENTUALLY find a solution.
112
+ **/
113
+ void solve_bruteforce_style();
114
+
115
+ /**
116
+ * @brief Accessor for Sudoku::status_ok
117
+ *
118
+ * @return bool Whether the Sudoku board is ready to solve
119
+ **/
120
+ bool good() const;
121
+ /**
122
+ * @brief Inverse accessor for Sudoku::status_ok
123
+ *
124
+ * @return bool Whether the Sudoku board is NOT ready to solve
125
+ **/
126
+ bool bad() const;
127
+ /**
128
+ * @brief Implicit accessor for Sudoku::status_ok
129
+ *
130
+ * @return bool Whether the Sudoku board is ready to solve
131
+ **/
132
+ operator bool() const;
133
+
134
+ private:
135
+ /**
136
+ * @brief Helper method for parsing a puzzle from a file
137
+ *
138
+ * @param f The file from which we are reading.
139
+ * @return bool Whether the parsing succeeded.
140
+ **/
141
+ bool parse_puzzle(std::istream& f);
142
+ /**
143
+ * @brief Helper method for parsing a single, tokenized row of a Sudoku grid
144
+ *
145
+ * @param tokens The row elements.
146
+ * @param y The index of the row.
147
+ * @return bool Whether the parsing succeeded.
148
+ **/
149
+ bool insert_row(std::vector<std::string>& tokens, std::size_t y);
150
+ /**
151
+ * @brief Helper method for checking whether the given puzzle is solvable
152
+ * @return bool Whether the validation succeeded
153
+ **/
154
+ bool validate() const;
155
+
156
+ /**
157
+ * @brief Helper method for finding the next unknown (i.e., undetermined) Sudoku cell.
158
+ *
159
+ * @param cur_board The Sudoku game board.
160
+ * @param cur_x The last x position considered on the game board.
161
+ * @param cur_y The last y position considered on the game board.
162
+ * @param x_out The x position of the next unknown cell.
163
+ * @param y_out The y position of the next unknown cell.
164
+ * @return bool Whether we were able to find an unknown cell.
165
+ **/
166
+ static bool find_unknown(Grid& cur_grid, std::size_t cur_x, std::size_t cur_y,
167
+ std::size_t& x_out, std::size_t& y_out);
168
+
169
+ /**
170
+ * @brief Helper method for solving an instance of a Sudoku puzzle using the graph 9-colorability
171
+ * solution method. If a 9-coloring is found, the method will return true and overwrite
172
+ * cur_board with the solution (which corresponds with the graph coloring).
173
+ *
174
+ * @param cur_board The Sudoku game board.
175
+ * @param cur_x The last x position considered on the game board. Defaults to 0.
176
+ * @param cur_y The last y position considered on the game board. Defaults to 0.
177
+ * @return bool Whether we were able to find a 9-coloring for the Sudoku board.
178
+ **/
179
+ static bool color_node(Grid& cur_grid, std::size_t cur_x = 0, std::size_t cur_y = 0);
180
+ /**
181
+ * @brief Helper method for solving an instance of a Sudoku puzzle using the bruteforce solution
182
+ * method. If a solution is found, the method will return true and overwrite cur_board with
183
+ * the solution.
184
+ *
185
+ * @param cur_grid ...
186
+ * @param cur_x ... Defaults to 0.
187
+ * @param cur_y ... Defaults to 0.
188
+ * @return bool
189
+ **/
190
+ static bool bruteforce_node(Grid& cur_grid, std::size_t cur_x = 0, std::size_t cur_y = 0);
191
+
192
+ static int singular_decider(Grid& cur_grid, bool found_one = false, std::size_t cur_x = 0,
193
+ std::size_t cur_y = 0);
194
+
195
+ /**
196
+ * @brief The Sudoku board, which we are saving in memory.
197
+ **/
198
+ Grid grid;
199
+
200
+ /**
201
+ * @brief Whether the board is initialized (i.e., can we operate on this object?)
202
+ **/
203
+ bool status_ok;
204
+ };
205
+
206
+ #endif // SUDOKU_H
@@ -0,0 +1,31 @@
1
+ #include <ruby.h>
2
+ #include <string>
3
+ #include "sudoku.h"
4
+ #include <iostream>
5
+
6
+ typedef VALUE (* ruby_method)(...);
7
+
8
+ extern "C"
9
+ VALUE sudoku_gem_solve(VALUE self, VALUE rb_puzzle)
10
+ {
11
+ Sudoku sudoku;
12
+ std::string cpp_puzzle = StringValueCStr(rb_puzzle);
13
+
14
+ if (!sudoku.read_puzzle_from_string(cpp_puzzle))
15
+ {
16
+ return T_NIL;
17
+ }
18
+
19
+ sudoku.solve_colorability_style();
20
+
21
+ std::string solution = sudoku.to_s();
22
+
23
+ return rb_str_new(solution.c_str(), solution.length());
24
+ }
25
+
26
+ extern "C"
27
+ void Init_sudoku_gem()
28
+ {
29
+ VALUE klass = rb_define_class("SudokuGem", rb_cObject);
30
+ rb_define_singleton_method(klass, "solve", (ruby_method)&sudoku_gem_solve, 1);
31
+ }
@@ -0,0 +1,326 @@
1
+ /*
2
+ * Sudoku Base - a library for solving Sudoku puzzles
3
+ * Copyright (C) 2013 Neal Patel <nealp9084@gmail.com>
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ #include "validator.h"
20
+
21
+ bool Validator::is_good_row(Grid const& cur_grid, std::size_t y)
22
+ {
23
+ const std::size_t n = cur_grid.n();
24
+ std::uint_fast64_t mask = 0, valid_mask = (1 << n) - 1;
25
+
26
+ for (std::size_t x = 0; x < n; x++)
27
+ {
28
+ int a = cur_grid.get(x, y);
29
+
30
+ //reject rows that are incomplete or have duplicates
31
+ if ((a == -1) || ((mask & (1 << (a - 1))) != 0))
32
+ {
33
+ return false;
34
+ }
35
+ else
36
+ {
37
+ mask |= (1 << (a - 1));
38
+ }
39
+ }
40
+
41
+ return (mask == valid_mask);
42
+ }
43
+
44
+ bool Validator::is_good_column(Grid const& cur_grid, std::size_t x)
45
+ {
46
+ const std::size_t n = cur_grid.n();
47
+ std::uint_fast64_t mask = 0, valid_mask = (1 << n) - 1;
48
+
49
+ for (std::size_t y = 0; y < n; y++)
50
+ {
51
+ int a = cur_grid.get(x, y);
52
+
53
+ //reject cols that are incomplete or have duplicates
54
+ if ((a == -1) || ((mask & (1 << (a - 1))) != 0))
55
+ {
56
+ return false;
57
+ }
58
+ else
59
+ {
60
+ mask |= (1 << (a - 1));
61
+ }
62
+ }
63
+
64
+ return (mask == valid_mask);
65
+ }
66
+
67
+ bool Validator::is_good_block(Grid const& cur_grid, std::size_t x, std::size_t y)
68
+ {
69
+ const std::size_t n = cur_grid.n(), n_root = std::size_t(sqrt(n) + 0.5);
70
+ std::uint_fast64_t mask = 0, valid_mask = (1 << n) - 1;
71
+
72
+ for (std::size_t y_off = 0; y_off < n_root; y_off++)
73
+ {
74
+ for (std::size_t x_off = 0; x_off < n_root; x_off++)
75
+ {
76
+ int a = cur_grid.get(x + x_off, y + y_off);
77
+
78
+ //reject blocks that are incomplete or have duplicates
79
+ if ((a == -1) || ((mask & (1 << (a - 1))) != 0))
80
+ {
81
+ return false;
82
+ }
83
+ else
84
+ {
85
+ mask |= (1 << (a - 1));
86
+ }
87
+ }
88
+ }
89
+
90
+ return (mask == valid_mask);
91
+ }
92
+
93
+ bool Validator::is_good_board(Grid const& cur_grid)
94
+ {
95
+ const std::size_t n = cur_grid.n(), n_root = std::size_t(sqrt(n) + 0.5);
96
+
97
+ for (std::size_t y = 0; y < n; y++)
98
+ {
99
+ if (!is_good_row(cur_grid, y))
100
+ {
101
+ return false;
102
+ }
103
+ }
104
+
105
+ for (std::size_t x = 0; x < n; x++)
106
+ {
107
+ if (!is_good_column(cur_grid, x))
108
+ {
109
+ return false;
110
+ }
111
+ }
112
+
113
+ for (std::size_t x = 0; x < n_root; x++)
114
+ {
115
+ for (std::size_t y = 0; y < n_root; y++)
116
+ {
117
+ if (!is_good_block(cur_grid, x * n_root, y * n_root))
118
+ {
119
+ return false;
120
+ }
121
+ }
122
+ }
123
+
124
+ return true;
125
+ }
126
+
127
+ std::uint_fast64_t Validator::row_colors(Grid const& cur_grid, std::size_t y)
128
+ {
129
+ const std::size_t n = cur_grid.n();
130
+ std::uint_fast64_t mask = 0;
131
+
132
+ for (std::size_t x = 0; x < n; x++)
133
+ {
134
+ int a = cur_grid.get(x, y);
135
+
136
+ //ignore incomplete elements
137
+ if (a != -1)
138
+ {
139
+ mask |= (1 << (a - 1));
140
+ }
141
+ }
142
+
143
+ return mask;
144
+ }
145
+
146
+ std::uint_fast64_t Validator::column_colors(Grid const& cur_grid, std::size_t x)
147
+ {
148
+ const std::size_t n = cur_grid.n();
149
+ std::uint_fast64_t mask = 0;
150
+
151
+ for (std::size_t y = 0; y < n; y++)
152
+ {
153
+ int a = cur_grid.get(x, y);
154
+
155
+ //ignore incomplete elements
156
+ if (a != -1)
157
+ {
158
+ mask |= (1 << (a - 1));
159
+ }
160
+ }
161
+
162
+ return mask;
163
+ }
164
+
165
+ std::uint_fast64_t Validator::block_colors(Grid const& cur_grid, std::size_t x, std::size_t y)
166
+ {
167
+ const std::size_t n = cur_grid.n(), n_root = std::size_t(sqrt(n) + 0.5);
168
+ std::uint_fast64_t mask = 0;
169
+
170
+ for (std::size_t y_off = 0; y_off < n_root; y_off++)
171
+ {
172
+ for (std::size_t x_off = 0; x_off < n_root; x_off++)
173
+ {
174
+ int a = cur_grid.get(x + x_off, y + y_off);
175
+
176
+ //ignore incomplete elements
177
+ if (a != -1)
178
+ {
179
+ mask |= (1 << (a - 1));
180
+ }
181
+ }
182
+ }
183
+
184
+ return mask;
185
+ }
186
+
187
+ bool Validator::is_good_color(Grid const& cur_grid, std::size_t x, std::size_t y, int i)
188
+ {
189
+ const std::size_t n = cur_grid.n(), n_root = std::size_t(sqrt(n) + 0.5);
190
+
191
+ int bit = (1 << (i - 1));
192
+ std::uint_fast64_t row_mask = row_colors(cur_grid, y),
193
+ col_mask = column_colors(cur_grid, x),
194
+ block_mask = block_colors(cur_grid, (x / n_root) * n_root, (y / n_root) * n_root);
195
+
196
+ return ((row_mask & bit) == 0 && (col_mask & bit) == 0 && (block_mask & bit) == 0);
197
+ }
198
+
199
+ std::uint_fast64_t Validator::good_colors(Grid const& cur_grid, std::size_t x, std::size_t y)
200
+ {
201
+ const std::size_t n = cur_grid.n(), n_root = std::size_t(sqrt(n) + 0.5);
202
+
203
+ std::uint_fast64_t row_mask = row_colors(cur_grid, y),
204
+ col_mask = column_colors(cur_grid, x),
205
+ block_mask = block_colors(cur_grid, (x / n_root) * n_root, (y / n_root) * n_root);
206
+
207
+ return (~(row_mask | col_mask | block_mask)) & ((1 << n) - 1);
208
+ }
209
+
210
+ bool Validator::is_good_partial_row(Grid const& cur_grid, std::size_t y)
211
+ {
212
+ const std::size_t n = cur_grid.n();
213
+ std::uint_fast64_t mask = 0;
214
+
215
+ for (std::size_t x = 0; x < n; x++)
216
+ {
217
+ int a = cur_grid.get(x, y);
218
+
219
+ //ignore incomplete elements
220
+ if (a != -1)
221
+ {
222
+ //reject rows that have duplicates
223
+ if ((mask & (1 << (a - 1))) != 0)
224
+ {
225
+ return false;
226
+ }
227
+ else
228
+ {
229
+ mask |= (1 << (a - 1));
230
+ }
231
+ }
232
+ }
233
+
234
+ return true;
235
+ }
236
+
237
+ bool Validator::is_good_partial_column(Grid const& cur_grid, std::size_t x)
238
+ {
239
+ const std::size_t n = cur_grid.n();
240
+ std::uint_fast64_t mask = 0;
241
+
242
+ for (std::size_t y = 0; y < n; y++)
243
+ {
244
+ int a = cur_grid.get(x, y);
245
+
246
+ //ignore incomplete elements
247
+ if (a != -1)
248
+ {
249
+ //reject cols that have duplicates
250
+ if ((mask & (1 << (a - 1))) != 0)
251
+ {
252
+ return false;
253
+ }
254
+ else
255
+ {
256
+ mask |= (1 << (a - 1));
257
+ }
258
+ }
259
+ }
260
+
261
+ return true;
262
+ }
263
+
264
+ bool Validator::is_good_partial_block(Grid const& cur_grid, std::size_t x, std::size_t y)
265
+ {
266
+ const std::size_t n = cur_grid.n(), n_root = std::size_t(sqrt(n) + 0.5);
267
+ std::uint_fast64_t mask = 0;
268
+
269
+ for (std::size_t y_off = 0; y_off < n_root; y_off++)
270
+ {
271
+ for (std::size_t x_off = 0; x_off < n_root; x_off++)
272
+ {
273
+ int a = cur_grid.get(x + x_off, y + y_off);
274
+
275
+ //ignore incomplete elements
276
+ if (a != -1)
277
+ {
278
+ //reject blocks that have duplicates
279
+ if ((mask & (1 << (a - 1))) != 0)
280
+ {
281
+ return false;
282
+ }
283
+ else
284
+ {
285
+ mask |= (1 << (a - 1));
286
+ }
287
+ }
288
+ }
289
+ }
290
+
291
+ return true;
292
+ }
293
+
294
+ bool Validator::is_good_partial_board(Grid const& cur_grid)
295
+ {
296
+ const std::size_t n = cur_grid.n(), n_root = std::size_t(sqrt(n) + 0.5);
297
+
298
+ for (std::size_t y = 0; y < n; y++)
299
+ {
300
+ if (!is_good_partial_row(cur_grid, y))
301
+ {
302
+ return false;
303
+ }
304
+ }
305
+
306
+ for (std::size_t x = 0; x < n; x++)
307
+ {
308
+ if (!is_good_partial_column(cur_grid, x))
309
+ {
310
+ return false;
311
+ }
312
+ }
313
+
314
+ for (std::size_t x = 0; x < n_root; x++)
315
+ {
316
+ for (std::size_t y = 0; y < n_root; y++)
317
+ {
318
+ if (!is_good_partial_block(cur_grid, x * n_root, y * n_root))
319
+ {
320
+ return false;
321
+ }
322
+ }
323
+ }
324
+
325
+ return true;
326
+ }