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