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