sudoku_gem 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }