solverforge_solver/heuristic/move/
list_change.rs1use std::fmt::Debug;
12
13use solverforge_core::domain::PlanningSolution;
14use solverforge_scoring::Director;
15
16use super::list_kernel::{
17 change_candidate_trace_identity, change_do_move, change_is_doable, change_tabu_signature,
18 change_undo_move, ChangeCoordinates, ChangeValueTransfer, StaticListChangeAccess,
19};
20use super::{Move, MoveTabuSignature};
21
22pub struct ListChangeMove<S, V> {
74 source_entity_index: usize,
76 source_position: usize,
78 dest_entity_index: usize,
80 dest_position: usize,
82 list_len: fn(&S, usize) -> usize,
83 list_get: fn(&S, usize, usize) -> Option<V>,
84 list_remove: fn(&mut S, usize, usize) -> Option<V>,
86 list_insert: fn(&mut S, usize, usize, V),
88 variable_name: &'static str,
89 descriptor_index: usize,
90 indices: [usize; 2],
92}
93
94impl<S, V> Clone for ListChangeMove<S, V> {
95 fn clone(&self) -> Self {
96 *self
97 }
98}
99
100impl<S, V> Copy for ListChangeMove<S, V> {}
101
102impl<S, V: Debug> Debug for ListChangeMove<S, V> {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 f.debug_struct("ListChangeMove")
105 .field("source_entity", &self.source_entity_index)
106 .field("source_position", &self.source_position)
107 .field("dest_entity", &self.dest_entity_index)
108 .field("dest_position", &self.dest_position)
109 .field("variable_name", &self.variable_name)
110 .finish()
111 }
112}
113
114impl<S, V> ListChangeMove<S, V> {
115 #[allow(clippy::too_many_arguments)]
129 pub fn new(
130 source_entity_index: usize,
131 source_position: usize,
132 dest_entity_index: usize,
133 dest_position: usize,
134 list_len: fn(&S, usize) -> usize,
135 list_get: fn(&S, usize, usize) -> Option<V>,
136 list_remove: fn(&mut S, usize, usize) -> Option<V>,
137 list_insert: fn(&mut S, usize, usize, V),
138 variable_name: &'static str,
139 descriptor_index: usize,
140 ) -> Self {
141 Self {
142 source_entity_index,
143 source_position,
144 dest_entity_index,
145 dest_position,
146 list_len,
147 list_get,
148 list_remove,
149 list_insert,
150 variable_name,
151 descriptor_index,
152 indices: [source_entity_index, dest_entity_index],
153 }
154 }
155
156 pub fn source_entity_index(&self) -> usize {
157 self.source_entity_index
158 }
159
160 pub fn source_position(&self) -> usize {
161 self.source_position
162 }
163
164 pub fn dest_entity_index(&self) -> usize {
165 self.dest_entity_index
166 }
167
168 pub fn dest_position(&self) -> usize {
169 self.dest_position
170 }
171
172 pub fn is_intra_list(&self) -> bool {
173 self.source_entity_index == self.dest_entity_index
174 }
175
176 fn access(&self) -> StaticListChangeAccess<S, V> {
177 StaticListChangeAccess {
178 list_len: self.list_len,
179 list_get: self.list_get,
180 list_remove: self.list_remove,
181 list_insert: self.list_insert,
182 variable_name: self.variable_name,
183 descriptor_index: self.descriptor_index,
184 }
185 }
186
187 fn coordinates(&self) -> ChangeCoordinates {
188 ChangeCoordinates {
189 source_entity: self.source_entity_index,
190 source_position: self.source_position,
191 destination_entity: self.dest_entity_index,
192 destination_position: self.dest_position,
193 }
194 }
195}
196
197impl<S, V> Move<S> for ListChangeMove<S, V>
198where
199 S: PlanningSolution,
200 V: Clone + PartialEq + Send + Sync + Debug + 'static,
201{
202 type Undo = ();
203
204 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
205 change_is_doable(&self.access(), self.coordinates(), score_director)
206 }
207
208 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
209 change_do_move(
210 &self.access(),
211 self.coordinates(),
212 ChangeValueTransfer::CloneBeforeInsert,
213 score_director,
214 );
215 }
216
217 fn undo_move<D: Director<S>>(&self, score_director: &mut D, (): Self::Undo) {
218 change_undo_move(&self.access(), self.coordinates(), score_director);
219 }
220
221 fn descriptor_index(&self) -> usize {
222 self.descriptor_index
223 }
224
225 fn entity_indices(&self) -> &[usize] {
226 if self.is_intra_list() {
227 &self.indices[0..1]
228 } else {
229 &self.indices
230 }
231 }
232
233 fn variable_name(&self) -> &str {
234 self.variable_name
235 }
236
237 fn telemetry_label(&self) -> &'static str {
238 "list_change"
239 }
240
241 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
242 change_tabu_signature(&self.access(), self.coordinates(), score_director)
243 }
244
245 fn candidate_trace_identity(&self) -> Option<crate::stats::CandidateTraceIdentity> {
246 Some(change_candidate_trace_identity(
247 &self.access(),
248 self.coordinates(),
249 ))
250 }
251}