solverforge_solver/heuristic/move/
change.rs1use std::borrow::Cow;
13use std::fmt::Debug;
14
15use smallvec::smallvec;
16use solverforge_core::domain::PlanningSolution;
17use solverforge_scoring::Director;
18
19use super::metadata::{encode_option_debug, encode_usize, hash_str, MoveTabuScope};
20use super::{Move, MoveTabuSignature};
21
22pub struct ChangeMove<S, V> {
31 entity_index: usize,
32 to_value: Option<V>,
33 getter: fn(&S, usize, usize) -> Option<V>,
34 setter: fn(&mut S, usize, usize, Option<V>),
35 variable_index: usize,
36 variable_name: &'static str,
37 descriptor_index: usize,
38}
39
40impl<S, V: Clone> Clone for ChangeMove<S, V> {
41 fn clone(&self) -> Self {
42 Self {
43 entity_index: self.entity_index,
44 to_value: self.to_value.clone(),
45 getter: self.getter,
46 setter: self.setter,
47 variable_index: self.variable_index,
48 variable_name: self.variable_name,
49 descriptor_index: self.descriptor_index,
50 }
51 }
52}
53
54impl<S, V: Copy> Copy for ChangeMove<S, V> {}
55
56impl<S, V: Debug> Debug for ChangeMove<S, V> {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.debug_struct("ChangeMove")
59 .field("entity_index", &self.entity_index)
60 .field("descriptor_index", &self.descriptor_index)
61 .field("variable_index", &self.variable_index)
62 .field("variable_name", &self.variable_name)
63 .field("to_value", &self.to_value)
64 .finish()
65 }
66}
67
68impl<S, V> ChangeMove<S, V> {
69 pub fn new(
79 entity_index: usize,
80 to_value: Option<V>,
81 getter: fn(&S, usize, usize) -> Option<V>,
82 setter: fn(&mut S, usize, usize, Option<V>),
83 variable_index: usize,
84 variable_name: &'static str,
85 descriptor_index: usize,
86 ) -> Self {
87 Self {
88 entity_index,
89 to_value,
90 getter,
91 setter,
92 variable_index,
93 variable_name,
94 descriptor_index,
95 }
96 }
97
98 pub fn entity_index(&self) -> usize {
99 self.entity_index
100 }
101
102 pub fn to_value(&self) -> Option<&V> {
103 self.to_value.as_ref()
104 }
105
106 pub fn getter(&self) -> fn(&S, usize, usize) -> Option<V> {
107 self.getter
108 }
109
110 pub fn setter(&self) -> fn(&mut S, usize, usize, Option<V>) {
111 self.setter
112 }
113
114 pub fn variable_index(&self) -> usize {
115 self.variable_index
116 }
117}
118
119impl<S, V> Move<S> for ChangeMove<S, V>
120where
121 S: PlanningSolution,
122 V: Clone + PartialEq + Send + Sync + Debug + 'static,
123{
124 type Undo = Option<V>;
125
126 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
127 let current = (self.getter)(
129 score_director.working_solution(),
130 self.entity_index,
131 self.variable_index,
132 );
133
134 match (¤t, &self.to_value) {
136 (None, None) => false, (Some(cur), Some(target)) => cur != target, _ => true, }
140 }
141
142 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
143 let old_value = (self.getter)(
145 score_director.working_solution(),
146 self.entity_index,
147 self.variable_index,
148 );
149
150 score_director.before_variable_changed(self.descriptor_index, self.entity_index);
152
153 (self.setter)(
155 score_director.working_solution_mut(),
156 self.entity_index,
157 self.variable_index,
158 self.to_value.clone(),
159 );
160
161 score_director.after_variable_changed(self.descriptor_index, self.entity_index);
163
164 old_value
165 }
166
167 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
168 score_director.before_variable_changed(self.descriptor_index, self.entity_index);
169 (self.setter)(
170 score_director.working_solution_mut(),
171 self.entity_index,
172 self.variable_index,
173 undo,
174 );
175 score_director.after_variable_changed(self.descriptor_index, self.entity_index);
176 }
177
178 fn descriptor_index(&self) -> usize {
179 self.descriptor_index
180 }
181
182 fn entity_indices(&self) -> &[usize] {
183 std::slice::from_ref(&self.entity_index)
184 }
185
186 fn variable_name(&self) -> &str {
187 self.variable_name
188 }
189
190 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> Cow<'_, MoveTabuSignature> {
191 let current = (self.getter)(
192 score_director.working_solution(),
193 self.entity_index,
194 self.variable_index,
195 );
196 let from_id = encode_option_debug(current.as_ref());
197 let to_id = encode_option_debug(self.to_value.as_ref());
198 let entity_id = encode_usize(self.entity_index);
199 let variable_id = hash_str(self.variable_name);
200 let scope = MoveTabuScope::new(self.descriptor_index, self.variable_name);
201
202 Cow::Owned(
203 MoveTabuSignature::new(
204 scope,
205 smallvec![
206 encode_usize(self.descriptor_index),
207 variable_id,
208 entity_id,
209 from_id,
210 to_id
211 ],
212 smallvec![
213 encode_usize(self.descriptor_index),
214 variable_id,
215 entity_id,
216 to_id,
217 from_id
218 ],
219 )
220 .with_entity_tokens([scope.entity_token(entity_id)])
221 .with_destination_value_tokens([scope.value_token(to_id)]),
222 )
223 }
224}