Skip to main content

solverforge_solver/heuristic/move/
scalar_union.rs

1/* ScalarMoveUnion - a monomorphized union of the canonical scalar move family.
2
3This allows local search to combine scalar change, swap, pillar, and
4ruin-recreate moves without trait-object dispatch.
5*/
6
7use std::borrow::Cow;
8use std::fmt::Debug;
9
10use solverforge_core::domain::PlanningSolution;
11use solverforge_scoring::Director;
12
13use crate::stats::CandidateTraceIdentity;
14
15use super::{
16    ChangeMove, CompoundScalarMove, ConflictRepairMove, Move, MoveTabuSignature, PillarChangeMove,
17    PillarSwapMove, RuinRecreateMove, RuntimeCompoundMove, SwapMove,
18};
19
20/// A monomorphized union of the canonical scalar move family.
21///
22/// Implements `Move<S>` by delegating to the inner variant.
23/// `Copy` when `V: Copy`, avoiding heap allocation in the move selector hot path.
24#[allow(clippy::large_enum_variant)]
25pub enum ScalarMoveUnion<S, V> {
26    Change(ChangeMove<S, V>),
27    Swap(SwapMove<S, V>),
28    PillarChange(PillarChangeMove<S, V>),
29    PillarSwap(PillarSwapMove<S, V>),
30    RuinRecreate(RuinRecreateMove<S>),
31    CompoundScalar(CompoundScalarMove<S>),
32    ConflictRepair(ConflictRepairMove<S>),
33    RuntimeCompound(RuntimeCompoundMove<S>),
34}
35
36pub enum ScalarMoveUnionUndo<S, V>
37where
38    S: PlanningSolution,
39    V: Clone + PartialEq + Send + Sync + Debug + 'static,
40{
41    Change(<ChangeMove<S, V> as Move<S>>::Undo),
42    Swap(<SwapMove<S, V> as Move<S>>::Undo),
43    PillarChange(<PillarChangeMove<S, V> as Move<S>>::Undo),
44    PillarSwap(<PillarSwapMove<S, V> as Move<S>>::Undo),
45    RuinRecreate(<RuinRecreateMove<S> as Move<S>>::Undo),
46    CompoundScalar(<CompoundScalarMove<S> as Move<S>>::Undo),
47    ConflictRepair(<ConflictRepairMove<S> as Move<S>>::Undo),
48    RuntimeCompound(<RuntimeCompoundMove<S> as Move<S>>::Undo),
49}
50
51impl<S, V> Debug for ScalarMoveUnion<S, V>
52where
53    S: PlanningSolution,
54    V: Clone + PartialEq + Send + Sync + Debug + 'static,
55{
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        match self {
58            Self::Change(m) => m.fmt(f),
59            Self::Swap(m) => m.fmt(f),
60            Self::PillarChange(m) => m.fmt(f),
61            Self::PillarSwap(m) => m.fmt(f),
62            Self::RuinRecreate(m) => m.fmt(f),
63            Self::CompoundScalar(m) => m.fmt(f),
64            Self::ConflictRepair(m) => m.fmt(f),
65            Self::RuntimeCompound(m) => m.fmt(f),
66        }
67    }
68}
69
70impl<S, V> Move<S> for ScalarMoveUnion<S, V>
71where
72    S: PlanningSolution,
73    V: Clone + PartialEq + Send + Sync + Debug + 'static,
74{
75    type Undo = ScalarMoveUnionUndo<S, V>;
76
77    fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
78        match self {
79            Self::Change(m) => m.is_doable(score_director),
80            Self::Swap(m) => m.is_doable(score_director),
81            Self::PillarChange(m) => m.is_doable(score_director),
82            Self::PillarSwap(m) => m.is_doable(score_director),
83            Self::RuinRecreate(m) => m.is_doable(score_director),
84            Self::CompoundScalar(m) => m.is_doable(score_director),
85            Self::ConflictRepair(m) => m.is_doable(score_director),
86            Self::RuntimeCompound(m) => m.is_doable(score_director),
87        }
88    }
89
90    fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
91        match self {
92            Self::Change(m) => ScalarMoveUnionUndo::Change(m.do_move(score_director)),
93            Self::Swap(m) => ScalarMoveUnionUndo::Swap(m.do_move(score_director)),
94            Self::PillarChange(m) => ScalarMoveUnionUndo::PillarChange(m.do_move(score_director)),
95            Self::PillarSwap(m) => ScalarMoveUnionUndo::PillarSwap(m.do_move(score_director)),
96            Self::RuinRecreate(m) => ScalarMoveUnionUndo::RuinRecreate(m.do_move(score_director)),
97            Self::CompoundScalar(m) => {
98                ScalarMoveUnionUndo::CompoundScalar(m.do_move(score_director))
99            }
100            Self::ConflictRepair(m) => {
101                ScalarMoveUnionUndo::ConflictRepair(m.do_move(score_director))
102            }
103            Self::RuntimeCompound(m) => {
104                ScalarMoveUnionUndo::RuntimeCompound(m.do_move(score_director))
105            }
106        }
107    }
108
109    fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
110        match (self, undo) {
111            (Self::Change(m), ScalarMoveUnionUndo::Change(undo)) => {
112                m.undo_move(score_director, undo)
113            }
114            (Self::Swap(m), ScalarMoveUnionUndo::Swap(undo)) => m.undo_move(score_director, undo),
115            (Self::PillarChange(m), ScalarMoveUnionUndo::PillarChange(undo)) => {
116                m.undo_move(score_director, undo)
117            }
118            (Self::PillarSwap(m), ScalarMoveUnionUndo::PillarSwap(undo)) => {
119                m.undo_move(score_director, undo)
120            }
121            (Self::RuinRecreate(m), ScalarMoveUnionUndo::RuinRecreate(undo)) => {
122                m.undo_move(score_director, undo)
123            }
124            (Self::CompoundScalar(m), ScalarMoveUnionUndo::CompoundScalar(undo)) => {
125                m.undo_move(score_director, undo)
126            }
127            (Self::ConflictRepair(m), ScalarMoveUnionUndo::ConflictRepair(undo)) => {
128                m.undo_move(score_director, undo)
129            }
130            (Self::RuntimeCompound(m), ScalarMoveUnionUndo::RuntimeCompound(undo)) => {
131                m.undo_move(score_director, undo)
132            }
133            _ => panic!("scalar move undo shape must match move shape"),
134        }
135    }
136
137    fn descriptor_index(&self) -> usize {
138        match self {
139            Self::Change(m) => m.descriptor_index(),
140            Self::Swap(m) => m.descriptor_index(),
141            Self::PillarChange(m) => m.descriptor_index(),
142            Self::PillarSwap(m) => m.descriptor_index(),
143            Self::RuinRecreate(m) => m.descriptor_index(),
144            Self::CompoundScalar(m) => m.descriptor_index(),
145            Self::ConflictRepair(m) => m.descriptor_index(),
146            Self::RuntimeCompound(m) => m.descriptor_index(),
147        }
148    }
149
150    fn entity_indices(&self) -> &[usize] {
151        match self {
152            Self::Change(m) => m.entity_indices(),
153            Self::Swap(m) => m.entity_indices(),
154            Self::PillarChange(m) => m.entity_indices(),
155            Self::PillarSwap(m) => m.entity_indices(),
156            Self::RuinRecreate(m) => m.entity_indices(),
157            Self::CompoundScalar(m) => m.entity_indices(),
158            Self::ConflictRepair(m) => m.entity_indices(),
159            Self::RuntimeCompound(m) => m.entity_indices(),
160        }
161    }
162
163    fn variable_name(&self) -> &str {
164        match self {
165            Self::Change(m) => m.variable_name(),
166            Self::Swap(m) => m.variable_name(),
167            Self::PillarChange(m) => m.variable_name(),
168            Self::PillarSwap(m) => m.variable_name(),
169            Self::RuinRecreate(m) => m.variable_name(),
170            Self::CompoundScalar(m) => m.variable_name(),
171            Self::ConflictRepair(m) => m.variable_name(),
172            Self::RuntimeCompound(m) => m.variable_name(),
173        }
174    }
175
176    fn telemetry_label(&self) -> &'static str {
177        match self {
178            Self::Change(m) => m.telemetry_label(),
179            Self::Swap(m) => m.telemetry_label(),
180            Self::PillarChange(m) => m.telemetry_label(),
181            Self::PillarSwap(m) => m.telemetry_label(),
182            Self::RuinRecreate(m) => m.telemetry_label(),
183            Self::CompoundScalar(m) => m.telemetry_label(),
184            Self::ConflictRepair(m) => m.telemetry_label(),
185            Self::RuntimeCompound(m) => m.telemetry_label(),
186        }
187    }
188
189    fn requires_hard_improvement(&self) -> bool {
190        match self {
191            Self::Change(m) => m.requires_hard_improvement(),
192            Self::Swap(m) => m.requires_hard_improvement(),
193            Self::PillarChange(m) => m.requires_hard_improvement(),
194            Self::PillarSwap(m) => m.requires_hard_improvement(),
195            Self::RuinRecreate(m) => m.requires_hard_improvement(),
196            Self::CompoundScalar(m) => m.requires_hard_improvement(),
197            Self::ConflictRepair(m) => m.requires_hard_improvement(),
198            Self::RuntimeCompound(m) => m.requires_hard_improvement(),
199        }
200    }
201
202    fn requires_score_improvement(&self) -> bool {
203        match self {
204            Self::Change(m) => m.requires_score_improvement(),
205            Self::Swap(m) => m.requires_score_improvement(),
206            Self::PillarChange(m) => m.requires_score_improvement(),
207            Self::PillarSwap(m) => m.requires_score_improvement(),
208            Self::RuinRecreate(m) => m.requires_score_improvement(),
209            Self::CompoundScalar(m) => m.requires_score_improvement(),
210            Self::ConflictRepair(m) => m.requires_score_improvement(),
211            Self::RuntimeCompound(m) => m.requires_score_improvement(),
212        }
213    }
214
215    fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> Cow<'_, MoveTabuSignature> {
216        match self {
217            Self::Change(m) => m.tabu_signature(score_director),
218            Self::Swap(m) => m.tabu_signature(score_director),
219            Self::PillarChange(m) => m.tabu_signature(score_director),
220            Self::PillarSwap(m) => m.tabu_signature(score_director),
221            Self::RuinRecreate(m) => m.tabu_signature(score_director),
222            Self::CompoundScalar(m) => m.tabu_signature(score_director),
223            Self::ConflictRepair(m) => m.tabu_signature(score_director),
224            Self::RuntimeCompound(m) => m.tabu_signature(score_director),
225        }
226    }
227
228    fn candidate_trace_identity(&self) -> Option<CandidateTraceIdentity> {
229        match self {
230            Self::Change(m) => m.candidate_trace_identity(),
231            Self::Swap(m) => m.candidate_trace_identity(),
232            Self::PillarChange(m) => m.candidate_trace_identity(),
233            Self::PillarSwap(m) => m.candidate_trace_identity(),
234            Self::RuinRecreate(m) => m.candidate_trace_identity(),
235            Self::CompoundScalar(m) => m.candidate_trace_identity(),
236            Self::ConflictRepair(m) => m.candidate_trace_identity(),
237            Self::RuntimeCompound(m) => m.candidate_trace_identity(),
238        }
239    }
240
241    fn for_each_affected_entity<W: FnMut(super::MoveAffectedEntity<'_>)>(&self, visitor: &mut W) {
242        match self {
243            Self::Change(m) => m.for_each_affected_entity(visitor),
244            Self::Swap(m) => m.for_each_affected_entity(visitor),
245            Self::PillarChange(m) => m.for_each_affected_entity(visitor),
246            Self::PillarSwap(m) => m.for_each_affected_entity(visitor),
247            Self::RuinRecreate(m) => m.for_each_affected_entity(visitor),
248            Self::CompoundScalar(m) => m.for_each_affected_entity(visitor),
249            Self::ConflictRepair(m) => m.for_each_affected_entity(visitor),
250            Self::RuntimeCompound(m) => m.for_each_affected_entity(visitor),
251        }
252    }
253}