solverforge_solver/phase/construction/
forager.rs1use std::fmt::Debug;
13use std::marker::PhantomData;
14
15use solverforge_core::domain::PlanningSolution;
16use solverforge_core::score::Score;
17use solverforge_scoring::{Director, RecordingDirector};
18
19use crate::heuristic::r#move::Move;
20
21use super::Placement;
22
23pub trait ConstructionForager<S, M>: Send + Debug
32where
33 S: PlanningSolution,
34 M: Move<S>,
35{
36 fn pick_move_index<D: Director<S>>(
41 &self,
42 placement: &Placement<S, M>,
43 score_director: &mut D,
44 ) -> Option<usize>;
45}
46
47pub struct FirstFitForager<S, M> {
52 _phantom: PhantomData<fn() -> (S, M)>,
53}
54
55impl<S, M> Clone for FirstFitForager<S, M> {
56 fn clone(&self) -> Self {
57 *self
58 }
59}
60
61impl<S, M> Copy for FirstFitForager<S, M> {}
62
63impl<S, M> Default for FirstFitForager<S, M> {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl<S, M> Debug for FirstFitForager<S, M> {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 f.debug_struct("FirstFitForager").finish()
72 }
73}
74
75impl<S, M> FirstFitForager<S, M> {
76 pub fn new() -> Self {
77 Self {
78 _phantom: PhantomData,
79 }
80 }
81}
82
83impl<S, M> ConstructionForager<S, M> for FirstFitForager<S, M>
84where
85 S: PlanningSolution,
86 M: Move<S>,
87{
88 fn pick_move_index<D: Director<S>>(
89 &self,
90 placement: &Placement<S, M>,
91 score_director: &mut D,
92 ) -> Option<usize> {
93 for (idx, m) in placement.moves.iter().enumerate() {
94 if m.is_doable(score_director) {
95 return Some(idx);
96 }
97 }
98 None
99 }
100}
101
102pub struct BestFitForager<S, M> {
108 _phantom: PhantomData<fn() -> (S, M)>,
109}
110
111impl<S, M> Clone for BestFitForager<S, M> {
112 fn clone(&self) -> Self {
113 *self
114 }
115}
116
117impl<S, M> Copy for BestFitForager<S, M> {}
118
119impl<S, M> Default for BestFitForager<S, M> {
120 fn default() -> Self {
121 Self::new()
122 }
123}
124
125impl<S, M> Debug for BestFitForager<S, M> {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 f.debug_struct("BestFitForager").finish()
128 }
129}
130
131impl<S, M> BestFitForager<S, M> {
132 pub fn new() -> Self {
133 Self {
134 _phantom: PhantomData,
135 }
136 }
137}
138
139impl<S, M> ConstructionForager<S, M> for BestFitForager<S, M>
140where
141 S: PlanningSolution,
142 M: Move<S>,
143{
144 fn pick_move_index<D: Director<S>>(
145 &self,
146 placement: &Placement<S, M>,
147 score_director: &mut D,
148 ) -> Option<usize> {
149 let mut best_idx: Option<usize> = None;
150 let mut best_score: Option<S::Score> = None;
151
152 for (idx, m) in placement.moves.iter().enumerate() {
153 if !m.is_doable(score_director) {
154 continue;
155 }
156
157 let score = {
159 let mut recording = RecordingDirector::new(score_director);
160
161 m.do_move(&mut recording);
163
164 let score = recording.calculate_score();
166
167 recording.undo_changes();
169
170 score
171 };
172
173 let is_better = match &best_score {
175 None => true,
176 Some(best) => score > *best,
177 };
178
179 if is_better {
180 best_idx = Some(idx);
181 best_score = Some(score);
182 }
183 }
184
185 best_idx
186 }
187}
188
189pub struct FirstFeasibleForager<S, M> {
194 _phantom: PhantomData<fn() -> (S, M)>,
195}
196
197impl<S, M> Clone for FirstFeasibleForager<S, M> {
198 fn clone(&self) -> Self {
199 *self
200 }
201}
202
203impl<S, M> Copy for FirstFeasibleForager<S, M> {}
204
205impl<S, M> Default for FirstFeasibleForager<S, M> {
206 fn default() -> Self {
207 Self::new()
208 }
209}
210
211impl<S, M> Debug for FirstFeasibleForager<S, M> {
212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 f.debug_struct("FirstFeasibleForager").finish()
214 }
215}
216
217impl<S, M> FirstFeasibleForager<S, M> {
218 pub fn new() -> Self {
219 Self {
220 _phantom: PhantomData,
221 }
222 }
223}
224
225impl<S, M> ConstructionForager<S, M> for FirstFeasibleForager<S, M>
226where
227 S: PlanningSolution,
228 M: Move<S>,
229{
230 fn pick_move_index<D: Director<S>>(
231 &self,
232 placement: &Placement<S, M>,
233 score_director: &mut D,
234 ) -> Option<usize> {
235 let mut fallback_idx: Option<usize> = None;
236 let mut fallback_score: Option<S::Score> = None;
237
238 for (idx, m) in placement.moves.iter().enumerate() {
239 if !m.is_doable(score_director) {
240 continue;
241 }
242
243 let score = {
245 let mut recording = RecordingDirector::new(score_director);
246
247 m.do_move(&mut recording);
249
250 let score = recording.calculate_score();
252
253 if score.is_feasible() {
255 recording.undo_changes();
256 return Some(idx);
257 }
258
259 recording.undo_changes();
261
262 score
263 };
264
265 let is_better = match &fallback_score {
267 None => true,
268 Some(best) => score > *best,
269 };
270
271 if is_better {
272 fallback_idx = Some(idx);
273 fallback_score = Some(score);
274 }
275 }
276
277 fallback_idx
279 }
280}
281
282pub struct WeakestFitForager<S, M> {
288 strength_fn: fn(&M) -> i64,
290 _phantom: PhantomData<fn() -> S>,
291}
292
293impl<S, M> Clone for WeakestFitForager<S, M> {
294 fn clone(&self) -> Self {
295 *self
296 }
297}
298
299impl<S, M> Copy for WeakestFitForager<S, M> {}
300
301impl<S, M> Debug for WeakestFitForager<S, M> {
302 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
303 f.debug_struct("WeakestFitForager").finish()
304 }
305}
306
307impl<S, M> WeakestFitForager<S, M> {
308 pub fn new(strength_fn: fn(&M) -> i64) -> Self {
313 Self {
314 strength_fn,
315 _phantom: PhantomData,
316 }
317 }
318}
319
320impl<S, M> ConstructionForager<S, M> for WeakestFitForager<S, M>
321where
322 S: PlanningSolution,
323 M: Move<S>,
324{
325 fn pick_move_index<D: Director<S>>(
326 &self,
327 placement: &Placement<S, M>,
328 score_director: &mut D,
329 ) -> Option<usize> {
330 let mut best_idx: Option<usize> = None;
331 let mut min_strength: Option<i64> = None;
332
333 for (idx, m) in placement.moves.iter().enumerate() {
334 if !m.is_doable(score_director) {
335 continue;
336 }
337
338 let strength = (self.strength_fn)(m);
339
340 let is_weaker = match min_strength {
341 None => true,
342 Some(best) => strength < best,
343 };
344
345 if is_weaker {
346 best_idx = Some(idx);
347 min_strength = Some(strength);
348 }
349 }
350
351 best_idx
352 }
353}
354
355pub struct StrongestFitForager<S, M> {
361 strength_fn: fn(&M) -> i64,
363 _phantom: PhantomData<fn() -> S>,
364}
365
366impl<S, M> Clone for StrongestFitForager<S, M> {
367 fn clone(&self) -> Self {
368 *self
369 }
370}
371
372impl<S, M> Copy for StrongestFitForager<S, M> {}
373
374impl<S, M> Debug for StrongestFitForager<S, M> {
375 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376 f.debug_struct("StrongestFitForager").finish()
377 }
378}
379
380impl<S, M> StrongestFitForager<S, M> {
381 pub fn new(strength_fn: fn(&M) -> i64) -> Self {
386 Self {
387 strength_fn,
388 _phantom: PhantomData,
389 }
390 }
391}
392
393impl<S, M> ConstructionForager<S, M> for StrongestFitForager<S, M>
394where
395 S: PlanningSolution,
396 M: Move<S>,
397{
398 fn pick_move_index<D: Director<S>>(
399 &self,
400 placement: &Placement<S, M>,
401 score_director: &mut D,
402 ) -> Option<usize> {
403 let mut best_idx: Option<usize> = None;
404 let mut max_strength: Option<i64> = None;
405
406 for (idx, m) in placement.moves.iter().enumerate() {
407 if !m.is_doable(score_director) {
408 continue;
409 }
410
411 let strength = (self.strength_fn)(m);
412
413 let is_stronger = match max_strength {
414 None => true,
415 Some(best) => strength > best,
416 };
417
418 if is_stronger {
419 best_idx = Some(idx);
420 max_strength = Some(strength);
421 }
422 }
423
424 best_idx
425 }
426}
427
428#[cfg(test)]
429#[path = "forager_tests.rs"]
430mod tests;