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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cc3301c8876d099d322ce989574598232841a388
4
+ data.tar.gz: 333dc8fa1b65c8296e571c7316279e660405556d
5
+ SHA512:
6
+ metadata.gz: 6aec0f8e6e95b1b38b0a8212b425f108f429f13018a702d7e7f57d80235457ace45627fbc3e5405104a1cf5917bb0098a4e3a1ff1f449372625a621dad072435
7
+ data.tar.gz: 9f28eae02891e28f1388b0fab319bc6b93b99b271c58148a592e341618f4cf19d011ce6887ca1058695a43df9877d4425029fd8788a3c9aa5fab6cf18c5d18af
@@ -0,0 +1,8 @@
1
+ require 'mkmf'
2
+
3
+ CONFIG['CC'] = CONFIG['CXX'] or 'g++'
4
+ $CFLAGS << ' --std=c++11'
5
+ have_library('stdc++')
6
+ $warnflags.gsub!(' -Wdeclaration-after-statement','')
7
+ $warnflags.gsub!(' -Wimplicit-function-declaration','')
8
+ create_makefile 'sudoku_gem/sudoku_gem'
@@ -0,0 +1,89 @@
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 "grid.h"
20
+
21
+ #include <sstream>
22
+
23
+ Grid::Grid(std::size_t n) : matrix(boost::extents[n][n]), dim(n)
24
+ {
25
+ }
26
+
27
+ Grid::Grid(const Grid& grid) : matrix(grid.matrix), dim(grid.dim)
28
+ {
29
+ }
30
+
31
+ Grid& Grid::operator=(const Grid& grid)
32
+ {
33
+ this->matrix = grid.matrix;
34
+ this->dim = grid.dim;
35
+ return *this;
36
+ }
37
+
38
+ int Grid::get(std::size_t x, std::size_t y) const
39
+ {
40
+ return this->matrix[y][x];
41
+ }
42
+
43
+ void Grid::set(std::size_t x, std::size_t y, int i)
44
+ {
45
+ this->matrix[y][x] = i;
46
+ }
47
+
48
+ std::size_t Grid::n() const
49
+ {
50
+ return this->dim;
51
+ }
52
+
53
+ void Grid::reset(std::size_t n)
54
+ {
55
+ this->matrix.resize(boost::extents[n][n]);
56
+ this->dim = n;
57
+ }
58
+
59
+ std::string Grid::to_s() const
60
+ {
61
+ std::stringstream out;
62
+ this->to_s(out);
63
+ return out.str();
64
+ }
65
+
66
+ void Grid::to_s(std::ostream& out) const
67
+ {
68
+ for (std::size_t y = 0; y < this->dim; y++)
69
+ {
70
+ if (y != 0)
71
+ {
72
+ out << std::endl;
73
+ }
74
+
75
+ for (std::size_t x = 0; x < this->dim; x++)
76
+ {
77
+ if (x != 0)
78
+ {
79
+ out << ' ';
80
+ }
81
+
82
+ out << this->matrix[y][x];
83
+ }
84
+ }
85
+ }
86
+
87
+ Grid::~Grid()
88
+ {
89
+ }
@@ -0,0 +1,115 @@
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 GRID_H
20
+ #define GRID_H
21
+
22
+ #include <cstddef>
23
+ #include <string>
24
+ #include <streambuf>
25
+ #include <boost/multi_array.hpp>
26
+
27
+ /**
28
+ * @brief A class that represents the state of a Sudoku board
29
+ *
30
+ * The Grid class represents the current state of a particular Sudoku board. It is essentially a
31
+ * wrapper around a square, 2D array, except that it does not need to be defined at compile-time.
32
+ * The grid is stored in an underlying boost::multi_array<int, 2>, and it has all the typical
33
+ * accessors: set an element, get an element, get the grid size, and set the grid size.
34
+ **/
35
+ class Grid
36
+ {
37
+ public:
38
+ /**
39
+ * @brief Construct a n*n grid
40
+ *
41
+ * @param n Side length of the square, 2D array.
42
+ **/
43
+ Grid(std::size_t n);
44
+ /**
45
+ * @brief Copy-construct a grid from another grid
46
+ *
47
+ * @param grid The other grid.
48
+ **/
49
+ Grid(Grid const& grid);
50
+ virtual ~Grid();
51
+
52
+ /**
53
+ * @brief Copy another grid
54
+ *
55
+ * @param grid The other grid.
56
+ * @return Grid& The current grid.
57
+ **/
58
+ Grid& operator =(Grid const& grid);
59
+
60
+ /**
61
+ * @brief Resize the existing grid so that it is an n*n grid
62
+ *
63
+ * @param n Side length of the square, 2D array.
64
+ **/
65
+ void reset(std::size_t n);
66
+
67
+ /**
68
+ * @brief Get the value of a specific element
69
+ *
70
+ * @param x The X-index of the element.
71
+ * @param y The Y-index of the element.
72
+ * @return int The value of the element.
73
+ **/
74
+ int get(std::size_t x, std::size_t y) const;
75
+ /**
76
+ * @brief Set the value of a specific element
77
+ *
78
+ * @param x The X-index of the element.
79
+ * @param y The Y-index of the element.
80
+ * @param i The new value of the element.
81
+ **/
82
+ void set(std::size_t x, std::size_t y, int i);
83
+
84
+ /**
85
+ * @brief The side length of the square, 2D array
86
+ *
87
+ * @return std::size_t The side-length.
88
+ **/
89
+ std::size_t n() const;
90
+
91
+ /**
92
+ * @brief Show the grid in a human-readable form.
93
+ *
94
+ * @return std::string The grid.
95
+ **/
96
+ std::string to_s() const;
97
+ /**
98
+ * @brief Convert the grid to a human-readable form and pipe that into the given output stream.
99
+ *
100
+ * @param out The output stream.
101
+ **/
102
+ void to_s(std::ostream& out) const;
103
+
104
+ private:
105
+ /**
106
+ * @brief The underlying grid.
107
+ **/
108
+ boost::multi_array<int, 2> matrix;
109
+ /**
110
+ * @brief The side length of the grid.
111
+ **/
112
+ std::size_t dim;
113
+ };
114
+
115
+ #endif // GRID_H
@@ -0,0 +1,48 @@
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 "sudoku.h"
20
+ #include <iostream>
21
+
22
+ int main(int argc, char* argv[])
23
+ {
24
+ Sudoku puzzle;
25
+ puzzle.read_puzzle_from_file(std::cin);
26
+
27
+ if (puzzle)
28
+ {
29
+ std::cout << "Read the board in OK." << std::endl;
30
+ }
31
+ else
32
+ {
33
+ std::cout << "Failed to read the board from cin." << std::endl;
34
+ return 1;
35
+ }
36
+
37
+ std::cout << "Here's the current state of the board." << std::endl;
38
+ puzzle.print(std::cout);
39
+
40
+ std::cout << std::endl << "Solving the puzzle..." << std::endl;
41
+
42
+ puzzle.solve_colorability_style();
43
+
44
+ std::cout << "A solution was found!" << std::endl;
45
+ puzzle.print(std::cout);
46
+
47
+ return 0;
48
+ }
@@ -0,0 +1,385 @@
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 "sudoku.h"
20
+ #include "validator.h"
21
+
22
+ #include <stdexcept>
23
+ #include <sstream>
24
+ #include <algorithm>
25
+ #include <boost/algorithm/string.hpp>
26
+
27
+ Sudoku::Sudoku() : grid(0), status_ok(false)
28
+ {
29
+ }
30
+
31
+ bool Sudoku::insert_row(std::vector<std::string>& tokens, std::size_t y)
32
+ {
33
+ for (std::size_t x = 0; x < this->grid.n(); x++)
34
+ {
35
+ std::string const& token = tokens[x];
36
+
37
+ try
38
+ {
39
+ int value = std::stoi(token);
40
+
41
+ //integer tokens must be between 1 and n, and unknowns should be question marks
42
+ if (value < 1 || value > (int)this->grid.n())
43
+ {
44
+ //invalid value in board
45
+ return false;
46
+ }
47
+ else
48
+ {
49
+ this->grid.set(x, y, value);
50
+ }
51
+ }
52
+ catch (std::invalid_argument &)
53
+ {
54
+ if (token == "?")
55
+ {
56
+ this->grid.set(x, y, -1);
57
+ }
58
+ else
59
+ {
60
+ //invalid character in board
61
+ return false;
62
+ }
63
+ }
64
+ }
65
+
66
+ return true;
67
+ }
68
+
69
+ bool Sudoku::parse_puzzle(std::istream& f)
70
+ {
71
+ std::string line;
72
+ std::vector<std::string> tokens;
73
+
74
+ //read the first line, to figure out n
75
+ std::getline(f, line);
76
+ boost::split(tokens, line, boost::is_any_of(" "));
77
+ std::size_t n = tokens.size();
78
+
79
+ if (n == 0 || line.empty())
80
+ {
81
+ //empty puzzle
82
+ return false;
83
+ }
84
+
85
+ //make sure n is a perfect square
86
+ std::size_t n_sq = std::size_t(sqrt(n));
87
+
88
+ if (n_sq * n_sq != n && (n_sq + 1) * (n_sq + 1) != n)
89
+ {
90
+ //n is not a perfect square
91
+ return false;
92
+ }
93
+
94
+ //make sure n is not too big
95
+
96
+ if (n > 64)
97
+ {
98
+ //the board cannot be solved with this program
99
+ return false;
100
+ }
101
+
102
+ //create the n*n grid
103
+ this->grid.reset(n);
104
+
105
+ //put in the first row
106
+ this->insert_row(tokens, 0);
107
+
108
+ //read n-1 lines
109
+ for (std::size_t y = 1; y < n; y++)
110
+ {
111
+ std::getline(f, line);
112
+ boost::split(tokens, line, boost::is_any_of(" "));
113
+
114
+ //each line must have n tokens
115
+ if (tokens.size() != n)
116
+ {
117
+ //invalid board
118
+ return false;
119
+ }
120
+
121
+ //insert that row
122
+ this->insert_row(tokens, y);
123
+ }
124
+
125
+ return true;
126
+ }
127
+
128
+ bool Sudoku::validate() const
129
+ {
130
+ //whether this board is solvable
131
+ return Validator::is_good_partial_board(this->grid);
132
+ }
133
+
134
+ bool Sudoku::read_puzzle_from_file(std::istream& f)
135
+ {
136
+ if (this->parse_puzzle(f))
137
+ {
138
+ if (this->validate())
139
+ {
140
+ this->status_ok = true;
141
+ return true;
142
+ }
143
+ }
144
+
145
+ return false;
146
+ }
147
+
148
+ bool Sudoku::read_puzzle_from_string(std::string const& s)
149
+ {
150
+ std::stringstream iss(s);
151
+
152
+ if (this->parse_puzzle(iss))
153
+ {
154
+ if (this->validate())
155
+ {
156
+ this->status_ok = true;
157
+ return true;
158
+ }
159
+ }
160
+
161
+ return false;
162
+ }
163
+
164
+ void Sudoku::print(std::ostream& out) const
165
+ {
166
+ if (!this->status_ok)
167
+ {
168
+ throw std::logic_error("Puzzle has not been initialized");
169
+ }
170
+
171
+ out << this->to_s() << std::endl;
172
+ }
173
+
174
+ std::string Sudoku::to_s() const
175
+ {
176
+ std::string str = this->grid.to_s();
177
+ boost::replace_all(str, "-1", "?");
178
+ return str;
179
+ }
180
+
181
+ bool Sudoku::find_unknown(Grid& cur_grid, std::size_t cur_x, std::size_t cur_y,
182
+ std::size_t& x_out, std::size_t& y_out)
183
+ {
184
+ //find the next unknown node from where we left off, so we don't need to re-examine any elements
185
+ for (std::size_t y = cur_y; y < cur_grid.n(); y++, cur_x = 0)
186
+ {
187
+ for (std::size_t x = cur_x; x < cur_grid.n(); x++)
188
+ {
189
+ if (cur_grid.get(x, y) == -1)
190
+ {
191
+ x_out = x;
192
+ y_out = y;
193
+ return true;
194
+ }
195
+ }
196
+ }
197
+
198
+ return false;
199
+ }
200
+
201
+ bool Sudoku::color_node(Grid& cur_grid, std::size_t cur_x, std::size_t cur_y)
202
+ {
203
+ std::size_t unknown_x, unknown_y;
204
+
205
+ //check if we can keep coloring nodes, or if we need to stop and assess the generated board
206
+ if (find_unknown(cur_grid, cur_x, cur_y, unknown_x, unknown_y))
207
+ {
208
+ std::uint_fast64_t colors = Validator::good_colors(cur_grid, unknown_x, unknown_y);
209
+
210
+ //clone the existing game board
211
+ Grid new_grid(cur_grid);
212
+
213
+ for (int i = 1; i <= (int)cur_grid.n(); i++)
214
+ {
215
+ //can we use this color here?
216
+ if ((colors & (1 << (i - 1))) != 0)
217
+ {
218
+ //color the node
219
+ new_grid.set(unknown_x, unknown_y, i);
220
+
221
+ //if the coloring was successful, then return the colored graph indicate success
222
+ if (color_node(new_grid, unknown_x, unknown_y))
223
+ {
224
+ cur_grid = new_grid;
225
+ return true;
226
+ }
227
+ }
228
+ }
229
+
230
+ //we couldn't find a coloring :(
231
+ return false;
232
+ }
233
+ else
234
+ {
235
+ //the board is completely colored, so we can't color any more nodes, but is it a valid coloring?
236
+ return Validator::is_good_board(cur_grid);
237
+ }
238
+ }
239
+
240
+ void Sudoku::solve_colorability_style()
241
+ {
242
+ if (!this->status_ok)
243
+ {
244
+ throw std::logic_error("Puzzle has not been initialized");
245
+ }
246
+
247
+ color_node(this->grid);
248
+ }
249
+
250
+ bool Sudoku::bruteforce_node(Grid& cur_grid, std::size_t cur_x, std::size_t cur_y)
251
+ {
252
+ std::size_t unknown_x, unknown_y;
253
+
254
+ //check if we can keep coloring nodes, or if we need to stop and assess the generated board
255
+ if (find_unknown(cur_grid, cur_x, cur_y, unknown_x, unknown_y))
256
+ {
257
+ //clone the existing game board
258
+ Grid new_grid(cur_grid);
259
+
260
+ for (int i = 1; i <= (int)cur_grid.n(); i++)
261
+ {
262
+ //color the cell value
263
+ new_grid.set(unknown_x, unknown_y, i);
264
+
265
+ //if the coloring was successful, then return the colored graph indicate success
266
+ if (bruteforce_node(new_grid, unknown_x, unknown_y))
267
+ {
268
+ cur_grid = new_grid;
269
+ return true;
270
+ }
271
+ }
272
+
273
+ //we couldn't find a solution :(
274
+ return false;
275
+ }
276
+ else
277
+ {
278
+ //the board is completely colored, so we can't color any more nodes, but is it a valid coloring?
279
+ return Validator::is_good_board(cur_grid);
280
+ }
281
+ }
282
+
283
+ void Sudoku::solve_bruteforce_style()
284
+ {
285
+ if (!this->status_ok)
286
+ {
287
+ throw std::logic_error("Puzzle has not been initialized");
288
+ }
289
+
290
+ bruteforce_node(this->grid);
291
+ }
292
+
293
+ int Sudoku::singular_decider(Grid& cur_grid, bool found_one, std::size_t cur_x, std::size_t cur_y)
294
+ {
295
+ std::size_t unknown_x, unknown_y;
296
+
297
+ //check if we can keep coloring nodes, or if we need to stop and assess the generated board
298
+ if (find_unknown(cur_grid, cur_x, cur_y, unknown_x, unknown_y))
299
+ {
300
+ std::uint_fast64_t colors = Validator::good_colors(cur_grid, unknown_x, unknown_y);
301
+
302
+ //clone the existing game board
303
+ Grid new_grid(cur_grid);
304
+
305
+ for (int i = 1; i <= (int)cur_grid.n(); i++)
306
+ {
307
+ //can we use this color here?
308
+ if ((colors & (1 << (i - 1))) != 0)
309
+ {
310
+ //color the node
311
+ new_grid.set(unknown_x, unknown_y, i);
312
+
313
+ //if the coloring was successful, then return the colored graph indicate success
314
+ switch (singular_decider(new_grid, found_one, unknown_x, unknown_y))
315
+ {
316
+ case 0: { break; } //that branch did not yield a solution, so keep going
317
+ case 1: { found_one = true; break; } //found one solution, so keep going
318
+ case 2: { return 2; } //confirmed multiple solutions, so stop
319
+ }
320
+ }
321
+ }
322
+
323
+ //we're done with this branch
324
+ return found_one ? 1 : 0;
325
+ }
326
+ else
327
+ {
328
+ //the board is completely colored, so we can't color any more nodes, but is it a valid coloring?
329
+ if (Validator::is_good_board(cur_grid))
330
+ {
331
+ if (found_one)
332
+ {
333
+ //oh no, we've got multiple solutions!
334
+ return 2;
335
+ }
336
+ else
337
+ {
338
+ //this is just the first solution
339
+ return 1;
340
+ }
341
+ }
342
+ else
343
+ {
344
+ //this branch did not yield a solution
345
+ return 0;
346
+ }
347
+ }
348
+ }
349
+
350
+ bool Sudoku::singular()
351
+ {
352
+ if (!this->status_ok)
353
+ {
354
+ throw std::logic_error("Puzzle has not been initialized");
355
+ }
356
+
357
+ if (this->validate())
358
+ {
359
+ return (singular_decider(this->grid) == 1);
360
+ }
361
+ else
362
+ {
363
+ return false;
364
+ }
365
+ }
366
+
367
+ bool Sudoku::good() const
368
+ {
369
+ return this->status_ok;
370
+ }
371
+
372
+ bool Sudoku::bad() const
373
+ {
374
+ return !this->good();
375
+ }
376
+
377
+ Sudoku::operator bool() const
378
+ {
379
+ return this->good();
380
+ }
381
+
382
+ Sudoku::~Sudoku()
383
+ {
384
+
385
+ }