solverforge_solver/heuristic/move/
list_reverse.rs1use std::fmt::Debug;
12use std::marker::PhantomData;
13
14use smallvec::SmallVec;
15use solverforge_core::domain::PlanningSolution;
16use solverforge_scoring::Director;
17
18use super::metadata::{
19 encode_option_debug, encode_usize, scoped_move_identity, MoveTabuScope, ScopedValueTabuToken,
20 TABU_OP_LIST_REVERSE,
21};
22use super::{Move, MoveTabuSignature};
23
24pub struct ListReverseMove<S, V> {
63 entity_index: usize,
65 start: usize,
67 end: usize,
69 list_len: fn(&S, usize) -> usize,
70 list_get: fn(&S, usize, usize) -> Option<V>,
71 list_reverse: fn(&mut S, usize, usize, usize),
73 variable_name: &'static str,
74 descriptor_index: usize,
75 _phantom: PhantomData<fn() -> V>,
76}
77
78impl<S, V> Clone for ListReverseMove<S, V> {
79 fn clone(&self) -> Self {
80 *self
81 }
82}
83
84impl<S, V> Copy for ListReverseMove<S, V> {}
85
86impl<S, V: Debug> Debug for ListReverseMove<S, V> {
87 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 f.debug_struct("ListReverseMove")
89 .field("entity", &self.entity_index)
90 .field("range", &(self.start..self.end))
91 .field("variable_name", &self.variable_name)
92 .finish()
93 }
94}
95
96impl<S, V> ListReverseMove<S, V> {
97 #[allow(clippy::too_many_arguments)]
109 pub fn new(
110 entity_index: usize,
111 start: usize,
112 end: usize,
113 list_len: fn(&S, usize) -> usize,
114 list_get: fn(&S, usize, usize) -> Option<V>,
115 list_reverse: fn(&mut S, usize, usize, usize),
116 variable_name: &'static str,
117 descriptor_index: usize,
118 ) -> Self {
119 Self {
120 entity_index,
121 start,
122 end,
123 list_len,
124 list_get,
125 list_reverse,
126 variable_name,
127 descriptor_index,
128 _phantom: PhantomData,
129 }
130 }
131
132 pub fn entity_index(&self) -> usize {
133 self.entity_index
134 }
135
136 pub fn start(&self) -> usize {
137 self.start
138 }
139
140 pub fn end(&self) -> usize {
141 self.end
142 }
143
144 pub fn segment_len(&self) -> usize {
145 self.end.saturating_sub(self.start)
146 }
147}
148
149impl<S, V> Move<S> for ListReverseMove<S, V>
150where
151 S: PlanningSolution,
152 V: Clone + Send + Sync + Debug + 'static,
153{
154 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
155 let solution = score_director.working_solution();
156
157 if self.end <= self.start + 1 {
159 return false;
160 }
161
162 let len = (self.list_len)(solution, self.entity_index);
164 if self.end > len {
165 return false;
166 }
167
168 true
169 }
170
171 fn do_move<D: Director<S>>(&self, score_director: &mut D) {
172 score_director.before_variable_changed(self.descriptor_index, self.entity_index);
174
175 (self.list_reverse)(
177 score_director.working_solution_mut(),
178 self.entity_index,
179 self.start,
180 self.end,
181 );
182
183 score_director.after_variable_changed(self.descriptor_index, self.entity_index);
185
186 let list_reverse = self.list_reverse;
188 let entity = self.entity_index;
189 let start = self.start;
190 let end = self.end;
191
192 score_director.register_undo(Box::new(move |s: &mut S| {
193 list_reverse(s, entity, start, end);
194 }));
195 }
196
197 fn descriptor_index(&self) -> usize {
198 self.descriptor_index
199 }
200
201 fn entity_indices(&self) -> &[usize] {
202 std::slice::from_ref(&self.entity_index)
203 }
204
205 fn variable_name(&self) -> &str {
206 self.variable_name
207 }
208
209 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
210 let mut value_ids: SmallVec<[u64; 2]> = SmallVec::new();
211 for pos in self.start..self.end {
212 let value = (self.list_get)(score_director.working_solution(), self.entity_index, pos);
213 value_ids.push(encode_option_debug(value.as_ref()));
214 }
215 let entity_id = encode_usize(self.entity_index);
216 let scope = MoveTabuScope::new(self.descriptor_index, self.variable_name);
217 let destination_value_tokens: SmallVec<[ScopedValueTabuToken; 2]> = value_ids
218 .iter()
219 .copied()
220 .map(|value_id| scope.value_token(value_id))
221 .collect();
222 let move_id = scoped_move_identity(
223 scope,
224 TABU_OP_LIST_REVERSE,
225 [entity_id, encode_usize(self.start), encode_usize(self.end)],
226 );
227
228 MoveTabuSignature::new(scope, move_id.clone(), move_id)
229 .with_entity_tokens([scope.entity_token(entity_id)])
230 .with_destination_value_tokens(destination_value_tokens)
231 }
232}