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 +7 -0
- data/ext/sudoku_gem/extconf.rb +8 -0
- data/ext/sudoku_gem/grid.cpp +89 -0
- data/ext/sudoku_gem/grid.h +115 -0
- data/ext/sudoku_gem/main.cpp +48 -0
- data/ext/sudoku_gem/sudoku.cpp +385 -0
- data/ext/sudoku_gem/sudoku.h +206 -0
- data/ext/sudoku_gem/sudoku_gem.c +31 -0
- data/ext/sudoku_gem/validator.cpp +326 -0
- data/ext/sudoku_gem/validator.h +171 -0
- data/lib/sudoku_gem.rb +13 -0
- metadata +54 -0
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,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
|
+
}
|