solverforge_solver/heuristic/move/
list_swap.rs1use std::borrow::Cow;
12use std::fmt::Debug;
13
14use solverforge_core::domain::PlanningSolution;
15use solverforge_scoring::Director;
16
17use super::list_kernel::{
18 swap_candidate_trace_identity, swap_do_move, swap_is_doable, swap_tabu_signature,
19 StaticListSwapAccess, SwapCoordinates,
20};
21use super::{Move, MoveTabuSignature};
22
23pub struct ListSwapMove<S, V> {
71 first_entity_index: usize,
73 first_position: usize,
75 second_entity_index: usize,
77 second_position: usize,
79 list_len: fn(&S, usize) -> usize,
80 list_get: fn(&S, usize, usize) -> Option<V>,
81 list_set: fn(&mut S, usize, usize, V),
83 variable_name: &'static str,
84 descriptor_index: usize,
85 indices: [usize; 2],
87}
88
89impl<S, V> Clone for ListSwapMove<S, V> {
90 fn clone(&self) -> Self {
91 *self
92 }
93}
94
95impl<S, V> Copy for ListSwapMove<S, V> {}
96
97impl<S, V: Debug> Debug for ListSwapMove<S, V> {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 f.debug_struct("ListSwapMove")
100 .field("first_entity", &self.first_entity_index)
101 .field("first_position", &self.first_position)
102 .field("second_entity", &self.second_entity_index)
103 .field("second_position", &self.second_position)
104 .field("variable_name", &self.variable_name)
105 .finish()
106 }
107}
108
109impl<S, V> ListSwapMove<S, V> {
110 #[allow(clippy::too_many_arguments)]
124 pub fn new(
125 first_entity_index: usize,
126 first_position: usize,
127 second_entity_index: usize,
128 second_position: usize,
129 list_len: fn(&S, usize) -> usize,
130 list_get: fn(&S, usize, usize) -> Option<V>,
131 list_set: fn(&mut S, usize, usize, V),
132 variable_name: &'static str,
133 descriptor_index: usize,
134 ) -> Self {
135 Self {
136 first_entity_index,
137 first_position,
138 second_entity_index,
139 second_position,
140 list_len,
141 list_get,
142 list_set,
143 variable_name,
144 descriptor_index,
145 indices: [first_entity_index, second_entity_index],
146 }
147 }
148
149 pub fn first_entity_index(&self) -> usize {
150 self.first_entity_index
151 }
152
153 pub fn first_position(&self) -> usize {
154 self.first_position
155 }
156
157 pub fn second_entity_index(&self) -> usize {
158 self.second_entity_index
159 }
160
161 pub fn second_position(&self) -> usize {
162 self.second_position
163 }
164
165 pub fn is_intra_list(&self) -> bool {
166 self.first_entity_index == self.second_entity_index
167 }
168
169 fn access(&self) -> StaticListSwapAccess<S, V> {
170 StaticListSwapAccess {
171 list_len: self.list_len,
172 list_get: self.list_get,
173 list_set: self.list_set,
174 variable_name: self.variable_name,
175 descriptor_index: self.descriptor_index,
176 }
177 }
178
179 fn coordinates(&self) -> SwapCoordinates {
180 SwapCoordinates {
181 first_entity: self.first_entity_index,
182 first_position: self.first_position,
183 second_entity: self.second_entity_index,
184 second_position: self.second_position,
185 }
186 }
187}
188
189impl<S, V> Move<S> for ListSwapMove<S, V>
190where
191 S: PlanningSolution,
192 V: Clone + PartialEq + Send + Sync + Debug + 'static,
193{
194 type Undo = ();
195
196 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
197 swap_is_doable(&self.access(), self.coordinates(), score_director)
198 }
199
200 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
201 swap_do_move(&self.access(), self.coordinates(), score_director);
202 }
203
204 fn undo_move<D: Director<S>>(&self, score_director: &mut D, (): Self::Undo) {
205 swap_do_move(&self.access(), self.coordinates(), score_director);
206 }
207
208 fn descriptor_index(&self) -> usize {
209 self.descriptor_index
210 }
211
212 fn entity_indices(&self) -> &[usize] {
213 if self.is_intra_list() {
214 &self.indices[0..1]
215 } else {
216 &self.indices
217 }
218 }
219
220 fn variable_name(&self) -> &str {
221 self.variable_name
222 }
223
224 fn telemetry_label(&self) -> &'static str {
225 "list_swap"
226 }
227
228 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> Cow<'_, MoveTabuSignature> {
229 Cow::Owned(swap_tabu_signature(
230 &self.access(),
231 self.coordinates(),
232 score_director,
233 ))
234 }
235
236 fn candidate_trace_identity(&self) -> Option<crate::stats::CandidateTraceIdentity> {
237 Some(swap_candidate_trace_identity(
238 &self.access(),
239 self.coordinates(),
240 ))
241 }
242}