Skip to main content

solverforge_solver/heuristic/move/
either.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, Move, MoveTabuSignature, PillarChangeMove, PillarSwapMove, RuinRecreateMove,
14    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}
30
31impl<S, V> Clone for ScalarMoveUnion<S, V>
32where
33    S: PlanningSolution,
34    V: Clone + PartialEq + Send + Sync + Debug + 'static,
35{
36    fn clone(&self) -> Self {
37        match self {
38            Self::Change(m) => Self::Change(m.clone()),
39            Self::Swap(m) => Self::Swap(*m),
40            Self::PillarChange(m) => Self::PillarChange(m.clone()),
41            Self::PillarSwap(m) => Self::PillarSwap(m.clone()),
42            Self::RuinRecreate(m) => Self::RuinRecreate(m.clone()),
43            Self::Composite(m) => Self::Composite(m.clone()),
44        }
45    }
46}
47
48impl<S, V> Debug for ScalarMoveUnion<S, V>
49where
50    S: PlanningSolution,
51    V: Clone + PartialEq + Send + Sync + Debug + 'static,
52{
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        match self {
55            Self::Change(m) => m.fmt(f),
56            Self::Swap(m) => m.fmt(f),
57            Self::PillarChange(m) => m.fmt(f),
58            Self::PillarSwap(m) => m.fmt(f),
59            Self::RuinRecreate(m) => m.fmt(f),
60            Self::Composite(m) => m.fmt(f),
61        }
62    }
63}
64
65impl<S, V> Move<S> for ScalarMoveUnion<S, V>
66where
67    S: PlanningSolution,
68    V: Clone + PartialEq + Send + Sync + Debug + 'static,
69{
70    fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
71        match self {
72            Self::Change(m) => m.is_doable(score_director),
73            Self::Swap(m) => m.is_doable(score_director),
74            Self::PillarChange(m) => m.is_doable(score_director),
75            Self::PillarSwap(m) => m.is_doable(score_director),
76            Self::RuinRecreate(m) => m.is_doable(score_director),
77            Self::Composite(m) => m.is_doable(score_director),
78        }
79    }
80
81    fn do_move<D: Director<S>>(&self, score_director: &mut D) {
82        match self {
83            Self::Change(m) => m.do_move(score_director),
84            Self::Swap(m) => m.do_move(score_director),
85            Self::PillarChange(m) => m.do_move(score_director),
86            Self::PillarSwap(m) => m.do_move(score_director),
87            Self::RuinRecreate(m) => m.do_move(score_director),
88            Self::Composite(m) => m.do_move(score_director),
89        }
90    }
91
92    fn descriptor_index(&self) -> usize {
93        match self {
94            Self::Change(m) => m.descriptor_index(),
95            Self::Swap(m) => m.descriptor_index(),
96            Self::PillarChange(m) => m.descriptor_index(),
97            Self::PillarSwap(m) => m.descriptor_index(),
98            Self::RuinRecreate(m) => m.descriptor_index(),
99            Self::Composite(m) => m.descriptor_index(),
100        }
101    }
102
103    fn entity_indices(&self) -> &[usize] {
104        match self {
105            Self::Change(m) => m.entity_indices(),
106            Self::Swap(m) => m.entity_indices(),
107            Self::PillarChange(m) => m.entity_indices(),
108            Self::PillarSwap(m) => m.entity_indices(),
109            Self::RuinRecreate(m) => m.entity_indices(),
110            Self::Composite(m) => m.entity_indices(),
111        }
112    }
113
114    fn variable_name(&self) -> &str {
115        match self {
116            Self::Change(m) => m.variable_name(),
117            Self::Swap(m) => m.variable_name(),
118            Self::PillarChange(m) => m.variable_name(),
119            Self::PillarSwap(m) => m.variable_name(),
120            Self::RuinRecreate(m) => m.variable_name(),
121            Self::Composite(m) => m.variable_name(),
122        }
123    }
124
125    fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
126        match self {
127            Self::Change(m) => m.tabu_signature(score_director),
128            Self::Swap(m) => m.tabu_signature(score_director),
129            Self::PillarChange(m) => m.tabu_signature(score_director),
130            Self::PillarSwap(m) => m.tabu_signature(score_director),
131            Self::RuinRecreate(m) => m.tabu_signature(score_director),
132            Self::Composite(m) => m.tabu_signature(score_director),
133        }
134    }
135}