Skip to main content

solverforge_solver/heuristic/move/
composite.rs

1/* CompositeMove - applies two moves in sequence by arena indices.
2
3This move stores indices into two arenas. The moves themselves
4live in their respective arenas - CompositeMove just references them.
5
6# Zero-Erasure Design
7
8No cloning, no boxing - just typed arena indices.
9*/
10
11use std::fmt::Debug;
12use std::marker::PhantomData;
13
14use solverforge_core::domain::PlanningSolution;
15use solverforge_scoring::Director;
16
17use super::{Move, MoveArena};
18
19/// A move that applies two moves in sequence via arena indices.
20///
21/// The moves live in separate arenas. CompositeMove stores the indices
22/// and arena references needed to execute both moves.
23///
24/// # Type Parameters
25/// * `S` - The planning solution type
26/// * `M1` - The first move type
27/// * `M2` - The second move type
28pub struct CompositeMove<S, M1, M2>
29where
30    S: PlanningSolution,
31    M1: Move<S>,
32    M2: Move<S>,
33{
34    index_1: usize,
35    index_2: usize,
36    _phantom: PhantomData<(fn() -> S, fn() -> M1, fn() -> M2)>,
37}
38
39impl<S, M1, M2> CompositeMove<S, M1, M2>
40where
41    S: PlanningSolution,
42    M1: Move<S>,
43    M2: Move<S>,
44{
45    pub fn new(index_1: usize, index_2: usize) -> Self {
46        Self {
47            index_1,
48            index_2,
49            _phantom: PhantomData,
50        }
51    }
52
53    pub fn index_1(&self) -> usize {
54        self.index_1
55    }
56
57    pub fn index_2(&self) -> usize {
58        self.index_2
59    }
60
61    pub fn is_doable_with_arenas<D: Director<S>>(
62        &self,
63        arena_1: &MoveArena<M1>,
64        arena_2: &MoveArena<M2>,
65        score_director: &D,
66    ) -> bool {
67        let m1 = arena_1.get(self.index_1);
68        let m2 = arena_2.get(self.index_2);
69
70        match (m1, m2) {
71            (Some(m1), Some(m2)) => m1.is_doable(score_director) || m2.is_doable(score_director),
72            _ => false,
73        }
74    }
75
76    /// Executes both moves using the arenas.
77    pub fn do_move_with_arenas<D: Director<S>>(
78        &self,
79        arena_1: &MoveArena<M1>,
80        arena_2: &MoveArena<M2>,
81        score_director: &mut D,
82    ) {
83        if let Some(m1) = arena_1.get(self.index_1) {
84            m1.do_move(score_director);
85        }
86        if let Some(m2) = arena_2.get(self.index_2) {
87            m2.do_move(score_director);
88        }
89    }
90}
91
92impl<S, M1, M2> Clone for CompositeMove<S, M1, M2>
93where
94    S: PlanningSolution,
95    M1: Move<S>,
96    M2: Move<S>,
97{
98    fn clone(&self) -> Self {
99        *self
100    }
101}
102
103impl<S, M1, M2> Copy for CompositeMove<S, M1, M2>
104where
105    S: PlanningSolution,
106    M1: Move<S>,
107    M2: Move<S>,
108{
109}
110
111impl<S, M1, M2> Debug for CompositeMove<S, M1, M2>
112where
113    S: PlanningSolution,
114    M1: Move<S>,
115    M2: Move<S>,
116{
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        f.debug_struct("CompositeMove")
119            .field("index_1", &self.index_1)
120            .field("index_2", &self.index_2)
121            .finish()
122    }
123}