1use sudoku::Grid;
24use Element;
25use Point;
26use Sudoku;
27use DIMENSIONS;
28
29use std::ops::{Index, IndexMut};
30
31#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
33pub enum Difficulty {
34 #[doc(hidden)]
35 Unplayable,
37 Beginner,
39 Easy,
41 Intermediate,
43 Difficult,
45 Advanced,
47}
48
49impl From<usize> for Difficulty {
50 fn from(score: usize) -> Self {
51 use Difficulty::*;
52 match score {
53 0...49 => Unplayable,
54 50...150 => Beginner,
55 151...250 => Easy,
56 251...400 => Intermediate,
57 401...550 => Difficult,
58 _ => Advanced,
59 }
60 }
61}
62
63#[derive(Clone, Debug)]
65#[allow(missing_copy_implementations)] pub enum Error {
67 Unknown,
69 #[doc(hidden)]
70 __TestOther,
71}
72
73pub trait Solve: Sized {
75 fn solution(&self) -> Result<Self, Error>;
77 fn is_uniquely_solvable(&self) -> bool {
79 self.solution().is_ok()
80 }
81}
82
83pub trait Score: Solve {
85 fn score(&self) -> Option<usize>;
87 fn difficulty(&self) -> Option<Difficulty> {
89 self.score().map(|s| s.into())
90 }
91}
92
93#[derive(Clone, Copy, Debug, PartialEq)]
95pub struct PossibilitySet {
96 pub values: u64,
97}
98
99impl PossibilitySet {
100 pub fn new(order: u8) -> Self {
102 let mut values = 0;
103 for i in 1..=order.pow(2) as usize {
104 values |= 1 << (i - 1);
105 }
106 Self { values }
107 }
108 pub fn eliminate(&self, value: usize) -> Option<Self> {
110 let values = self.values & !(1 << (value - 1));
111 match values {
112 0 => None,
113 _ => Some(Self { values }),
114 }
115 }
116 pub fn freedom(&self) -> usize {
118 let mut x = self.values;
119 let mut n = 0;
120 while x > 0 {
121 x &= x - 1;
122 n += 1;
123 }
124 n
125 }
126 pub fn contains(&self, value: usize) -> bool {
128 self.values | (1 << (value - 1)) == self.values
129 }
130}
131
132#[derive(Debug)]
133pub struct PossibilityMap {
134 possibilities: Vec<Option<PossibilitySet>>,
135 order: u8,
136}
137
138impl PossibilityMap {
139 pub fn new(order: u8) -> Self {
141 Self {
142 possibilities: vec![
143 Some(PossibilitySet::new(order));
144 (order as usize).pow(2 + DIMENSIONS as u32)
145 ],
146 order,
147 }
148 }
149
150 pub fn eliminate(&mut self, index: Point, value: usize) {
154 self[index] = self[index].and_then(|e| e.eliminate(value));
155 }
156
157 pub fn next(&self) -> (Option<Point>, Option<PossibilitySet>) {
159 let mut best = None;
160 let mut best_index = None;
161 let mut best_score = None;
162 for index in self.points() {
163 if let Some(element) = self[index] {
164 if best_score.is_none() || best_score.unwrap() > element.freedom() {
165 best = Some(element);
166 best_index = Some(index);
167 best_score = Some(element.freedom());
168 }
169 }
170 }
171 (best_index, best)
172 }
173}
174
175impl Index<Point> for PossibilityMap {
176 type Output = Option<PossibilitySet>;
177
178 fn index(&self, index: Point) -> &Self::Output {
179 let index = index.fold(self.order);
180 &self.possibilities[index]
181 }
182}
183
184impl IndexMut<Point> for PossibilityMap {
185 fn index_mut(&mut self, index: Point) -> &mut Option<PossibilitySet> {
186 let index = index.fold(self.order);
187 &mut self.possibilities[index]
188 }
189}
190
191impl Grid for PossibilityMap {
192 fn points(&self) -> Vec<Point> {
193 (0..(self.order as usize).pow(2 + DIMENSIONS as u32))
194 .map(|p| Point::unfold(p, self.order))
195 .collect()
196 }
197}
198
199impl From<Sudoku> for PossibilityMap {
200 fn from(sudoku: Sudoku) -> Self {
201 let order = sudoku.order;
202 let mut map = PossibilityMap::new(order);
203 for i in 0..(sudoku.order as usize).pow(2 + DIMENSIONS as u32) {
204 let point = Point::unfold(i, order);
205 if sudoku[point].is_some() {
206 map[point] = None;
207 } else {
208 let groups = sudoku.groups(point);
209 for group in groups.iter() {
210 let elements = group.elements();
211 for element in elements {
212 match element {
213 Some(Element(value)) => {
214 map.eliminate(point, value as usize);
215 }
216 None => {}
217 }
218 }
219 }
220 }
221 }
222 map
223 }
224}
225
226pub fn solve(puzzle: &Sudoku) -> Result<Sudoku, Error> {
227 solve_and_score(puzzle).map(|(sol, _)| sol)
228}
229
230pub fn solve_and_score(puzzle: &Sudoku) -> Result<(Sudoku, usize), Error> {
231 let mut context = Context {
232 problem: puzzle.clone(),
233 count: 0,
234 solution: None,
235 branch_score: 0,
236 };
237 recurse(&mut context, 0);
238 let s = context.branch_score;
239 let c = calculate_c(puzzle) as isize;
240 let e = count_empty(puzzle) as isize;
241 context
242 .solution
243 .ok_or(Error::Unknown)
244 .map(|sol| (sol, (s * c + e) as usize))
245}
246
247struct Context {
248 problem: Sudoku,
249 count: usize,
250 solution: Option<Sudoku>,
251 branch_score: isize,
252}
253
254fn recurse(mut context: &mut Context, difficulty: isize) {
255 let problem = context.problem.clone();
256 let map: PossibilityMap = problem.into();
257 match map.next() {
258 (None, _) => {
259 if context.problem.is_complete() {
260 if context.count == 0 {
262 context.branch_score = difficulty;
263 context.solution = Some(context.problem.clone());
264 }
265 context.count += 1;
266 }
267 return;
268 }
269 (Some(index), Some(set)) => {
270 let branch_factor = set.freedom() as isize - 1;
271 let possible = (1..=(context.problem.order as usize).pow(2))
272 .filter(|v| set.contains(*v))
273 .collect::<Vec<_>>();
274 let difficulty = difficulty + branch_factor.pow(DIMENSIONS as u32);
275 for value in possible {
276 let problem = context
277 .problem
278 .substitute(index, Some(Element(value as u8)));
279 context.problem = problem;
280 recurse(&mut context, difficulty);
281 if context.count > 1 {
282 return;
284 }
285 }
286 context.problem = context.problem.substitute(index, None);
287 }
288 _ => unreachable!(),
289 }
290}
291
292fn count_empty(sudoku: &Sudoku) -> usize {
296 sudoku
297 .elements
298 .iter()
299 .filter(|e| e.is_none())
300 .collect::<Vec<_>>()
301 .len()
302}
303
304fn calculate_c(sudoku: &Sudoku) -> usize {
306 let order = sudoku.order;
307 10.0_f64.powf((order as f64).powf(4.0).log10().ceil()) as usize
308}
309
310pub fn score(sudoku: &Sudoku) -> Option<usize> {
312 solve_and_score(&sudoku).ok().map(|(_, s)| s)
313}
314
315#[cfg(test)]
316mod tests {
317
318 use sol::{calculate_c, Error, PossibilityMap, PossibilitySet, Solve};
319 use Point;
320 use Sudoku;
321 use DIMENSIONS;
322
323 struct DummyPuzzle(bool);
324
325 impl DummyPuzzle {
326 fn new(solvable: bool) -> Self {
327 Self { 0: solvable }
328 }
329 }
330
331 impl Solve for DummyPuzzle {
332 fn solution(&self) -> Result<Self, Error> {
333 if self.0 {
334 Ok(Self { 0: true })
335 } else {
336 Err(Error::__TestOther)
337 }
338 }
339 }
340
341 #[test]
342 fn test_is_uniquely_solvable() {
343 let solvable = DummyPuzzle::new(true);
344 assert_eq!(solvable.is_uniquely_solvable(), true);
345 let unsolvable = DummyPuzzle::new(false);
346 assert_eq!(unsolvable.is_uniquely_solvable(), false);
347 }
348
349 #[test]
350 fn test_calculate_c() {
351 let sudoku = Sudoku::new(3);
352 assert_eq!(calculate_c(&sudoku), 100);
353 let sudoku = Sudoku::new(4);
354 assert_eq!(calculate_c(&sudoku), 1_000);
355 let sudoku = Sudoku::new(5);
356 assert_eq!(calculate_c(&sudoku), 1_000);
357 let sudoku = Sudoku::new(6);
358 assert_eq!(calculate_c(&sudoku), 10_000);
359 }
360
361 #[test]
362 fn test_map_new() {
363 for order in 1..6 {
364 let map = PossibilityMap::new(order);
365 for i in 0..(order as usize).pow(DIMENSIONS as u32 + 2) {
366 let index = Point::unfold(i, order);
367 let set = PossibilitySet::new(order);
368 assert_eq!(map[index], Some(set));
369 }
370 }
371 }
372
373 #[test]
374 fn test_map_from_sudoku() {
375 let sudoku = Sudoku::new(3);
377 let map: PossibilityMap = sudoku.into();
378 for p in map.possibilities {
379 assert_eq!(p, Some(PossibilitySet::new(3)));
380 }
381 }
382
383 #[test]
384 fn test_set_new() {
385 let set = PossibilitySet::new(3);
386 for i in 1..10 {
387 assert!(set.contains(i));
388 }
389 }
390
391 #[test]
392 fn test_set_eliminate() {
393 let mut set = PossibilitySet::new(3);
394 for i in 1..9 {
395 set = set.eliminate(i).unwrap();
396 assert!(!set.contains(i));
397 }
398 assert_eq!(set.eliminate(9), None);
399 }
400
401 #[test]
402 fn test_set_freedom() {
403 let mut set = PossibilitySet::new(3);
404 for i in 1..9 {
405 set = set.eliminate(i).unwrap();
406 assert_eq!(set.freedom(), 9 - i);
407 }
408 }
409}