solverforge_solver/heuristic/move/
swap.rs1use std::fmt::Debug;
13
14use solverforge_core::domain::PlanningSolution;
15use solverforge_scoring::Director;
16
17use super::metadata::{
18 encode_option_debug, encode_usize, ordered_coordinate_pair, scoped_move_identity,
19 MoveTabuScope, TABU_OP_SWAP,
20};
21use super::{Move, MoveTabuSignature};
22
23pub struct SwapMove<S, V> {
55 left_entity_index: usize,
56 right_entity_index: usize,
57 getter: fn(&S, usize, usize) -> Option<V>,
59 setter: fn(&mut S, usize, usize, Option<V>),
61 variable_index: usize,
62 variable_name: &'static str,
63 descriptor_index: usize,
64 indices: [usize; 2],
66}
67
68impl<S, V> Clone for SwapMove<S, V> {
69 fn clone(&self) -> Self {
70 *self
71 }
72}
73
74impl<S, V> Copy for SwapMove<S, V> {}
75
76impl<S, V: Debug> Debug for SwapMove<S, V> {
77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 f.debug_struct("SwapMove")
79 .field("left_entity_index", &self.left_entity_index)
80 .field("right_entity_index", &self.right_entity_index)
81 .field("descriptor_index", &self.descriptor_index)
82 .field("variable_index", &self.variable_index)
83 .field("variable_name", &self.variable_name)
84 .finish()
85 }
86}
87
88impl<S, V> SwapMove<S, V> {
89 pub fn new(
99 left_entity_index: usize,
100 right_entity_index: usize,
101 getter: fn(&S, usize, usize) -> Option<V>,
102 setter: fn(&mut S, usize, usize, Option<V>),
103 variable_index: usize,
104 variable_name: &'static str,
105 descriptor_index: usize,
106 ) -> Self {
107 Self {
108 left_entity_index,
109 right_entity_index,
110 getter,
111 setter,
112 variable_index,
113 variable_name,
114 descriptor_index,
115 indices: [left_entity_index, right_entity_index],
116 }
117 }
118
119 pub fn left_entity_index(&self) -> usize {
120 self.left_entity_index
121 }
122
123 pub fn right_entity_index(&self) -> usize {
124 self.right_entity_index
125 }
126}
127
128impl<S, V> Move<S> for SwapMove<S, V>
129where
130 S: PlanningSolution,
131 V: Clone + PartialEq + Send + Sync + Debug + 'static,
132{
133 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
134 if self.left_entity_index == self.right_entity_index {
136 return false;
137 }
138
139 let left_val = (self.getter)(
141 score_director.working_solution(),
142 self.left_entity_index,
143 self.variable_index,
144 );
145 let right_val = (self.getter)(
146 score_director.working_solution(),
147 self.right_entity_index,
148 self.variable_index,
149 );
150
151 left_val != right_val
153 }
154
155 fn do_move<D: Director<S>>(&self, score_director: &mut D) {
156 let left_value = (self.getter)(
158 score_director.working_solution(),
159 self.left_entity_index,
160 self.variable_index,
161 );
162 let right_value = (self.getter)(
163 score_director.working_solution(),
164 self.right_entity_index,
165 self.variable_index,
166 );
167
168 score_director.before_variable_changed(self.descriptor_index, self.left_entity_index);
170 score_director.before_variable_changed(self.descriptor_index, self.right_entity_index);
171
172 (self.setter)(
174 score_director.working_solution_mut(),
175 self.left_entity_index,
176 self.variable_index,
177 right_value.clone(),
178 );
179 (self.setter)(
180 score_director.working_solution_mut(),
181 self.right_entity_index,
182 self.variable_index,
183 left_value.clone(),
184 );
185
186 score_director.after_variable_changed(self.descriptor_index, self.left_entity_index);
188 score_director.after_variable_changed(self.descriptor_index, self.right_entity_index);
189
190 let setter = self.setter;
192 let left_idx = self.left_entity_index;
193 let right_idx = self.right_entity_index;
194 let variable_index = self.variable_index;
195 score_director.register_undo(Box::new(move |s: &mut S| {
196 setter(s, left_idx, variable_index, left_value);
198 setter(s, right_idx, variable_index, right_value);
199 }));
200 }
201
202 fn descriptor_index(&self) -> usize {
203 self.descriptor_index
204 }
205
206 fn entity_indices(&self) -> &[usize] {
207 &self.indices
208 }
209
210 fn variable_name(&self) -> &str {
211 self.variable_name
212 }
213
214 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
215 let left_val = (self.getter)(
216 score_director.working_solution(),
217 self.left_entity_index,
218 self.variable_index,
219 );
220 let right_val = (self.getter)(
221 score_director.working_solution(),
222 self.right_entity_index,
223 self.variable_index,
224 );
225 let left_id = encode_option_debug(left_val.as_ref());
226 let right_id = encode_option_debug(right_val.as_ref());
227 let left_entity_id = encode_usize(self.left_entity_index);
228 let right_entity_id = encode_usize(self.right_entity_index);
229 let scope = MoveTabuScope::new(self.descriptor_index, self.variable_name);
230 let entity_pair = ordered_coordinate_pair((left_entity_id, 0), (right_entity_id, 0));
231 let move_id = scoped_move_identity(
232 scope,
233 TABU_OP_SWAP,
234 entity_pair.into_iter().map(|(entity_id, _)| entity_id),
235 );
236
237 MoveTabuSignature::new(scope, move_id.clone(), move_id)
238 .with_entity_tokens([
239 scope.entity_token(left_entity_id),
240 scope.entity_token(right_entity_id),
241 ])
242 .with_destination_value_tokens([
243 scope.value_token(right_id),
244 scope.value_token(left_id),
245 ])
246 }
247}