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