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
@@ -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
|
+
}
|