domino_lib/generate/
mod.rs1use std::time::{Duration, Instant};
6
7use crate::{classify_puzzle, utils::find_eulerian_cycle, validate_puzzle, ComplexityClass, Graph, Puzzle, Solution, Tile};
8use rand::Rng;
9
10pub fn generate_puzzle(n: usize, c: usize) -> Puzzle {
26 let graph = Graph::regular(n);
27 let eulerian_cycle = find_eulerian_cycle(&graph,true);
28 let mut solution: Solution = create_solution_from_cycle(&eulerian_cycle);
29 let mut puzzle= solution.clone().into_iter().map(Some).collect::<Vec<Option<Tile>>>();
30 let mut rng = rand::thread_rng();
31
32 let mut expected_complexity = ComplexityClass::new(c).ok();
34 let mut actual_complexity: Option<ComplexityClass> = None;
35 let mut is_not_complex_enough = actual_complexity != expected_complexity;
36 let mut is_too_complex = false;
37
38 let mut now = Instant::now();
40 let timeout = Duration::from_millis(2000);
41
42 let is_not_valid = validate_puzzle(&puzzle.clone().into(), &solution).is_err();
44
45 let mut index = rng.gen_range(0..puzzle.len());
47 let mut removal_history: Vec<(Option<Tile>, usize)> = vec![(puzzle[index].clone(), index)];
48 puzzle[index] = None;
49
50
51 while is_not_valid || is_not_complex_enough {
53
54 while is_too_complex {
55 reinsert_tile(&mut puzzle, &mut removal_history);
57 update_complexity(&mut actual_complexity, &mut expected_complexity, &puzzle, &mut is_not_complex_enough, &mut is_too_complex);
58 }
59
60 let removed_tile: Option<Tile>;
61 let removed_position: Option<usize>;
62
63 (puzzle, removed_tile, removed_position) = remove_non_empty_tile(puzzle);
65 removal_history.push((removed_tile, removed_position.unwrap()));
66
67 update_complexity(&mut actual_complexity, &mut expected_complexity, &puzzle, &mut is_not_complex_enough, &mut is_too_complex);
69
70 let is_not_valid = validate_puzzle(&puzzle.clone().into(), &solution).is_err();
72
73 if is_not_valid {
75 reinsert_tile(&mut puzzle, &mut removal_history);
76 update_complexity(&mut actual_complexity, &mut expected_complexity, &puzzle, &mut is_not_complex_enough, &mut is_too_complex);
77 }
78
79 if now.elapsed().as_millis() > timeout.as_millis() || actual_complexity.is_none(){
81 solution = create_solution_from_cycle(&eulerian_cycle);
83 puzzle = solution.clone().into_iter().map(Some).collect::<Vec<Option<Tile>>>().into();
84 index = rng.gen_range(0..puzzle.len());
85 removal_history = vec![(puzzle[index].clone(), index)];
86 puzzle[index] = None;
87 update_complexity(&mut actual_complexity, &mut expected_complexity, &puzzle, &mut is_not_complex_enough, &mut is_too_complex);
88 now = Instant::now();
89 }
90
91 }
92
93 puzzle.into()
94}
95
96fn update_complexity(actual_complexity: &mut Option<ComplexityClass>, expected_complexity: &mut Option<ComplexityClass>, puzzle: &Vec<Option<Tile>>, is_not_complex_enough: &mut bool, is_too_complex: &mut bool) {
97 let result = classify_puzzle(&puzzle.clone().into());
98 *actual_complexity = result.ok();
99 *is_not_complex_enough = actual_complexity != expected_complexity;
100 *is_too_complex = actual_complexity.is_some() && actual_complexity.unwrap() > expected_complexity.unwrap();
101
102}
103
104fn reinsert_tile(puzzle: &mut Vec<Option<Tile>>, history: &mut Vec<(Option<Tile>, usize)>) {
105 let (removed_tile, removed_position) = history.pop().unwrap();
107 puzzle[removed_position] = removed_tile;
108 }
111
112fn remove_non_empty_tile(mut puzzle: Vec<Option<Tile>>) -> (Vec<Option<Tile>>, Option<Tile>, Option<usize>) {
113 let mut rng = rand::thread_rng();
114 let mut index = rng.gen_range(0..puzzle.len());
115
116 for _ in 0..10 {
117 if puzzle[index].is_some() {
118 index = rng.gen_range(0..puzzle.len());
119 }
120 }
121
122 while puzzle[index].is_none() {
123 index = (index + 1) % puzzle.len();
124 }
125 let removed_tile = puzzle[index].clone();
126 puzzle[index] = None;
127 (puzzle.clone(), removed_tile, Some(index))
128}
129
130fn create_solution_from_cycle(eulerian_cycle: &Vec<crate::Node>) -> Solution {
131eulerian_cycle
132 .windows(2)
133 .map(|arc| {
134 Tile(
135 arc[0].clone().try_into().unwrap(),
136 arc[1].clone().try_into().unwrap(),
137 )
138 })
139 .collect()
140}