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, ruin-recreate,
4and cartesian-composite moves without trait-object dispatch.
5*/
6
7use std::fmt::Debug;
8
9use solverforge_core::domain::PlanningSolution;
10use solverforge_scoring::Director;
11
12use super::{
13    ChangeMove, CompoundScalarMove, ConflictRepairMove, Move, MoveTabuSignature, PillarChangeMove,
14    PillarSwapMove, RuinRecreateMove, SequentialCompositeMove, SwapMove,
15};
16
17/// A monomorphized union of the canonical scalar move family.
18///
19/// Implements `Move<S>` by delegating to the inner variant.
20/// `Copy` when `V: Copy`, avoiding heap allocation in the move selector hot path.
21#[allow(clippy::large_enum_variant)]
22pub enum ScalarMoveUnion<S, V> {
23    Change(ChangeMove<S, V>),
24    Swap(SwapMove<S, V>),
25    PillarChange(PillarChangeMove<S, V>),
26    PillarSwap(PillarSwapMove<S, V>),
27    RuinRecreate(RuinRecreateMove<S>),
28    Composite(SequentialCompositeMove<S, ScalarMoveUnion<S, V>>),
29    CompoundScalar(CompoundScalarMove<S>),
30    ConflictRepair(ConflictRepairMove<S>),
31}
32
33impl<S, V> Clone for ScalarMoveUnion<S, V>
34where
35    S: PlanningSolution,
36    V: Clone + PartialEq + Send + Sync + Debug + 'static,
37{
38    fn clone(&self) -> Self {
39        match self {
40            Self::Change(m) => Self::Change(m.clone()),
41            Self::Swap(m) => Self::Swap(*m),
42            Self::PillarChange(m) => Self::PillarChange(m.clone()),
43            Self::PillarSwap(m) => Self::PillarSwap(m.clone()),
44            Self::RuinRecreate(m) => Self::RuinRecreate(m.clone()),
45            Self::Composite(m) => Self::Composite(m.clone()),
46            Self::CompoundScalar(m) => Self::CompoundScalar(m.clone()),
47            Self::ConflictRepair(m) => Self::ConflictRepair(m.clone()),
48        }
49    }
50}
51
52impl<S, V> Debug for ScalarMoveUnion<S, V>
53where
54    S: PlanningSolution,
55    V: Clone + PartialEq + Send + Sync + Debug + 'static,
56{
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        match self {
59            Self::Change(m) => m.fmt(f),
60            Self::Swap(m) => m.fmt(f),
61            Self::PillarChange(m) => m.fmt(f),
62            Self::PillarSwap(m) => m.fmt(f),
63            Self::RuinRecreate(m) => m.fmt(f),
64            Self::Composite(m) => m.fmt(f),
65            Self::CompoundScalar(m) => m.fmt(f),
66            Self::ConflictRepair(m) => m.fmt(f),
67        }
68    }
69}
70
71impl<S, V> Move<S> for ScalarMoveUnion<S, V>
72where
73    S: PlanningSolution,
74    V: Clone + PartialEq + Send + Sync + Debug + 'static,
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::Composite(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        }
87    }
88
89    fn do_move<D: Director<S>>(&self, score_director: &mut D) {
90        match self {
91            Self::Change(m) => m.do_move(score_director),
92            Self::Swap(m) => m.do_move(score_director),
93            Self::PillarChange(m) => m.do_move(score_director),
94            Self::PillarSwap(m) => m.do_move(score_director),
95            Self::RuinRecreate(m) => m.do_move(score_director),
96            Self::Composite(m) => m.do_move(score_director),
97            Self::CompoundScalar(m) => m.do_move(score_director),
98            Self::ConflictRepair(m) => m.do_move(score_director),
99        }
100    }
101
102    fn descriptor_index(&self) -> usize {
103        match self {
104            Self::Change(m) => m.descriptor_index(),
105            Self::Swap(m) => m.descriptor_index(),
106            Self::PillarChange(m) => m.descriptor_index(),
107            Self::PillarSwap(m) => m.descriptor_index(),
108            Self::RuinRecreate(m) => m.descriptor_index(),
109            Self::Composite(m) => m.descriptor_index(),
110            Self::CompoundScalar(m) => m.descriptor_index(),
111            Self::ConflictRepair(m) => m.descriptor_index(),
112        }
113    }
114
115    fn entity_indices(&self) -> &[usize] {
116        match self {
117            Self::Change(m) => m.entity_indices(),
118            Self::Swap(m) => m.entity_indices(),
119            Self::PillarChange(m) => m.entity_indices(),
120            Self::PillarSwap(m) => m.entity_indices(),
121            Self::RuinRecreate(m) => m.entity_indices(),
122            Self::Composite(m) => m.entity_indices(),
123            Self::CompoundScalar(m) => m.entity_indices(),
124            Self::ConflictRepair(m) => m.entity_indices(),
125        }
126    }
127
128    fn variable_name(&self) -> &str {
129        match self {
130            Self::Change(m) => m.variable_name(),
131            Self::Swap(m) => m.variable_name(),
132            Self::PillarChange(m) => m.variable_name(),
133            Self::PillarSwap(m) => m.variable_name(),
134            Self::RuinRecreate(m) => m.variable_name(),
135            Self::Composite(m) => m.variable_name(),
136            Self::CompoundScalar(m) => m.variable_name(),
137            Self::ConflictRepair(m) => m.variable_name(),
138        }
139    }
140
141    fn requires_hard_improvement(&self) -> bool {
142        match self {
143            Self::Change(m) => m.requires_hard_improvement(),
144            Self::Swap(m) => m.requires_hard_improvement(),
145            Self::PillarChange(m) => m.requires_hard_improvement(),
146            Self::PillarSwap(m) => m.requires_hard_improvement(),
147            Self::RuinRecreate(m) => m.requires_hard_improvement(),
148            Self::Composite(m) => m.requires_hard_improvement(),
149            Self::CompoundScalar(m) => m.requires_hard_improvement(),
150            Self::ConflictRepair(m) => m.requires_hard_improvement(),
151        }
152    }
153
154    fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
155        match self {
156            Self::Change(m) => m.tabu_signature(score_director),
157            Self::Swap(m) => m.tabu_signature(score_director),
158            Self::PillarChange(m) => m.tabu_signature(score_director),
159            Self::PillarSwap(m) => m.tabu_signature(score_director),
160            Self::RuinRecreate(m) => m.tabu_signature(score_director),
161            Self::Composite(m) => m.tabu_signature(score_director),
162            Self::CompoundScalar(m) => m.tabu_signature(score_director),
163            Self::ConflictRepair(m) => m.tabu_signature(score_director),
164        }
165    }
166
167    fn for_each_affected_entity(&self, visitor: &mut dyn FnMut(super::MoveAffectedEntity<'_>)) {
168        match self {
169            Self::Change(m) => m.for_each_affected_entity(visitor),
170            Self::Swap(m) => m.for_each_affected_entity(visitor),
171            Self::PillarChange(m) => m.for_each_affected_entity(visitor),
172            Self::PillarSwap(m) => m.for_each_affected_entity(visitor),
173            Self::RuinRecreate(m) => m.for_each_affected_entity(visitor),
174            Self::Composite(m) => m.for_each_affected_entity(visitor),
175            Self::CompoundScalar(m) => m.for_each_affected_entity(visitor),
176            Self::ConflictRepair(m) => m.for_each_affected_entity(visitor),
177        }
178    }
179}