Sudoku Solver 1.0
Загрузка...
Поиск...
Не найдено
sudoku_solver.h
См. документацию.
1#pragma once
2#include <array>
3#include <vector>
4#include <concepts>
6#include <expected>
7
13constexpr size_t constexpr_sqrt(size_t n) {
14 if (n == 0 || n == 1) return n;
15 size_t lo = 1, hi = n;
16 while (lo <= hi) {
17 size_t mid = lo + (hi - lo) / 2;
18 if (mid * mid == n) return mid;
19 if (mid * mid < n) lo = mid + 1;
20 else hi = mid - 1;
21 }
22 return hi;
23}
24
30constexpr bool isPerfectSquare(size_t n) {
31 for (size_t i = 1; i * i <= n; ++i) {
32 if (i * i == n) return true;
33 }
34 return false;
35}
36
41template<size_t N>
43
48template<size_t N> requires ValidSudokuSize<N>
49class SudokuSolver final {
50public:
54 using Board = std::array<std::array<int, N>, N>;
55
59 using FixedCell = std::tuple<int, int, int>; // row, col, digit
60
68
73 explicit SudokuSolver(const Board& board);
74
79 explicit SudokuSolver(const std::vector<FixedCell>& fixed_cells);
80
81 // Правило пяти (явно)
82 SudokuSolver(const SudokuSolver&) = default;
86 ~SudokuSolver() = default;
87
92 std::expected<Board, SolverError> solve() const;
93
94private:
95 std::vector<FixedCell> fixed_cells; //начальные клетки
96
97 //константы времени компиляции
98 static constexpr size_t BLOCK_SIZE = constexpr_sqrt(N);
99 static constexpr size_t CELLS = N * N;
100 static constexpr size_t CONSTRAINTS_COUNT = 4;
101 static constexpr size_t TOTAL_COLUMNS = CONSTRAINTS_COUNT * CELLS;
102
103 // Вспомогательные методы
104private:
105 std::vector<FixedCell> fixed_cells;
106
107 // константы времени компиляции
108 static constexpr size_t BLOCK_SIZE = constexpr_sqrt(N);
109 static constexpr size_t CELLS = N * N;
110 static constexpr size_t CONSTRAINTS_COUNT = 4;
111 static constexpr size_t TOTAL_COLUMNS = CONSTRAINTS_COUNT * CELLS;
112
113 // Вспомогательные методы
121 static std::array<int, SudokuSolver<N>::CONSTRAINTS_COUNT> getColumnIndices(int row, int col, int digit);
122
130 static int encodeRowId(int row, int col, int digit);
131
137 static FixedCell decodeRowId(int row_id);
138
143 void fillExactCoverMatrix(DancingLinksMatrix& dlx) const;
144
150 bool applyInitialConstraints(DancingLinksMatrix& dlx) const;
151
157 Board rebuildBoardFromSolution(const std::vector<int>& solution) const;
158};
159
160// ============================================================================
161// Реализация
162// ============================================================================
163
164// Конструкторы
165template<size_t N> requires ValidSudokuSize<N>
166SudokuSolver<N>::SudokuSolver(const std::vector<FixedCell>& fixed_cells)
167 : fixed_cells(fixed_cells) {}
168
169template<size_t N> requires ValidSudokuSize<N>
171 for (size_t row = 0; row < N; ++row) {
172 for (size_t col = 0; col < N; ++col) {
173 if (board[row][col] != 0) {
174 fixed_cells.emplace_back(row, col, board[row][col]);
175 }
176 }
177 }
178}
179
180// Решение
181template<size_t N> requires ValidSudokuSize<N>
182std::expected<typename SudokuSolver<N>::Board, typename SudokuSolver<N>::SolverError>
184
185 DancingLinksMatrix dlx(TOTAL_COLUMNS);
186 fillExactCoverMatrix(dlx);
187
188 // Проверка начальных ограничений
189 if (!applyInitialConstraints(dlx)) {
190 return std::unexpected(SolverError::INVALID_CONSTRAINTS);
191 }
192
193 auto solution = dlx.search(CELLS - fixed_cells.size());
194 if (!solution.has_value()) {
195 return std::unexpected(SolverError::NO_SOLUTION);
196 }
197
198 return rebuildBoardFromSolution(solution.value());
199}
200
201// Служебные методы
202template<size_t N> requires ValidSudokuSize<N>
203std::array<int, SudokuSolver<N>::CONSTRAINTS_COUNT> SudokuSolver<N>::getColumnIndices
204(int row, int col, int digit) {
205
206 int d = digit - 1; //индексы с 0, цифры начинаются с 1
207 int block = (row / BLOCK_SIZE) * BLOCK_SIZE + (col / BLOCK_SIZE);
208
209 int cell_idx = row * N + col;
210 int row_idx = CELLS + row * N + d;
211 int col_idx = 2 * CELLS + col * N + d;
212 int block_idx = 3 * CELLS + block * N + d;
213 return { cell_idx, row_idx, col_idx, block_idx};
214}
215
216template<size_t N> requires ValidSudokuSize<N>
217int SudokuSolver<N>::encodeRowId(int row, int col, int digit) {
218 return ((row * N + col) * N + (digit - 1));
219}
220
221template<size_t N> requires ValidSudokuSize<N>
222SudokuSolver<N>::FixedCell SudokuSolver<N>::decodeRowId(int row_id) {
223 int digit_minus_1 = row_id % N;
224 int cell = row_id / N;
225 int row = cell / N;
226 int col = cell % N;
227 return { row, col, digit_minus_1 + 1 };
228}
229
230template<size_t N> requires ValidSudokuSize<N>
231void SudokuSolver<N>::fillExactCoverMatrix(DancingLinksMatrix& dlx) const {
232
233 for (size_t row = 0; row != N; ++row) {
234 for (size_t col = 0; col != N; ++col) {
235 for (int digit = 1; digit <= N; ++digit) {
236
237 int row_id = encodeRowId(row, col, digit);
238 auto indices = getColumnIndices(row, col, digit);
239
240 dlx.addRow(indices, row_id);
241 }
242 }
243 }
244}
245
246template<size_t N> requires ValidSudokuSize<N>
247bool SudokuSolver<N>::applyInitialConstraints(DancingLinksMatrix& dlx) const {
248 for (const auto& [row, col, digit] : fixed_cells) {
249 for (int col_idx : getColumnIndices(row, col, digit)) {
250 if (digit < 1 || digit > N || dlx.isCovered(col_idx)) {
251 return false; // конфликт
252 }
253 dlx.cover(col_idx);
254 }
255 }
256 return true;
257}
258
259template<size_t N> requires ValidSudokuSize<N>
261SudokuSolver<N>::rebuildBoardFromSolution(const std::vector<int>& solution) const {
262
263 Board board{};
264 // Заполняем начальные значения
265 for (const auto& [row, col, digit] : fixed_cells) {
266 board[row][col] = digit;
267 }
268 // Заполняем найденные алгоритмом
269 for (int row_id : solution) {
270 auto [row, col, digit] = decodeRowId(row_id);
271 board[row][col] = digit;
272 }
273 return board;
274}
SudokuSolver & operator=(const SudokuSolver &)=default
std::array< std::array< int, N >, N > Board
Тип доски: квадратная матрица N×N, 0 обозначает пустую клетку
Определения sudoku_solver.h:54
SudokuSolver(const SudokuSolver &)=default
std::expected< Board, SolverError > solve() const
Решает судоку с заданными начальными условиями
Определения sudoku_solver.h:183
SudokuSolver(const Board &board)
Конструктор от полной доски
Определения sudoku_solver.h:170
SolverError
Ошибки, которые может вернуть метод solve().
Определения sudoku_solver.h:64
@ INVALID_CONSTRAINTS
Начальные данные противоречивы
Определения sudoku_solver.h:65
@ NO_SOLUTION
Решения не существует
Определения sudoku_solver.h:66
SudokuSolver(SudokuSolver &&)=default
std::tuple< int, int, int > FixedCell
Тип ячейки с фиксированным значением: (строка, столбец, цифра).
Определения sudoku_solver.h:59
~SudokuSolver()=default
SudokuSolver & operator=(SudokuSolver &&)=default
Концепт: размер судоку должен быть полным квадратом
Определения sudoku_solver.h:42
constexpr size_t constexpr_sqrt(size_t n)
Вычисление целочисленного квадратного корня на этапе компиляции
Определения sudoku_solver.h:13
constexpr bool isPerfectSquare(size_t n)
Проверяет, является ли число полным квадратом
Определения sudoku_solver.h:30