1use std::fmt::Debug;
12use std::marker::PhantomData;
13
14use smallvec::SmallVec;
15use solverforge_core::domain::{PlanningSolution, SolutionDescriptor};
16use solverforge_scoring::{ConstraintMetadata, 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 ) -> (M1::Undo, M2::Undo) {
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 let first = m1.do_move(score_director);
92 let second = m2.do_move(score_director);
93 (first, second)
94 }
95}
96
97impl<S, M1, M2> Clone for CompositeMove<S, M1, M2>
98where
99 S: PlanningSolution,
100 M1: Move<S>,
101 M2: Move<S>,
102{
103 fn clone(&self) -> Self {
104 *self
105 }
106}
107
108impl<S, M1, M2> Copy for CompositeMove<S, M1, M2>
109where
110 S: PlanningSolution,
111 M1: Move<S>,
112 M2: Move<S>,
113{
114}
115
116impl<S, M1, M2> Debug for CompositeMove<S, M1, M2>
117where
118 S: PlanningSolution,
119 M1: Move<S>,
120 M2: Move<S>,
121{
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 f.debug_struct("CompositeMove")
124 .field("index_1", &self.index_1)
125 .field("index_2", &self.index_2)
126 .finish()
127 }
128}
129
130pub(crate) struct SequentialPreviewDirector<'a, S: PlanningSolution> {
131 working_solution: S,
132 descriptor: &'a SolutionDescriptor,
133 constraint_metadata: Vec<ConstraintMetadata<'a>>,
134 entity_counts: Vec<Option<usize>>,
135 total_entity_count: Option<usize>,
136}
137
138impl<'a, S: PlanningSolution> SequentialPreviewDirector<'a, S> {
139 pub(crate) fn from_director<D: Director<S>>(score_director: &'a D) -> Self {
140 let descriptor = score_director.solution_descriptor();
141 let entity_counts = (0..descriptor.entity_descriptor_count())
142 .map(|descriptor_index| score_director.entity_count(descriptor_index))
143 .collect();
144
145 Self {
146 working_solution: score_director.clone_working_solution(),
147 descriptor,
148 constraint_metadata: score_director.constraint_metadata(),
149 entity_counts,
150 total_entity_count: score_director.total_entity_count(),
151 }
152 }
153}
154
155impl<S: PlanningSolution> Director<S> for SequentialPreviewDirector<'_, S> {
156 fn working_solution(&self) -> &S {
157 &self.working_solution
158 }
159
160 fn working_solution_mut(&mut self) -> &mut S {
161 &mut self.working_solution
162 }
163
164 fn calculate_score(&mut self) -> S::Score {
165 panic!("preview directors are only for selector generation")
166 }
167
168 fn solution_descriptor(&self) -> &SolutionDescriptor {
169 self.descriptor
170 }
171
172 fn clone_working_solution(&self) -> S {
173 self.working_solution.clone()
174 }
175
176 fn before_variable_changed(&mut self, _descriptor_index: usize, _entity_index: usize) {
177 self.working_solution.set_score(None);
178 }
179
180 fn after_variable_changed(&mut self, descriptor_index: usize, entity_index: usize) {
181 self.working_solution
182 .update_entity_shadows(descriptor_index, entity_index);
183 self.working_solution.set_score(None);
184 }
185
186 fn entity_count(&self, descriptor_index: usize) -> Option<usize> {
187 self.entity_counts.get(descriptor_index).copied().flatten()
188 }
189
190 fn total_entity_count(&self) -> Option<usize> {
191 self.total_entity_count
192 }
193
194 fn constraint_metadata(&self) -> Vec<ConstraintMetadata<'_>> {
195 self.constraint_metadata.to_vec()
196 }
197
198 fn is_incremental(&self) -> bool {
199 false
200 }
201
202 fn snapshot_score_state(&self) -> DirectorScoreState<S::Score> {
203 DirectorScoreState {
204 solution_score: self.working_solution.score(),
205 committed_score: self.working_solution.score(),
206 initialized: self.working_solution.score().is_some(),
207 }
208 }
209
210 fn restore_score_state(&mut self, state: DirectorScoreState<S::Score>) {
211 self.working_solution.set_score(state.solution_score);
212 }
213}
214
215pub struct SequentialCompositeMove<S, M> {
220 moves: MoveArena<M>,
221 descriptor_index: usize,
222 entity_indices: SmallVec<[usize; 8]>,
223 variable_name: String,
224 tabu_signature: MoveTabuSignature,
225 require_hard_improvement: bool,
226 _phantom: PhantomData<fn() -> S>,
227}
228
229impl<S, M> SequentialCompositeMove<S, M>
230where
231 S: PlanningSolution,
232 M: Move<S>,
233{
234 pub fn new(
235 first: M,
236 second: M,
237 descriptor_index: usize,
238 entity_indices: SmallVec<[usize; 8]>,
239 variable_name: impl Into<String>,
240 tabu_signature: MoveTabuSignature,
241 ) -> Self {
242 let mut moves = MoveArena::with_capacity(2);
243 moves.push(first);
244 moves.push(second);
245
246 Self {
247 moves,
248 descriptor_index,
249 entity_indices,
250 variable_name: variable_name.into(),
251 tabu_signature,
252 require_hard_improvement: false,
253 _phantom: PhantomData,
254 }
255 }
256
257 pub fn with_require_hard_improvement(mut self, require_hard_improvement: bool) -> Self {
258 self.require_hard_improvement = require_hard_improvement;
259 self
260 }
261
262 fn first_move(&self) -> &M {
263 self.moves
264 .get(0)
265 .expect("sequential composite first move must remain valid")
266 }
267
268 fn second_move(&self) -> &M {
269 self.moves
270 .get(1)
271 .expect("sequential composite second move must remain valid")
272 }
273}
274
275pub struct SequentialCompositeMoveRef<'a, S, M>
276where
277 S: PlanningSolution,
278 M: Move<S>,
279{
280 first: &'a M,
281 second: &'a M,
282 descriptor_index: usize,
283 entity_indices: &'a [usize],
284 variable_name: &'a str,
285 tabu_signature: &'a MoveTabuSignature,
286 require_hard_improvement: bool,
287 _phantom: PhantomData<fn() -> S>,
288}
289
290impl<S, M> Debug for SequentialCompositeMoveRef<'_, S, M>
291where
292 S: PlanningSolution,
293 M: Move<S>,
294{
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 f.debug_struct("SequentialCompositeMoveRef")
297 .field("descriptor_index", &self.descriptor_index)
298 .field("variable_name", &self.variable_name)
299 .field("entity_indices", &self.entity_indices)
300 .finish()
301 }
302}
303
304impl<'a, S, M> SequentialCompositeMoveRef<'a, S, M>
305where
306 S: PlanningSolution,
307 M: Move<S>,
308{
309 pub fn new(
310 first: &'a M,
311 second: &'a M,
312 descriptor_index: usize,
313 entity_indices: &'a [usize],
314 variable_name: &'a str,
315 tabu_signature: &'a MoveTabuSignature,
316 require_hard_improvement: bool,
317 ) -> Self {
318 Self {
319 first,
320 second,
321 descriptor_index,
322 entity_indices,
323 variable_name,
324 tabu_signature,
325 require_hard_improvement,
326 _phantom: PhantomData,
327 }
328 }
329
330 pub fn first(&self) -> &'a M {
331 self.first
332 }
333
334 pub fn second(&self) -> &'a M {
335 self.second
336 }
337}
338
339impl<S, M> Clone for SequentialCompositeMoveRef<'_, S, M>
340where
341 S: PlanningSolution,
342 M: Move<S>,
343{
344 fn clone(&self) -> Self {
345 Self {
346 first: self.first,
347 second: self.second,
348 descriptor_index: self.descriptor_index,
349 entity_indices: self.entity_indices,
350 variable_name: self.variable_name,
351 tabu_signature: self.tabu_signature,
352 require_hard_improvement: self.require_hard_improvement,
353 _phantom: PhantomData,
354 }
355 }
356}
357
358impl<S, M> Move<S> for SequentialCompositeMoveRef<'_, S, M>
359where
360 S: PlanningSolution,
361 M: Move<S>,
362{
363 type Undo = (M::Undo, M::Undo);
364
365 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
366 if !self.first.is_doable(score_director) {
367 return false;
368 }
369
370 let mut preview = SequentialPreviewDirector::from_director(score_director);
371 let _ = self.first.do_move(&mut preview);
372 self.second.is_doable(&preview)
373 }
374
375 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
376 let first = self.first.do_move(score_director);
377 let second = self.second.do_move(score_director);
378 (first, second)
379 }
380
381 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
382 self.second.undo_move(score_director, undo.1);
383 self.first.undo_move(score_director, undo.0);
384 }
385
386 fn descriptor_index(&self) -> usize {
387 self.descriptor_index
388 }
389
390 fn entity_indices(&self) -> &[usize] {
391 self.entity_indices
392 }
393
394 fn variable_name(&self) -> &str {
395 self.variable_name
396 }
397
398 fn requires_hard_improvement(&self) -> bool {
399 self.require_hard_improvement
400 || self.first.requires_hard_improvement()
401 || self.second.requires_hard_improvement()
402 }
403
404 fn tabu_signature<D: Director<S>>(&self, _score_director: &D) -> MoveTabuSignature {
405 self.tabu_signature.clone()
406 }
407}
408
409impl<S, M> Clone for SequentialCompositeMove<S, M>
410where
411 S: PlanningSolution,
412 M: Move<S> + Clone,
413{
414 fn clone(&self) -> Self {
415 Self::new(
416 self.first_move().clone(),
417 self.second_move().clone(),
418 self.descriptor_index,
419 self.entity_indices.clone(),
420 self.variable_name.clone(),
421 self.tabu_signature.clone(),
422 )
423 .with_require_hard_improvement(self.require_hard_improvement)
424 }
425}
426
427impl<S, M> Debug for SequentialCompositeMove<S, M>
428where
429 S: PlanningSolution,
430 M: Move<S>,
431{
432 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
433 f.debug_struct("SequentialCompositeMove")
434 .field("descriptor_index", &self.descriptor_index)
435 .field("variable_name", &self.variable_name)
436 .field("entity_indices", &self.entity_indices)
437 .finish()
438 }
439}
440
441impl<S, M> Move<S> for SequentialCompositeMove<S, M>
442where
443 S: PlanningSolution,
444 M: Move<S>,
445{
446 type Undo = (M::Undo, M::Undo);
447
448 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
449 let first = self.first_move();
450 if !first.is_doable(score_director) {
451 return false;
452 }
453
454 let mut preview = SequentialPreviewDirector::from_director(score_director);
455 let _ = first.do_move(&mut preview);
456 self.second_move().is_doable(&preview)
457 }
458
459 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
460 let first = self.first_move().do_move(score_director);
461 let second = self.second_move().do_move(score_director);
462 (first, second)
463 }
464
465 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
466 self.second_move().undo_move(score_director, undo.1);
467 self.first_move().undo_move(score_director, undo.0);
468 }
469
470 fn descriptor_index(&self) -> usize {
471 self.descriptor_index
472 }
473
474 fn entity_indices(&self) -> &[usize] {
475 &self.entity_indices
476 }
477
478 fn variable_name(&self) -> &str {
479 &self.variable_name
480 }
481
482 fn requires_hard_improvement(&self) -> bool {
483 self.require_hard_improvement
484 || self.first_move().requires_hard_improvement()
485 || self.second_move().requires_hard_improvement()
486 }
487
488 fn tabu_signature<D: Director<S>>(&self, _score_director: &D) -> MoveTabuSignature {
489 self.tabu_signature.clone()
490 }
491}