solverforge_solver/heuristic/move/
pillar_change.rs1use std::borrow::Cow;
13use std::fmt::Debug;
14
15use smallvec::{smallvec, SmallVec};
16use solverforge_core::domain::PlanningSolution;
17use solverforge_scoring::Director;
18
19use super::metadata::{
20 encode_option_debug, encode_usize, hash_str, MoveTabuScope, ScopedEntityTabuToken,
21};
22use super::{Move, MoveTabuSignature};
23
24pub struct PillarChangeMove<S, V> {
33 entity_indices: Vec<usize>,
34 descriptor_index: usize,
35 variable_name: &'static str,
36 to_value: Option<V>,
37 getter: fn(&S, usize, usize) -> Option<V>,
39 setter: fn(&mut S, usize, usize, Option<V>),
41 variable_index: usize,
42}
43
44impl<S, V: Debug> Debug for PillarChangeMove<S, V> {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("PillarChangeMove")
47 .field("entity_indices", &self.entity_indices)
48 .field("descriptor_index", &self.descriptor_index)
49 .field("variable_index", &self.variable_index)
50 .field("variable_name", &self.variable_name)
51 .field("to_value", &self.to_value)
52 .finish()
53 }
54}
55
56impl<S, V> PillarChangeMove<S, V> {
57 pub fn new(
67 entity_indices: Vec<usize>,
68 to_value: Option<V>,
69 getter: fn(&S, usize, usize) -> Option<V>,
70 setter: fn(&mut S, usize, usize, Option<V>),
71 variable_index: usize,
72 variable_name: &'static str,
73 descriptor_index: usize,
74 ) -> Self {
75 Self {
76 entity_indices,
77 descriptor_index,
78 variable_name,
79 to_value,
80 getter,
81 setter,
82 variable_index,
83 }
84 }
85
86 pub fn pillar_size(&self) -> usize {
87 self.entity_indices.len()
88 }
89
90 pub fn to_value(&self) -> Option<&V> {
91 self.to_value.as_ref()
92 }
93}
94
95impl<S, V> Move<S> for PillarChangeMove<S, V>
96where
97 S: PlanningSolution,
98 V: Clone + PartialEq + Send + Sync + Debug + 'static,
99{
100 type Undo = SmallVec<[(usize, Option<V>); 4]>;
101
102 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
103 if self.entity_indices.is_empty() {
104 return false;
105 }
106
107 let count = score_director.entity_count(self.descriptor_index);
109 if let Some(&first_idx) = self.entity_indices.first() {
110 if count.is_none_or(|c| first_idx >= c) {
111 return false;
112 }
113
114 let current = (self.getter)(
116 score_director.working_solution(),
117 first_idx,
118 self.variable_index,
119 );
120
121 match (¤t, &self.to_value) {
122 (None, None) => false,
123 (Some(cur), Some(target)) => cur != target,
124 _ => true,
125 }
126 } else {
127 false
128 }
129 }
130
131 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
132 let old_values: SmallVec<[(usize, Option<V>); 4]> = self
134 .entity_indices
135 .iter()
136 .map(|&idx| {
137 (
138 idx,
139 (self.getter)(score_director.working_solution(), idx, self.variable_index),
140 )
141 })
142 .collect();
143
144 for &idx in &self.entity_indices {
146 score_director.before_variable_changed(self.descriptor_index, idx);
147 }
148
149 for &idx in &self.entity_indices {
151 (self.setter)(
152 score_director.working_solution_mut(),
153 idx,
154 self.variable_index,
155 self.to_value.clone(),
156 );
157 }
158
159 for &idx in &self.entity_indices {
161 score_director.after_variable_changed(self.descriptor_index, idx);
162 }
163
164 old_values
165 }
166
167 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
168 for (idx, _) in &undo {
169 score_director.before_variable_changed(self.descriptor_index, *idx);
170 }
171 for (idx, old_value) in undo {
172 (self.setter)(
173 score_director.working_solution_mut(),
174 idx,
175 self.variable_index,
176 old_value,
177 );
178 }
179 for &idx in &self.entity_indices {
180 score_director.after_variable_changed(self.descriptor_index, idx);
181 }
182 }
183
184 fn descriptor_index(&self) -> usize {
185 self.descriptor_index
186 }
187
188 fn entity_indices(&self) -> &[usize] {
189 &self.entity_indices
190 }
191
192 fn variable_name(&self) -> &str {
193 self.variable_name
194 }
195
196 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> Cow<'_, MoveTabuSignature> {
197 let from_value = self.entity_indices.first().and_then(|&idx| {
198 (self.getter)(score_director.working_solution(), idx, self.variable_index)
199 });
200 let from_id = encode_option_debug(from_value.as_ref());
201 let to_id = encode_option_debug(self.to_value.as_ref());
202 let variable_id = hash_str(self.variable_name);
203 let scope = MoveTabuScope::new(self.descriptor_index, self.variable_name);
204 let entity_ids: SmallVec<[u64; 2]> = self
205 .entity_indices
206 .iter()
207 .map(|&idx| encode_usize(idx))
208 .collect();
209 let entity_tokens: SmallVec<[ScopedEntityTabuToken; 2]> = entity_ids
210 .iter()
211 .copied()
212 .map(|entity_id| scope.entity_token(entity_id))
213 .collect();
214 let mut move_id = smallvec![
215 encode_usize(self.descriptor_index),
216 variable_id,
217 encode_usize(self.entity_indices.len()),
218 from_id,
219 to_id
220 ];
221 move_id.extend(entity_ids.iter().copied());
222
223 let mut undo_move_id = smallvec![
224 encode_usize(self.descriptor_index),
225 variable_id,
226 encode_usize(self.entity_indices.len()),
227 to_id,
228 from_id
229 ];
230 undo_move_id.extend(entity_ids.iter().copied());
231
232 Cow::Owned(
233 MoveTabuSignature::new(scope, move_id, undo_move_id)
234 .with_entity_tokens(entity_tokens)
235 .with_destination_value_tokens([scope.value_token(to_id)]),
236 )
237 }
238}