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