solverforge_solver/heuristic/move/
dynamic_scalar_change.rs1use 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}