solverforge_solver/heuristic/move/
pillar_change.rs1use std::fmt::Debug;
13
14use solverforge_core::domain::PlanningSolution;
15use solverforge_scoring::Director;
16
17use super::Move;
18
19pub struct PillarChangeMove<S, V> {
28 entity_indices: Vec<usize>,
29 descriptor_index: usize,
30 variable_name: &'static str,
31 to_value: Option<V>,
32 getter: fn(&S, usize) -> Option<V>,
34 setter: fn(&mut S, usize, Option<V>),
36}
37
38impl<S, V: Clone> Clone for PillarChangeMove<S, V> {
39 fn clone(&self) -> Self {
40 Self {
41 entity_indices: self.entity_indices.clone(),
42 descriptor_index: self.descriptor_index,
43 variable_name: self.variable_name,
44 to_value: self.to_value.clone(),
45 getter: self.getter,
46 setter: self.setter,
47 }
48 }
49}
50
51impl<S, V: Debug> Debug for PillarChangeMove<S, V> {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.debug_struct("PillarChangeMove")
54 .field("entity_indices", &self.entity_indices)
55 .field("descriptor_index", &self.descriptor_index)
56 .field("variable_name", &self.variable_name)
57 .field("to_value", &self.to_value)
58 .finish()
59 }
60}
61
62impl<S, V> PillarChangeMove<S, V> {
63 pub fn new(
73 entity_indices: Vec<usize>,
74 to_value: Option<V>,
75 getter: fn(&S, usize) -> Option<V>,
76 setter: fn(&mut S, usize, Option<V>),
77 variable_name: &'static str,
78 descriptor_index: usize,
79 ) -> Self {
80 Self {
81 entity_indices,
82 descriptor_index,
83 variable_name,
84 to_value,
85 getter,
86 setter,
87 }
88 }
89
90 pub fn pillar_size(&self) -> usize {
91 self.entity_indices.len()
92 }
93
94 pub fn to_value(&self) -> Option<&V> {
95 self.to_value.as_ref()
96 }
97}
98
99impl<S, V> Move<S> for PillarChangeMove<S, V>
100where
101 S: PlanningSolution,
102 V: Clone + PartialEq + Send + Sync + Debug + 'static,
103{
104 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
105 if self.entity_indices.is_empty() {
106 return false;
107 }
108
109 let count = score_director.entity_count(self.descriptor_index);
111 if let Some(&first_idx) = self.entity_indices.first() {
112 if count.is_none_or(|c| first_idx >= c) {
113 return false;
114 }
115
116 let current = (self.getter)(score_director.working_solution(), first_idx);
118
119 match (¤t, &self.to_value) {
120 (None, None) => false,
121 (Some(cur), Some(target)) => cur != target,
122 _ => true,
123 }
124 } else {
125 false
126 }
127 }
128
129 fn do_move<D: Director<S>>(&self, score_director: &mut D) {
130 let old_values: Vec<(usize, Option<V>)> = self
132 .entity_indices
133 .iter()
134 .map(|&idx| (idx, (self.getter)(score_director.working_solution(), idx)))
135 .collect();
136
137 for &idx in &self.entity_indices {
139 score_director.before_variable_changed(self.descriptor_index, idx);
140 }
141
142 for &idx in &self.entity_indices {
144 (self.setter)(
145 score_director.working_solution_mut(),
146 idx,
147 self.to_value.clone(),
148 );
149 }
150
151 for &idx in &self.entity_indices {
153 score_director.after_variable_changed(self.descriptor_index, idx);
154 }
155
156 let setter = self.setter;
158 score_director.register_undo(Box::new(move |s: &mut S| {
159 for (idx, old_value) in old_values {
160 setter(s, idx, old_value);
161 }
162 }));
163 }
164
165 fn descriptor_index(&self) -> usize {
166 self.descriptor_index
167 }
168
169 fn entity_indices(&self) -> &[usize] {
170 &self.entity_indices
171 }
172
173 fn variable_name(&self) -> &str {
174 self.variable_name
175 }
176}