use std::fmt::Debug;
use std::marker::PhantomData;
use solverforge_core::domain::PlanningSolution;
use solverforge_core::score::Score;
use solverforge_scoring::{Director, RecordingDirector};
use crate::heuristic::r#move::Move;
use super::Placement;
pub trait ConstructionForager<S, M>: Send + Debug
where
S: PlanningSolution,
M: Move<S>,
{
fn pick_move_index<D: Director<S>>(
&self,
placement: &Placement<S, M>,
score_director: &mut D,
) -> Option<usize>;
}
pub struct FirstFitForager<S, M> {
_phantom: PhantomData<fn() -> (S, M)>,
}
impl<S, M> Clone for FirstFitForager<S, M> {
fn clone(&self) -> Self {
*self
}
}
impl<S, M> Copy for FirstFitForager<S, M> {}
impl<S, M> Default for FirstFitForager<S, M> {
fn default() -> Self {
Self::new()
}
}
impl<S, M> Debug for FirstFitForager<S, M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FirstFitForager").finish()
}
}
impl<S, M> FirstFitForager<S, M> {
pub fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}
impl<S, M> ConstructionForager<S, M> for FirstFitForager<S, M>
where
S: PlanningSolution,
M: Move<S>,
{
fn pick_move_index<D: Director<S>>(
&self,
placement: &Placement<S, M>,
score_director: &mut D,
) -> Option<usize> {
for (idx, m) in placement.moves.iter().enumerate() {
if m.is_doable(score_director) {
return Some(idx);
}
}
None
}
}
pub struct BestFitForager<S, M> {
_phantom: PhantomData<fn() -> (S, M)>,
}
impl<S, M> Clone for BestFitForager<S, M> {
fn clone(&self) -> Self {
*self
}
}
impl<S, M> Copy for BestFitForager<S, M> {}
impl<S, M> Default for BestFitForager<S, M> {
fn default() -> Self {
Self::new()
}
}
impl<S, M> Debug for BestFitForager<S, M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BestFitForager").finish()
}
}
impl<S, M> BestFitForager<S, M> {
pub fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}
impl<S, M> ConstructionForager<S, M> for BestFitForager<S, M>
where
S: PlanningSolution,
M: Move<S>,
{
fn pick_move_index<D: Director<S>>(
&self,
placement: &Placement<S, M>,
score_director: &mut D,
) -> Option<usize> {
let mut best_idx: Option<usize> = None;
let mut best_score: Option<S::Score> = None;
for (idx, m) in placement.moves.iter().enumerate() {
if !m.is_doable(score_director) {
continue;
}
let score = {
let mut recording = RecordingDirector::new(score_director);
m.do_move(&mut recording);
let score = recording.calculate_score();
recording.undo_changes();
score
};
let is_better = match &best_score {
None => true,
Some(best) => score > *best,
};
if is_better {
best_idx = Some(idx);
best_score = Some(score);
}
}
best_idx
}
}
pub struct FirstFeasibleForager<S, M> {
_phantom: PhantomData<fn() -> (S, M)>,
}
impl<S, M> Clone for FirstFeasibleForager<S, M> {
fn clone(&self) -> Self {
*self
}
}
impl<S, M> Copy for FirstFeasibleForager<S, M> {}
impl<S, M> Default for FirstFeasibleForager<S, M> {
fn default() -> Self {
Self::new()
}
}
impl<S, M> Debug for FirstFeasibleForager<S, M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FirstFeasibleForager").finish()
}
}
impl<S, M> FirstFeasibleForager<S, M> {
pub fn new() -> Self {
Self {
_phantom: PhantomData,
}
}
}
impl<S, M> ConstructionForager<S, M> for FirstFeasibleForager<S, M>
where
S: PlanningSolution,
M: Move<S>,
{
fn pick_move_index<D: Director<S>>(
&self,
placement: &Placement<S, M>,
score_director: &mut D,
) -> Option<usize> {
let mut fallback_idx: Option<usize> = None;
let mut fallback_score: Option<S::Score> = None;
for (idx, m) in placement.moves.iter().enumerate() {
if !m.is_doable(score_director) {
continue;
}
let score = {
let mut recording = RecordingDirector::new(score_director);
m.do_move(&mut recording);
let score = recording.calculate_score();
if score.is_feasible() {
recording.undo_changes();
return Some(idx);
}
recording.undo_changes();
score
};
let is_better = match &fallback_score {
None => true,
Some(best) => score > *best,
};
if is_better {
fallback_idx = Some(idx);
fallback_score = Some(score);
}
}
fallback_idx
}
}
pub struct WeakestFitForager<S, M> {
strength_fn: fn(&M) -> i64,
_phantom: PhantomData<fn() -> S>,
}
impl<S, M> Clone for WeakestFitForager<S, M> {
fn clone(&self) -> Self {
*self
}
}
impl<S, M> Copy for WeakestFitForager<S, M> {}
impl<S, M> Debug for WeakestFitForager<S, M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WeakestFitForager").finish()
}
}
impl<S, M> WeakestFitForager<S, M> {
pub fn new(strength_fn: fn(&M) -> i64) -> Self {
Self {
strength_fn,
_phantom: PhantomData,
}
}
}
impl<S, M> ConstructionForager<S, M> for WeakestFitForager<S, M>
where
S: PlanningSolution,
M: Move<S>,
{
fn pick_move_index<D: Director<S>>(
&self,
placement: &Placement<S, M>,
score_director: &mut D,
) -> Option<usize> {
let mut best_idx: Option<usize> = None;
let mut min_strength: Option<i64> = None;
for (idx, m) in placement.moves.iter().enumerate() {
if !m.is_doable(score_director) {
continue;
}
let strength = (self.strength_fn)(m);
let is_weaker = match min_strength {
None => true,
Some(best) => strength < best,
};
if is_weaker {
best_idx = Some(idx);
min_strength = Some(strength);
}
}
best_idx
}
}
pub struct StrongestFitForager<S, M> {
strength_fn: fn(&M) -> i64,
_phantom: PhantomData<fn() -> S>,
}
impl<S, M> Clone for StrongestFitForager<S, M> {
fn clone(&self) -> Self {
*self
}
}
impl<S, M> Copy for StrongestFitForager<S, M> {}
impl<S, M> Debug for StrongestFitForager<S, M> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StrongestFitForager").finish()
}
}
impl<S, M> StrongestFitForager<S, M> {
pub fn new(strength_fn: fn(&M) -> i64) -> Self {
Self {
strength_fn,
_phantom: PhantomData,
}
}
}
impl<S, M> ConstructionForager<S, M> for StrongestFitForager<S, M>
where
S: PlanningSolution,
M: Move<S>,
{
fn pick_move_index<D: Director<S>>(
&self,
placement: &Placement<S, M>,
score_director: &mut D,
) -> Option<usize> {
let mut best_idx: Option<usize> = None;
let mut max_strength: Option<i64> = None;
for (idx, m) in placement.moves.iter().enumerate() {
if !m.is_doable(score_director) {
continue;
}
let strength = (self.strength_fn)(m);
let is_stronger = match max_strength {
None => true,
Some(best) => strength > best,
};
if is_stronger {
best_idx = Some(idx);
max_strength = Some(strength);
}
}
best_idx
}
}
#[cfg(test)]
#[path = "forager_tests.rs"]
mod tests;