1use super::{latin_square, ExactCover};
6use std::collections::HashSet;
7
8#[derive(Debug)]
10pub struct Sudoku {
11 pub possibilities: Vec<Possibility>,
14 pub constraints: Vec<Constraint>,
16}
17
18impl Sudoku {
19 pub fn new(
24 box_side_length: usize,
25 filled_values: impl IntoIterator<Item = latin_square::Possibility>,
26 ) -> Self {
27 let side_length = box_side_length * box_side_length;
28 let filled_values: Vec<_> = filled_values.into_iter().collect();
29
30 let latin = latin_square::LatinSquare::new(side_length, filled_values.iter().copied());
31
32 let satisfied: HashSet<_> = filled_values
33 .iter()
34 .copied()
35 .map(|latin_poss| Possibility::from_latin(latin_poss, box_side_length))
36 .flat_map(Possibility::satisfied_constraints)
37 .collect();
38
39 let possibilities = latin
40 .possibilities
41 .into_iter()
42 .map(|latin_poss| Possibility::from_latin(latin_poss, box_side_length))
43 .collect();
44
45 let constraints = latin
46 .constraints
47 .into_iter()
48 .map(Constraint::from)
49 .chain(Constraint::all_square_number(box_side_length))
50 .filter(|cons| !satisfied.contains(cons))
51 .collect();
52
53 Self {
54 possibilities,
55 constraints,
56 }
57 }
58}
59
60impl ExactCover for Sudoku {
61 type Constraint = Constraint;
62 type Possibility = Possibility;
63
64 fn satisfies(&self, poss: &Self::Possibility, cons: &Self::Constraint) -> bool {
65 use Constraint::*;
66
67 match cons {
68 Latin(latin_cons) => {
69 <Possibility as Into<latin_square::Possibility>>::into(*poss).satisfies(latin_cons)
70 }
71 SquareNumber { square, value } => poss.square == *square && poss.value == *value,
72 }
73 }
74
75 fn is_optional(&self, _cons: &Self::Constraint) -> bool {
76 false
77 }
78
79 fn possibilities(&self) -> &[Self::Possibility] {
80 &self.possibilities
81 }
82
83 fn constraints(&self) -> &[Self::Constraint] {
84 &self.constraints
85 }
86}
87
88#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
90pub struct Possibility {
91 pub row: usize,
96
97 pub column: usize,
102
103 pub square: usize,
110
111 pub value: usize,
116}
117
118impl Possibility {
119 pub fn from_latin(latin: latin_square::Possibility, box_side_length: usize) -> Self {
121 let side_length = box_side_length * box_side_length;
122 let index = latin.row * side_length + latin.column;
123 let square = ((index % side_length) / box_side_length)
124 + box_side_length * (index / (side_length * box_side_length));
125
126 Possibility {
127 row: latin.row,
128 column: latin.column,
129 value: latin.value,
130 square,
131 }
132 }
133
134 pub fn satisfied_constraints(self) -> impl Iterator<Item = Constraint> {
137 [
138 Constraint::Latin(latin_square::Constraint::RowNumber {
139 row: self.row,
140 value: self.value,
141 }),
142 Constraint::Latin(latin_square::Constraint::ColumnNumber {
143 column: self.column,
144 value: self.value,
145 }),
146 Constraint::Latin(latin_square::Constraint::RowColumn {
147 row: self.row,
148 column: self.column,
149 }),
150 Constraint::SquareNumber {
151 square: self.square,
152 value: self.value,
153 },
154 ]
155 .into_iter()
156 }
157}
158
159impl From<Possibility> for latin_square::Possibility {
160 fn from(src: Possibility) -> Self {
161 latin_square::Possibility {
162 row: src.row,
163 column: src.column,
164 value: src.value,
165 }
166 }
167}
168
169#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
171pub enum Constraint {
172 Latin(latin_square::Constraint),
174 SquareNumber {
177 square: usize,
179 value: usize,
181 },
182}
183
184impl Constraint {
185 fn all_square_number(box_side_length: usize) -> impl Iterator<Item = Constraint> {
186 let side_length = box_side_length * box_side_length;
187
188 crate::util::two_combination_iter([side_length, side_length + 1], [0, 1])
189 .map(|[square, value]| Constraint::SquareNumber { square, value })
190 }
191}
192
193impl From<latin_square::Constraint> for Constraint {
194 fn from(src: latin_square::Constraint) -> Self {
195 Constraint::Latin(src)
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202
203 fn p(row: usize, column: usize, square: usize, value: usize) -> Possibility {
204 Possibility {
205 row,
206 column,
207 square,
208 value,
209 }
210 }
211
212 fn c_row(row: usize, value: usize) -> Constraint {
213 Constraint::Latin(latin_square::Constraint::RowNumber { row, value })
214 }
215
216 fn c_col(column: usize, value: usize) -> Constraint {
217 Constraint::Latin(latin_square::Constraint::ColumnNumber { column, value })
218 }
219
220 fn c_row_col(row: usize, column: usize) -> Constraint {
221 Constraint::Latin(latin_square::Constraint::RowColumn { row, column })
222 }
223
224 fn c_square(square: usize, value: usize) -> Constraint {
225 Constraint::SquareNumber { square, value }
226 }
227
228 #[test]
229 fn check_generated_possibilities_constraints() {
230 let mut sudoku = Sudoku::new(
231 2,
232 vec![
233 latin_square::tests::p(0, 0, 1),
235 latin_square::tests::p(0, 1, 2),
236 latin_square::tests::p(0, 2, 3),
237 latin_square::tests::p(0, 3, 4),
238 latin_square::tests::p(1, 0, 3),
240 latin_square::tests::p(2, 0, 2),
241 latin_square::tests::p(1, 3, 2),
242 latin_square::tests::p(2, 3, 3),
243 latin_square::tests::p(3, 0, 4),
245 latin_square::tests::p(3, 1, 3),
246 latin_square::tests::p(3, 2, 2),
247 latin_square::tests::p(3, 3, 1),
248 ],
249 );
250
251 sudoku.possibilities.sort();
252 assert_eq!(
253 sudoku.possibilities,
254 vec![
255 p(1, 1, 0, 1),
256 p(1, 1, 0, 2),
257 p(1, 1, 0, 3),
258 p(1, 1, 0, 4),
259 p(1, 2, 1, 1),
260 p(1, 2, 1, 2),
261 p(1, 2, 1, 3),
262 p(1, 2, 1, 4),
263 p(2, 1, 2, 1),
264 p(2, 1, 2, 2),
265 p(2, 1, 2, 3),
266 p(2, 1, 2, 4),
267 p(2, 2, 3, 1),
268 p(2, 2, 3, 2),
269 p(2, 2, 3, 3),
270 p(2, 2, 3, 4),
271 ]
272 );
273 sudoku.constraints.sort();
274 assert_eq!(
275 sudoku.constraints,
276 vec![
277 c_row(1, 1),
278 c_row(1, 4),
279 c_row(2, 1),
280 c_row(2, 4),
281 c_col(1, 1),
282 c_col(1, 4),
283 c_col(2, 1),
284 c_col(2, 4),
285 c_row_col(1, 1),
286 c_row_col(1, 2),
287 c_row_col(2, 1),
288 c_row_col(2, 2),
289 c_square(0, 4),
290 c_square(1, 1),
291 c_square(2, 1),
292 c_square(3, 4),
293 ]
294 );
295 }
296
297 #[test]
298 fn solve_small_sudoku() {
299 let sudoku = Sudoku::new(
300 2,
301 vec![
302 latin_square::tests::p(0, 0, 1),
304 latin_square::tests::p(0, 1, 2),
305 latin_square::tests::p(0, 2, 3),
306 latin_square::tests::p(0, 3, 4),
307 latin_square::tests::p(1, 0, 3),
309 latin_square::tests::p(2, 0, 2),
310 latin_square::tests::p(1, 3, 2),
311 latin_square::tests::p(2, 3, 3),
312 latin_square::tests::p(3, 0, 4),
314 latin_square::tests::p(3, 1, 3),
315 latin_square::tests::p(3, 2, 2),
316 latin_square::tests::p(3, 3, 1),
317 ],
318 );
319
320 let mut solver = sudoku.solver();
321 let solutions = solver.all_solutions();
322
323 assert_eq!(solutions.len(), 1);
324 assert_eq!(
325 solutions[0],
326 vec![
327 &p(1, 1, 0, 4),
328 &p(1, 2, 1, 1),
329 &p(2, 1, 2, 1),
330 &p(2, 2, 3, 4)
331 ]
332 );
333 }
334}