Skip to main content

solverforge_solver/heuristic/move/
traits.rs

1// Move trait definition.
2
3use std::fmt::Debug;
4
5use solverforge_core::domain::PlanningSolution;
6use solverforge_scoring::Director;
7
8use super::MoveTabuSignature;
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub struct MoveAffectedEntity<'a> {
12    pub descriptor_index: usize,
13    pub entity_index: usize,
14    pub variable_name: &'a str,
15}
16
17/// A move that modifies one or more planning variables.
18///
19/// Moves are fully monomorphized for maximum performance - no boxing, no virtual dispatch.
20/// Undo is handled by move-owned typed data, not boxed director callbacks.
21///
22/// # Type Parameters
23/// * `S` - The planning solution type
24///
25/// # Implementation Notes
26/// - Moves should be lightweight
27/// - Moves are NEVER cloned - ownership transfers via arena indices
28/// - Methods are generic over D to allow use with concrete directors
29pub trait Move<S: PlanningSolution>: Send + Sync + Debug {
30    type Undo: Send;
31
32    /* Returns true if this move can be executed in the current state.
33
34    A move is not doable if:
35    - The source value equals the destination value (no change)
36    - Required entities are pinned
37    - The move would violate hard constraints that can be detected early
38    */
39    fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool;
40
41    /* Executes this move, modifying the working solution.
42
43    This method modifies the planning variables through the score director.
44    Returns concrete undo data that can be passed back to `undo_move`.
45    */
46    fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo;
47
48    fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo);
49
50    fn descriptor_index(&self) -> usize;
51
52    fn entity_indices(&self) -> &[usize];
53
54    fn variable_name(&self) -> &str;
55
56    fn telemetry_label(&self) -> &'static str {
57        "move"
58    }
59
60    fn requires_hard_improvement(&self) -> bool {
61        false
62    }
63
64    fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature;
65
66    fn for_each_affected_entity(&self, visitor: &mut dyn FnMut(MoveAffectedEntity<'_>)) {
67        let descriptor_index = self.descriptor_index();
68        let variable_name = self.variable_name();
69        for &entity_index in self.entity_indices() {
70            visitor(MoveAffectedEntity {
71                descriptor_index,
72                entity_index,
73                variable_name,
74            });
75        }
76    }
77}