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