1use std::fmt::Debug;
12use std::marker::PhantomData;
13
14use smallvec::SmallVec;
15use solverforge_core::domain::{PlanningSolution, SolutionDescriptor};
16use solverforge_scoring::{Director, DirectorScoreState};
17
18use super::{Move, MoveArena, MoveTabuSignature};
19
20pub struct CompositeMove<S, M1, M2>
30where
31 S: PlanningSolution,
32 M1: Move<S>,
33 M2: Move<S>,
34{
35 index_1: usize,
36 index_2: usize,
37 _phantom: PhantomData<(fn() -> S, fn() -> M1, fn() -> M2)>,
38}
39
40impl<S, M1, M2> CompositeMove<S, M1, M2>
41where
42 S: PlanningSolution,
43 M1: Move<S>,
44 M2: Move<S>,
45{
46 pub fn new(index_1: usize, index_2: usize) -> Self {
47 Self {
48 index_1,
49 index_2,
50 _phantom: PhantomData,
51 }
52 }
53
54 pub fn index_1(&self) -> usize {
55 self.index_1
56 }
57
58 pub fn index_2(&self) -> usize {
59 self.index_2
60 }
61
62 pub fn is_doable_with_arenas<D: Director<S>>(
63 &self,
64 arena_1: &MoveArena<M1>,
65 arena_2: &MoveArena<M2>,
66 score_director: &D,
67 ) -> bool {
68 let m1 = arena_1.get(self.index_1);
69 let m2 = arena_2.get(self.index_2);
70
71 match (m1, m2) {
72 (Some(m1), Some(m2)) => m1.is_doable(score_director) && m2.is_doable(score_director),
73 _ => false,
74 }
75 }
76
77 pub fn do_move_with_arenas<D: Director<S>>(
79 &self,
80 arena_1: &MoveArena<M1>,
81 arena_2: &MoveArena<M2>,
82 score_director: &mut D,
83 ) {
84 let m1 = arena_1
85 .get(self.index_1)
86 .expect("composite move first arena index must remain valid");
87 let m2 = arena_2
88 .get(self.index_2)
89 .expect("composite move second arena index must remain valid");
90
91 m1.do_move(score_director);
92 m2.do_move(score_director);
93 }
94}
95
96impl<S, M1, M2> Clone for CompositeMove<S, M1, M2>
97where
98 S: PlanningSolution,
99 M1: Move<S>,
100 M2: Move<S>,
101{
102 fn clone(&self) -> Self {
103 *self
104 }
105}
106
107impl<S, M1, M2> Copy for CompositeMove<S, M1, M2>
108where
109 S: PlanningSolution,
110 M1: Move<S>,
111 M2: Move<S>,
112{
113}
114
115impl<S, M1, M2> Debug for CompositeMove<S, M1, M2>
116where
117 S: PlanningSolution,
118 M1: Move<S>,
119 M2: Move<S>,
120{
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 f.debug_struct("CompositeMove")
123 .field("index_1", &self.index_1)
124 .field("index_2", &self.index_2)
125 .finish()
126 }
127}
128
129pub(crate) struct SequentialPreviewDirector<'a, S: PlanningSolution> {
130 working_solution: S,
131 descriptor: &'a SolutionDescriptor,
132 entity_counts: Vec<Option<usize>>,
133 total_entity_count: Option<usize>,
134}
135
136impl<'a, S: PlanningSolution> SequentialPreviewDirector<'a, S> {
137 pub(crate) fn from_director<D: Director<S>>(score_director: &'a D) -> Self {
138 let descriptor = score_director.solution_descriptor();
139 let entity_counts = (0..descriptor.entity_descriptor_count())
140 .map(|descriptor_index| score_director.entity_count(descriptor_index))
141 .collect();
142
143 Self {
144 working_solution: score_director.clone_working_solution(),
145 descriptor,
146 entity_counts,
147 total_entity_count: score_director.total_entity_count(),
148 }
149 }
150}
151
152impl<S: PlanningSolution> Director<S> for SequentialPreviewDirector<'_, S> {
153 fn working_solution(&self) -> &S {
154 &self.working_solution
155 }
156
157 fn working_solution_mut(&mut self) -> &mut S {
158 &mut self.working_solution
159 }
160
161 fn calculate_score(&mut self) -> S::Score {
162 panic!("preview directors are only for selector generation")
163 }
164
165 fn solution_descriptor(&self) -> &SolutionDescriptor {
166 self.descriptor
167 }
168
169 fn clone_working_solution(&self) -> S {
170 self.working_solution.clone()
171 }
172
173 fn before_variable_changed(&mut self, _descriptor_index: usize, _entity_index: usize) {
174 self.working_solution.set_score(None);
175 }
176
177 fn after_variable_changed(&mut self, descriptor_index: usize, entity_index: usize) {
178 self.working_solution
179 .update_entity_shadows(descriptor_index, entity_index);
180 self.working_solution.set_score(None);
181 }
182
183 fn entity_count(&self, descriptor_index: usize) -> Option<usize> {
184 self.entity_counts.get(descriptor_index).copied().flatten()
185 }
186
187 fn total_entity_count(&self) -> Option<usize> {
188 self.total_entity_count
189 }
190
191 fn is_incremental(&self) -> bool {
192 false
193 }
194
195 fn snapshot_score_state(&self) -> DirectorScoreState<S::Score> {
196 DirectorScoreState {
197 solution_score: self.working_solution.score(),
198 committed_score: self.working_solution.score(),
199 initialized: self.working_solution.score().is_some(),
200 }
201 }
202
203 fn restore_score_state(&mut self, state: DirectorScoreState<S::Score>) {
204 self.working_solution.set_score(state.solution_score);
205 }
206
207 fn register_undo(&mut self, _undo: Box<dyn FnOnce(&mut S) + Send>) {}
208}
209
210pub struct SequentialCompositeMove<S, M> {
215 moves: MoveArena<M>,
216 descriptor_index: usize,
217 entity_indices: SmallVec<[usize; 8]>,
218 variable_name: String,
219 tabu_signature: MoveTabuSignature,
220 _phantom: PhantomData<fn() -> S>,
221}
222
223impl<S, M> SequentialCompositeMove<S, M>
224where
225 S: PlanningSolution,
226 M: Move<S>,
227{
228 pub fn new(
229 first: M,
230 second: M,
231 descriptor_index: usize,
232 entity_indices: SmallVec<[usize; 8]>,
233 variable_name: impl Into<String>,
234 tabu_signature: MoveTabuSignature,
235 ) -> Self {
236 let mut moves = MoveArena::with_capacity(2);
237 moves.push(first);
238 moves.push(second);
239
240 Self {
241 moves,
242 descriptor_index,
243 entity_indices,
244 variable_name: variable_name.into(),
245 tabu_signature,
246 _phantom: PhantomData,
247 }
248 }
249
250 fn first_move(&self) -> &M {
251 self.moves
252 .get(0)
253 .expect("sequential composite first move must remain valid")
254 }
255
256 fn second_move(&self) -> &M {
257 self.moves
258 .get(1)
259 .expect("sequential composite second move must remain valid")
260 }
261}
262
263pub struct SequentialCompositeMoveRef<'a, S, M>
264where
265 S: PlanningSolution,
266 M: Move<S>,
267{
268 first: &'a M,
269 second: &'a M,
270 descriptor_index: usize,
271 entity_indices: &'a [usize],
272 variable_name: &'a str,
273 tabu_signature: &'a MoveTabuSignature,
274 _phantom: PhantomData<fn() -> S>,
275}
276
277impl<S, M> Debug for SequentialCompositeMoveRef<'_, S, M>
278where
279 S: PlanningSolution,
280 M: Move<S>,
281{
282 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283 f.debug_struct("SequentialCompositeMoveRef")
284 .field("descriptor_index", &self.descriptor_index)
285 .field("variable_name", &self.variable_name)
286 .field("entity_indices", &self.entity_indices)
287 .finish()
288 }
289}
290
291impl<'a, S, M> SequentialCompositeMoveRef<'a, S, M>
292where
293 S: PlanningSolution,
294 M: Move<S>,
295{
296 pub fn new(
297 first: &'a M,
298 second: &'a M,
299 descriptor_index: usize,
300 entity_indices: &'a [usize],
301 variable_name: &'a str,
302 tabu_signature: &'a MoveTabuSignature,
303 ) -> Self {
304 Self {
305 first,
306 second,
307 descriptor_index,
308 entity_indices,
309 variable_name,
310 tabu_signature,
311 _phantom: PhantomData,
312 }
313 }
314
315 pub fn first(&self) -> &'a M {
316 self.first
317 }
318
319 pub fn second(&self) -> &'a M {
320 self.second
321 }
322}
323
324impl<S, M> Clone for SequentialCompositeMoveRef<'_, S, M>
325where
326 S: PlanningSolution,
327 M: Move<S>,
328{
329 fn clone(&self) -> Self {
330 Self {
331 first: self.first,
332 second: self.second,
333 descriptor_index: self.descriptor_index,
334 entity_indices: self.entity_indices,
335 variable_name: self.variable_name,
336 tabu_signature: self.tabu_signature,
337 _phantom: PhantomData,
338 }
339 }
340}
341
342impl<S, M> Move<S> for SequentialCompositeMoveRef<'_, S, M>
343where
344 S: PlanningSolution,
345 M: Move<S>,
346{
347 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
348 if !self.first.is_doable(score_director) {
349 return false;
350 }
351
352 let mut preview = SequentialPreviewDirector::from_director(score_director);
353 self.first.do_move(&mut preview);
354 self.second.is_doable(&preview)
355 }
356
357 fn do_move<D: Director<S>>(&self, score_director: &mut D) {
358 self.first.do_move(score_director);
359 self.second.do_move(score_director);
360 }
361
362 fn descriptor_index(&self) -> usize {
363 self.descriptor_index
364 }
365
366 fn entity_indices(&self) -> &[usize] {
367 self.entity_indices
368 }
369
370 fn variable_name(&self) -> &str {
371 self.variable_name
372 }
373
374 fn tabu_signature<D: Director<S>>(&self, _score_director: &D) -> MoveTabuSignature {
375 self.tabu_signature.clone()
376 }
377}
378
379impl<S, M> Clone for SequentialCompositeMove<S, M>
380where
381 S: PlanningSolution,
382 M: Move<S> + Clone,
383{
384 fn clone(&self) -> Self {
385 Self::new(
386 self.first_move().clone(),
387 self.second_move().clone(),
388 self.descriptor_index,
389 self.entity_indices.clone(),
390 self.variable_name.clone(),
391 self.tabu_signature.clone(),
392 )
393 }
394}
395
396impl<S, M> Debug for SequentialCompositeMove<S, M>
397where
398 S: PlanningSolution,
399 M: Move<S>,
400{
401 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
402 f.debug_struct("SequentialCompositeMove")
403 .field("descriptor_index", &self.descriptor_index)
404 .field("variable_name", &self.variable_name)
405 .field("entity_indices", &self.entity_indices)
406 .finish()
407 }
408}
409
410impl<S, M> Move<S> for SequentialCompositeMove<S, M>
411where
412 S: PlanningSolution,
413 M: Move<S>,
414{
415 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
416 let first = self.first_move();
417 if !first.is_doable(score_director) {
418 return false;
419 }
420
421 let mut preview = SequentialPreviewDirector::from_director(score_director);
422 first.do_move(&mut preview);
423 self.second_move().is_doable(&preview)
424 }
425
426 fn do_move<D: Director<S>>(&self, score_director: &mut D) {
427 self.first_move().do_move(score_director);
428 self.second_move().do_move(score_director);
429 }
430
431 fn descriptor_index(&self) -> usize {
432 self.descriptor_index
433 }
434
435 fn entity_indices(&self) -> &[usize] {
436 &self.entity_indices
437 }
438
439 fn variable_name(&self) -> &str {
440 &self.variable_name
441 }
442
443 fn tabu_signature<D: Director<S>>(&self, _score_director: &D) -> MoveTabuSignature {
444 self.tabu_signature.clone()
445 }
446}