solverforge_solver/builder/
forager.rs1use solverforge_config::{ForagerConfig, ScoreTieBreak};
4use solverforge_core::domain::PlanningSolution;
5use solverforge_core::score::Score;
6use std::fmt::Debug;
7
8use crate::heuristic::r#move::Move;
9use crate::heuristic::selector::move_selector::CandidateId;
10use crate::phase::localsearch::{
11 AcceptedCountForager, BestScoreForager, FirstAcceptedForager, FirstBestScoreImprovingForager,
12 FirstLastStepScoreImprovingForager, ForagerDecision, LocalSearchForager,
13};
14
15#[allow(clippy::large_enum_variant)] pub enum AnyForager<S: PlanningSolution> {
22 AcceptedCount(AcceptedCountForager<S>),
24 FirstAccepted(FirstAcceptedForager<S>),
26 BestScore(BestScoreForager<S>),
28 BestScoreImproving(FirstBestScoreImprovingForager<S>),
30 LastStepScoreImproving(FirstLastStepScoreImprovingForager<S>),
32}
33
34impl<S: PlanningSolution> Debug for AnyForager<S> {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 match self {
37 Self::AcceptedCount(a) => write!(f, "AnyForager::AcceptedCount({a:?})"),
38 Self::FirstAccepted(a) => write!(f, "AnyForager::FirstAccepted({a:?})"),
39 Self::BestScore(a) => write!(f, "AnyForager::BestScore({a:?})"),
40 Self::BestScoreImproving(a) => write!(f, "AnyForager::BestScoreImproving({a:?})"),
41 Self::LastStepScoreImproving(a) => {
42 write!(f, "AnyForager::LastStepScoreImproving({a:?})")
43 }
44 }
45 }
46}
47
48impl<S: PlanningSolution, M: Move<S>> LocalSearchForager<S, M> for AnyForager<S>
49where
50 S::Score: Score,
51{
52 fn step_started(&mut self, best_score: S::Score, last_step_score: S::Score, step_seed: u64) {
53 match self {
54 Self::AcceptedCount(f) => {
55 LocalSearchForager::<S, M>::step_started(f, best_score, last_step_score, step_seed)
56 }
57 Self::FirstAccepted(f) => {
58 LocalSearchForager::<S, M>::step_started(f, best_score, last_step_score, step_seed)
59 }
60 Self::BestScore(f) => {
61 LocalSearchForager::<S, M>::step_started(f, best_score, last_step_score, step_seed)
62 }
63 Self::BestScoreImproving(f) => {
64 LocalSearchForager::<S, M>::step_started(f, best_score, last_step_score, step_seed)
65 }
66 Self::LastStepScoreImproving(f) => {
67 LocalSearchForager::<S, M>::step_started(f, best_score, last_step_score, step_seed)
68 }
69 }
70 }
71
72 fn add_move_index(&mut self, index: CandidateId, score: S::Score) -> ForagerDecision {
73 match self {
74 Self::AcceptedCount(f) => LocalSearchForager::<S, M>::add_move_index(f, index, score),
75 Self::FirstAccepted(f) => LocalSearchForager::<S, M>::add_move_index(f, index, score),
76 Self::BestScore(f) => LocalSearchForager::<S, M>::add_move_index(f, index, score),
77 Self::BestScoreImproving(f) => {
78 LocalSearchForager::<S, M>::add_move_index(f, index, score)
79 }
80 Self::LastStepScoreImproving(f) => {
81 LocalSearchForager::<S, M>::add_move_index(f, index, score)
82 }
83 }
84 }
85
86 fn is_quit_early(&self) -> bool {
87 match self {
88 Self::AcceptedCount(f) => LocalSearchForager::<S, M>::is_quit_early(f),
89 Self::FirstAccepted(f) => LocalSearchForager::<S, M>::is_quit_early(f),
90 Self::BestScore(f) => LocalSearchForager::<S, M>::is_quit_early(f),
91 Self::BestScoreImproving(f) => LocalSearchForager::<S, M>::is_quit_early(f),
92 Self::LastStepScoreImproving(f) => LocalSearchForager::<S, M>::is_quit_early(f),
93 }
94 }
95
96 fn accepted_count_limit(&self) -> Option<usize> {
97 match self {
98 Self::AcceptedCount(f) => LocalSearchForager::<S, M>::accepted_count_limit(f),
99 Self::FirstAccepted(f) => LocalSearchForager::<S, M>::accepted_count_limit(f),
100 Self::BestScore(f) => LocalSearchForager::<S, M>::accepted_count_limit(f),
101 Self::BestScoreImproving(f) => LocalSearchForager::<S, M>::accepted_count_limit(f),
102 Self::LastStepScoreImproving(f) => LocalSearchForager::<S, M>::accepted_count_limit(f),
103 }
104 }
105
106 fn pick_move_index(&mut self) -> Option<(CandidateId, S::Score)> {
107 match self {
108 Self::AcceptedCount(f) => LocalSearchForager::<S, M>::pick_move_index(f),
109 Self::FirstAccepted(f) => LocalSearchForager::<S, M>::pick_move_index(f),
110 Self::BestScore(f) => LocalSearchForager::<S, M>::pick_move_index(f),
111 Self::BestScoreImproving(f) => LocalSearchForager::<S, M>::pick_move_index(f),
112 Self::LastStepScoreImproving(f) => LocalSearchForager::<S, M>::pick_move_index(f),
113 }
114 }
115}
116
117pub struct ForagerBuilder;
119
120impl ForagerBuilder {
121 pub fn build<S: PlanningSolution>(
123 config: Option<&ForagerConfig>,
124 score_tie_break: ScoreTieBreak,
125 ) -> AnyForager<S>
126 where
127 S::Score: Score,
128 {
129 let random_ties = matches!(score_tie_break, ScoreTieBreak::Random);
130 let Some(cfg) = config else {
131 return AnyForager::AcceptedCount(AcceptedCountForager::new(1, random_ties));
132 };
133
134 match cfg {
135 ForagerConfig::AcceptedCount(accepted) => {
136 let limit = accepted.limit.unwrap_or(1).max(1);
137 AnyForager::AcceptedCount(AcceptedCountForager::new(limit, random_ties))
138 }
139 ForagerConfig::BestScore => AnyForager::BestScore(BestScoreForager::new(random_ties)),
140 ForagerConfig::FirstAccepted => AnyForager::FirstAccepted(FirstAcceptedForager::new()),
141 ForagerConfig::FirstBestScoreImproving => {
142 AnyForager::BestScoreImproving(FirstBestScoreImprovingForager::new(random_ties))
143 }
144 ForagerConfig::FirstLastStepScoreImproving => AnyForager::LastStepScoreImproving(
145 FirstLastStepScoreImprovingForager::new(random_ties),
146 ),
147 }
148 }
149}