Skip to main content

solverforge_solver/heuristic/move/
dynamic_scalar_change.rs

1use std::fmt::Debug;
2
3use smallvec::smallvec;
4use solverforge_core::domain::{DynamicScalarVariableSlot, PlanningSolution};
5use solverforge_scoring::Director;
6
7use super::metadata::{encode_option_debug, encode_usize, hash_str, MoveTabuScope};
8use super::{Move, MoveTabuSignature};
9
10pub struct DynamicScalarChangeMove<S> {
11    slot: DynamicScalarVariableSlot<S>,
12    entity_index: usize,
13    to_value: Option<usize>,
14}
15
16impl<S> Clone for DynamicScalarChangeMove<S> {
17    fn clone(&self) -> Self {
18        Self {
19            slot: self.slot.clone(),
20            entity_index: self.entity_index,
21            to_value: self.to_value,
22        }
23    }
24}
25
26impl<S> Debug for DynamicScalarChangeMove<S> {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        f.debug_struct("DynamicScalarChangeMove")
29            .field("entity_index", &self.entity_index)
30            .field("descriptor_index", &self.slot.descriptor_index())
31            .field("variable", &self.slot.variable)
32            .field("variable_name", &self.slot.variable_name)
33            .field("to_value", &self.to_value)
34            .finish()
35    }
36}
37
38impl<S> DynamicScalarChangeMove<S> {
39    pub fn new(
40        slot: DynamicScalarVariableSlot<S>,
41        entity_index: usize,
42        to_value: Option<usize>,
43    ) -> Self {
44        Self {
45            slot,
46            entity_index,
47            to_value,
48        }
49    }
50
51    pub fn entity_index(&self) -> usize {
52        self.entity_index
53    }
54
55    pub fn to_value(&self) -> Option<usize> {
56        self.to_value
57    }
58}
59
60impl<S> Move<S> for DynamicScalarChangeMove<S>
61where
62    S: PlanningSolution,
63{
64    type Undo = Option<usize>;
65
66    fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
67        if !self.slot.value_is_legal(
68            score_director.working_solution(),
69            self.entity_index,
70            self.to_value,
71        ) {
72            return false;
73        }
74        self.slot
75            .current_value(score_director.working_solution(), self.entity_index)
76            != self.to_value
77    }
78
79    fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
80        let old_value = self
81            .slot
82            .current_value(score_director.working_solution(), self.entity_index);
83        let descriptor_index = self.slot.descriptor_index();
84
85        score_director.before_variable_changed(descriptor_index, self.entity_index);
86        self.slot.set_value(
87            score_director.working_solution_mut(),
88            self.entity_index,
89            self.to_value,
90        );
91        score_director.after_variable_changed(descriptor_index, self.entity_index);
92
93        old_value
94    }
95
96    fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
97        let descriptor_index = self.slot.descriptor_index();
98        score_director.before_variable_changed(descriptor_index, self.entity_index);
99        self.slot.set_value(
100            score_director.working_solution_mut(),
101            self.entity_index,
102            undo,
103        );
104        score_director.after_variable_changed(descriptor_index, self.entity_index);
105    }
106
107    fn descriptor_index(&self) -> usize {
108        self.slot.descriptor_index()
109    }
110
111    fn entity_indices(&self) -> &[usize] {
112        std::slice::from_ref(&self.entity_index)
113    }
114
115    fn variable_name(&self) -> &str {
116        self.slot.variable_name
117    }
118
119    fn telemetry_label(&self) -> &'static str {
120        "dynamic_scalar_change"
121    }
122
123    fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
124        let current = self
125            .slot
126            .current_value(score_director.working_solution(), self.entity_index);
127        let from_id = encode_option_debug(current.as_ref());
128        let to_id = encode_option_debug(self.to_value.as_ref());
129        let entity_id = encode_usize(self.entity_index);
130        let variable_id = hash_str(self.slot.variable_name);
131        let scope = MoveTabuScope::new(self.slot.descriptor_index(), self.slot.variable_name);
132
133        MoveTabuSignature::new(
134            scope,
135            smallvec![
136                encode_usize(self.slot.descriptor_index()),
137                variable_id,
138                entity_id,
139                from_id,
140                to_id
141            ],
142            smallvec![
143                encode_usize(self.slot.descriptor_index()),
144                variable_id,
145                entity_id,
146                to_id,
147                from_id
148            ],
149        )
150        .with_entity_tokens([scope.entity_token(entity_id)])
151        .with_destination_value_tokens([scope.value_token(to_id)])
152    }
153}