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