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 super::{
13    ChangeMove, CompoundScalarMove, ConflictRepairMove, Move, MoveTabuSignature, PillarChangeMove,
14    PillarSwapMove, RuinRecreateMove, 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    CompoundScalar(CompoundScalarMove<S>),
29    ConflictRepair(ConflictRepairMove<S>),
30}
31
32pub enum ScalarMoveUnionUndo<S, V>
33where
34    S: PlanningSolution,
35    V: Clone + PartialEq + Send + Sync + Debug + 'static,
36{
37    Change(<ChangeMove<S, V> as Move<S>>::Undo),
38    Swap(<SwapMove<S, V> as Move<S>>::Undo),
39    PillarChange(<PillarChangeMove<S, V> as Move<S>>::Undo),
40    PillarSwap(<PillarSwapMove<S, V> as Move<S>>::Undo),
41    RuinRecreate(<RuinRecreateMove<S> as Move<S>>::Undo),
42    CompoundScalar(<CompoundScalarMove<S> as Move<S>>::Undo),
43    ConflictRepair(<ConflictRepairMove<S> as Move<S>>::Undo),
44}
45
46impl<S, V> Clone for ScalarMoveUnion<S, V>
47where
48    S: PlanningSolution,
49    V: Clone + PartialEq + Send + Sync + Debug + 'static,
50{
51    fn clone(&self) -> Self {
52        match self {
53            Self::Change(m) => Self::Change(m.clone()),
54            Self::Swap(m) => Self::Swap(*m),
55            Self::PillarChange(m) => Self::PillarChange(m.clone()),
56            Self::PillarSwap(m) => Self::PillarSwap(m.clone()),
57            Self::RuinRecreate(m) => Self::RuinRecreate(m.clone()),
58            Self::CompoundScalar(m) => Self::CompoundScalar(m.clone()),
59            Self::ConflictRepair(m) => Self::ConflictRepair(m.clone()),
60        }
61    }
62}
63
64impl<S, V> Debug for ScalarMoveUnion<S, V>
65where
66    S: PlanningSolution,
67    V: Clone + PartialEq + Send + Sync + Debug + 'static,
68{
69    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70        match self {
71            Self::Change(m) => m.fmt(f),
72            Self::Swap(m) => m.fmt(f),
73            Self::PillarChange(m) => m.fmt(f),
74            Self::PillarSwap(m) => m.fmt(f),
75            Self::RuinRecreate(m) => m.fmt(f),
76            Self::CompoundScalar(m) => m.fmt(f),
77            Self::ConflictRepair(m) => m.fmt(f),
78        }
79    }
80}
81
82impl<S, V> Move<S> for ScalarMoveUnion<S, V>
83where
84    S: PlanningSolution,
85    V: Clone + PartialEq + Send + Sync + Debug + 'static,
86{
87    type Undo = ScalarMoveUnionUndo<S, V>;
88
89    fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
90        match self {
91            Self::Change(m) => m.is_doable(score_director),
92            Self::Swap(m) => m.is_doable(score_director),
93            Self::PillarChange(m) => m.is_doable(score_director),
94            Self::PillarSwap(m) => m.is_doable(score_director),
95            Self::RuinRecreate(m) => m.is_doable(score_director),
96            Self::CompoundScalar(m) => m.is_doable(score_director),
97            Self::ConflictRepair(m) => m.is_doable(score_director),
98        }
99    }
100
101    fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
102        match self {
103            Self::Change(m) => ScalarMoveUnionUndo::Change(m.do_move(score_director)),
104            Self::Swap(m) => ScalarMoveUnionUndo::Swap(m.do_move(score_director)),
105            Self::PillarChange(m) => ScalarMoveUnionUndo::PillarChange(m.do_move(score_director)),
106            Self::PillarSwap(m) => ScalarMoveUnionUndo::PillarSwap(m.do_move(score_director)),
107            Self::RuinRecreate(m) => ScalarMoveUnionUndo::RuinRecreate(m.do_move(score_director)),
108            Self::CompoundScalar(m) => {
109                ScalarMoveUnionUndo::CompoundScalar(m.do_move(score_director))
110            }
111            Self::ConflictRepair(m) => {
112                ScalarMoveUnionUndo::ConflictRepair(m.do_move(score_director))
113            }
114        }
115    }
116
117    fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
118        match (self, undo) {
119            (Self::Change(m), ScalarMoveUnionUndo::Change(undo)) => {
120                m.undo_move(score_director, undo)
121            }
122            (Self::Swap(m), ScalarMoveUnionUndo::Swap(undo)) => m.undo_move(score_director, undo),
123            (Self::PillarChange(m), ScalarMoveUnionUndo::PillarChange(undo)) => {
124                m.undo_move(score_director, undo)
125            }
126            (Self::PillarSwap(m), ScalarMoveUnionUndo::PillarSwap(undo)) => {
127                m.undo_move(score_director, undo)
128            }
129            (Self::RuinRecreate(m), ScalarMoveUnionUndo::RuinRecreate(undo)) => {
130                m.undo_move(score_director, undo)
131            }
132            (Self::CompoundScalar(m), ScalarMoveUnionUndo::CompoundScalar(undo)) => {
133                m.undo_move(score_director, undo)
134            }
135            (Self::ConflictRepair(m), ScalarMoveUnionUndo::ConflictRepair(undo)) => {
136                m.undo_move(score_director, undo)
137            }
138            _ => panic!("scalar move undo shape must match move shape"),
139        }
140    }
141
142    fn descriptor_index(&self) -> usize {
143        match self {
144            Self::Change(m) => m.descriptor_index(),
145            Self::Swap(m) => m.descriptor_index(),
146            Self::PillarChange(m) => m.descriptor_index(),
147            Self::PillarSwap(m) => m.descriptor_index(),
148            Self::RuinRecreate(m) => m.descriptor_index(),
149            Self::CompoundScalar(m) => m.descriptor_index(),
150            Self::ConflictRepair(m) => m.descriptor_index(),
151        }
152    }
153
154    fn entity_indices(&self) -> &[usize] {
155        match self {
156            Self::Change(m) => m.entity_indices(),
157            Self::Swap(m) => m.entity_indices(),
158            Self::PillarChange(m) => m.entity_indices(),
159            Self::PillarSwap(m) => m.entity_indices(),
160            Self::RuinRecreate(m) => m.entity_indices(),
161            Self::CompoundScalar(m) => m.entity_indices(),
162            Self::ConflictRepair(m) => m.entity_indices(),
163        }
164    }
165
166    fn variable_name(&self) -> &str {
167        match self {
168            Self::Change(m) => m.variable_name(),
169            Self::Swap(m) => m.variable_name(),
170            Self::PillarChange(m) => m.variable_name(),
171            Self::PillarSwap(m) => m.variable_name(),
172            Self::RuinRecreate(m) => m.variable_name(),
173            Self::CompoundScalar(m) => m.variable_name(),
174            Self::ConflictRepair(m) => m.variable_name(),
175        }
176    }
177
178    fn telemetry_label(&self) -> &'static str {
179        match self {
180            Self::Change(m) => m.telemetry_label(),
181            Self::Swap(m) => m.telemetry_label(),
182            Self::PillarChange(m) => m.telemetry_label(),
183            Self::PillarSwap(m) => m.telemetry_label(),
184            Self::RuinRecreate(m) => m.telemetry_label(),
185            Self::CompoundScalar(m) => m.telemetry_label(),
186            Self::ConflictRepair(m) => m.telemetry_label(),
187        }
188    }
189
190    fn requires_hard_improvement(&self) -> bool {
191        match self {
192            Self::Change(m) => m.requires_hard_improvement(),
193            Self::Swap(m) => m.requires_hard_improvement(),
194            Self::PillarChange(m) => m.requires_hard_improvement(),
195            Self::PillarSwap(m) => m.requires_hard_improvement(),
196            Self::RuinRecreate(m) => m.requires_hard_improvement(),
197            Self::CompoundScalar(m) => m.requires_hard_improvement(),
198            Self::ConflictRepair(m) => m.requires_hard_improvement(),
199        }
200    }
201
202    fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
203        match self {
204            Self::Change(m) => m.tabu_signature(score_director),
205            Self::Swap(m) => m.tabu_signature(score_director),
206            Self::PillarChange(m) => m.tabu_signature(score_director),
207            Self::PillarSwap(m) => m.tabu_signature(score_director),
208            Self::RuinRecreate(m) => m.tabu_signature(score_director),
209            Self::CompoundScalar(m) => m.tabu_signature(score_director),
210            Self::ConflictRepair(m) => m.tabu_signature(score_director),
211        }
212    }
213
214    fn for_each_affected_entity(&self, visitor: &mut dyn FnMut(super::MoveAffectedEntity<'_>)) {
215        match self {
216            Self::Change(m) => m.for_each_affected_entity(visitor),
217            Self::Swap(m) => m.for_each_affected_entity(visitor),
218            Self::PillarChange(m) => m.for_each_affected_entity(visitor),
219            Self::PillarSwap(m) => m.for_each_affected_entity(visitor),
220            Self::RuinRecreate(m) => m.for_each_affected_entity(visitor),
221            Self::CompoundScalar(m) => m.for_each_affected_entity(visitor),
222            Self::ConflictRepair(m) => m.for_each_affected_entity(visitor),
223        }
224    }
225}