sudoku_gem 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+ }