|
1 | 1 | /* Copyright 2023 Arjun Aravind */ |
2 | | -#ifndef SRC_SUDOKU_SUITE_H_ |
3 | | -#define SRC_SUDOKU_SUITE_H_ |
4 | | -#endif // SRC_SUDOKU_SUITE_H_ |
| 2 | +#ifndef SRC_GRID_H_ |
| 3 | +#define SRC_GRID_H_ |
5 | 4 |
|
6 | | -#include<iostream> |
7 | | -#include<fstream> |
8 | 5 | #include<array> |
| 6 | +#include<fstream> |
| 7 | +#include<iostream> |
9 | 8 | #include<set> |
| 9 | +#include<stdexcept> |
10 | 10 | #include<string> |
11 | | -#include<vector> |
12 | | -#include<algorithm> |
13 | 11 | #include<utility> |
14 | | -#include<stdexcept> |
15 | | -#include<chrono> |
16 | | -#include<random> |
17 | | - |
18 | | -namespace sudoku { |
19 | 12 |
|
20 | | -typedef std::pair<int, int> Coord; |
| 13 | +#include"../src/coord.h" |
21 | 14 |
|
22 | | -int GRID_LEN = 9; |
| 15 | +namespace sudoku { |
23 | 16 |
|
24 | 17 | class Grid { |
25 | 18 | /* A data structure that holds the Sudoku puzzle. */ |
@@ -176,6 +169,28 @@ class Grid { |
176 | 169 | return (element_found != coords_that_were_pre_filled.end()); |
177 | 170 | } |
178 | 171 |
|
| 172 | + /*------------------------*/ |
| 173 | + /* Cell-related Functions */ |
| 174 | + |
| 175 | + std::vector<int> get_possible_values_for_cell_at_coord(Coord coord) { |
| 176 | + std::vector<int> values = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; |
| 177 | + std::vector<int> filtered_values(values.size()); |
| 178 | + |
| 179 | + auto it = std::copy_if( |
| 180 | + values.begin(), values.end(), |
| 181 | + filtered_values.begin(), |
| 182 | + [this, coord] (int value) -> bool { |
| 183 | + return !( |
| 184 | + this->value_exists_elsewhere_in_column(coord, value) || |
| 185 | + this->value_exists_elsewhere_in_row(coord, value) || |
| 186 | + this->value_exists_elsewhere_in_3x3_grid(coord, value)); |
| 187 | + }); |
| 188 | + |
| 189 | + // Shrink vector to remove empty elements. |
| 190 | + filtered_values.resize(std::distance(filtered_values.begin(), it)); |
| 191 | + return filtered_values; |
| 192 | + } |
| 193 | + |
179 | 194 | /*--------------------*/ |
180 | 195 | /* Operator Overloads */ |
181 | 196 |
|
@@ -206,166 +221,6 @@ std::ostream& operator<< (std::ostream& out, Grid grid) { |
206 | 221 | return out; |
207 | 222 | } |
208 | 223 |
|
209 | | -/*------------------*/ |
210 | | -/* COORD FUNCTIONS */ |
211 | | -/*------------------*/ |
212 | | - |
213 | | -Coord get_next_cell_coord(Coord coord) { |
214 | | - /* Function which returns the next successive coordinate for a |
215 | | - * Sudoku grid, given a current coordinate. */ |
216 | | - if (coord.second == GRID_LEN-1 && coord.first == GRID_LEN-1) |
217 | | - return coord; |
218 | | - else if (coord.second == GRID_LEN-1) |
219 | | - return std::make_pair(coord.first+1, 0); |
220 | | - return std::make_pair(coord.first, coord.second + 1); |
221 | | -} |
222 | | - |
223 | | -std::set<Coord> get_N_random_cell_coords(int n) { |
224 | | - std::random_device rd; // obtain a random number from hardware |
225 | | - std::mt19937 gen(rd()); // seed the generator |
226 | | - std::uniform_int_distribution<> distr(0, GRID_LEN-1); // define the range |
227 | | - |
228 | | - std::set<Coord> random_cell_coords; |
229 | | - |
230 | | - while (n > 0) { |
231 | | - auto cell_coord = std::make_pair(distr(gen), distr(gen)); |
232 | | - auto inserted = random_cell_coords.insert(cell_coord); |
233 | | - |
234 | | - if (!inserted.second) continue; // Element already existed. |
235 | | - n--; |
236 | | - } |
237 | | - |
238 | | - return random_cell_coords; |
239 | | -} |
240 | | - |
241 | | -std::vector<int> get_possible_values_for_cell_at_coord( |
242 | | - const Grid& grid, |
243 | | - Coord coord |
244 | | -) { |
245 | | - std::vector<int> values = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; |
246 | | - std::vector<int> filtered_values(values.size()); |
247 | | - |
248 | | - auto it = std::copy_if( |
249 | | - values.begin(), values.end(), |
250 | | - filtered_values.begin(), |
251 | | - [&grid, coord] (int value) -> bool { |
252 | | - return !( |
253 | | - grid.value_exists_elsewhere_in_column(coord, value) || |
254 | | - grid.value_exists_elsewhere_in_row(coord, value) || |
255 | | - grid.value_exists_elsewhere_in_3x3_grid(coord, value)); |
256 | | - }); |
257 | | - |
258 | | - // Shrink vector to remove empty elements. |
259 | | - filtered_values.resize(std::distance(filtered_values.begin(), it)); |
260 | | - return filtered_values; |
261 | | -} |
262 | | - |
263 | | -/*----------------------*/ |
264 | | -/* GENERATION FUNCTIONS */ |
265 | | -/*----------------------*/ |
266 | | - |
267 | | - |
268 | | -bool fill_with_valid_solution( |
269 | | - Grid *grid, |
270 | | - Coord curr_coord = std::make_pair(0, 0) |
271 | | -) { |
272 | | - auto next_coord = get_next_cell_coord(curr_coord); |
273 | | - |
274 | | - auto values = get_possible_values_for_cell_at_coord(*grid, curr_coord); |
275 | | - if (values.size() == 0) return false; |
276 | | - |
277 | | - // We randomise the values so that we get a random solution each time. |
278 | | - auto rnd_seed = std::chrono::system_clock::now().time_since_epoch().count(); |
279 | | - std::shuffle( |
280 | | - values.begin(), |
281 | | - values.end(), |
282 | | - std::default_random_engine(rnd_seed)); |
283 | | - |
284 | | - for (auto value : values) { |
285 | | - grid->update(curr_coord, value); |
286 | | - if (curr_coord == std::make_pair(8, 8)) return true; // Done |
287 | | - |
288 | | - bool next_cell_is_filled = fill_with_valid_solution(grid, next_coord); |
289 | | - if (next_cell_is_filled) return true; |
290 | | - |
291 | | - /* If this value didn't work, we need to clear the grid of all the |
292 | | - * values that have been filled as a result of this. */ |
293 | | - grid->clear_values_starting_from_coord(curr_coord); |
294 | | - } |
295 | | - |
296 | | - return false; |
297 | | -} |
298 | | - |
299 | | -void remove_values_from_solution(Grid *grid, int values_to_remove) { |
300 | | - auto random_cell_coords = get_N_random_cell_coords(values_to_remove); |
301 | | - for (auto coord : random_cell_coords) grid->update(coord, 0); |
302 | | -} |
303 | | - |
304 | | -/*-----------------*/ |
305 | | -/* SOLVE FUNCTIONS */ |
306 | | -/*-----------------*/ |
307 | | - |
308 | | -bool _solve( |
309 | | - Grid *grid, |
310 | | - Coord cell_coord = std::make_pair(0, 0) |
311 | | -) { |
312 | | - auto next_coord = get_next_cell_coord(cell_coord); |
313 | | - |
314 | | - if (grid->coord_was_pre_filled(cell_coord)) { |
315 | | - if (cell_coord == std::make_pair(8, 8)) return true; |
316 | | - return _solve(grid, next_coord); |
317 | | - } |
318 | | - |
319 | | - auto values = get_possible_values_for_cell_at_coord(*grid, cell_coord); |
320 | | - if (values.size() == 0) return false; |
321 | | - |
322 | | - for (auto value : values) { |
323 | | - grid->update(cell_coord, value); |
324 | | - if (cell_coord == std::make_pair(8, 8)) return true; // Solved! |
325 | | - |
326 | | - bool next_cell_is_solved = _solve(grid, next_coord); |
327 | | - if (next_cell_is_solved) return true; |
328 | | - |
329 | | - /* If this value didn't work, we need to clear the grid of all the |
330 | | - * values that have been filled as a result of this. */ |
331 | | - grid->clear_values_starting_from_coord(cell_coord); |
332 | | - } |
333 | | - |
334 | | - if (cell_coord == std::make_pair(0, 0)) throw std::logic_error( |
335 | | - "This puzzle doesn't have a solution!"); |
336 | | - |
337 | | - return false; |
338 | | -} |
339 | | - |
340 | | -/*-----------------*/ |
341 | | -/* SUITE FUNCTIONS */ |
342 | | -/*-----------------*/ |
343 | | - |
344 | | -void solve(Grid *grid) { |
345 | | - _solve(grid); |
346 | | -} |
347 | | - |
348 | | -bool is_valid_solution( |
349 | | - const Grid& grid, |
350 | | - Coord curr_coord = std::make_pair(0, 0) |
351 | | -) { |
352 | | - int value = grid.get(curr_coord); |
353 | | - if (value == 0) return false; // It's unfinished. |
354 | | - else if ( |
355 | | - grid.value_exists_elsewhere_in_column(curr_coord, value) || |
356 | | - grid.value_exists_elsewhere_in_row(curr_coord, value) || |
357 | | - grid.value_exists_elsewhere_in_3x3_grid(curr_coord, value)) |
358 | | - return false; |
359 | | - if (curr_coord == std::make_pair(8, 8)) return true; |
360 | | - else |
361 | | - return is_valid_solution(grid, get_next_cell_coord(curr_coord)); |
362 | | -} |
363 | | - |
364 | | -Grid generate_puzzle() { |
365 | | - Grid grid; |
366 | | - fill_with_valid_solution(&grid); |
367 | | - remove_values_from_solution(&grid, 30); |
368 | | - return grid; |
369 | | -} |
370 | | - |
371 | 224 | } // namespace sudoku |
| 225 | + |
| 226 | +#endif // SRC_GRID_H_ |
0 commit comments