use std::cmp::Ordering;
use std::fmt::Debug;
use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;
use super::entity::{EntityReference, EntitySelector};
use super::mimic::MimicRecorder;
pub trait NearbyDistanceMeter<Origin, Destination>: Send + Sync + Debug {
fn distance(&self, origin: &Origin, destination: &Destination) -> f64;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum NearbyDistributionType {
#[default]
Linear,
Parabolic,
Block,
}
#[derive(Debug, Clone)]
pub struct NearbySelectionConfig {
pub distribution_type: NearbyDistributionType,
pub max_nearby_size: Option<usize>,
pub min_distance: f64,
}
impl Default for NearbySelectionConfig {
fn default() -> Self {
Self {
distribution_type: NearbyDistributionType::Linear,
max_nearby_size: None,
min_distance: 0.0,
}
}
}
impl NearbySelectionConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_distribution_type(mut self, distribution_type: NearbyDistributionType) -> Self {
self.distribution_type = distribution_type;
self
}
pub fn with_max_nearby_size(mut self, max_size: usize) -> Self {
self.max_nearby_size = Some(max_size);
self
}
pub fn with_min_distance(mut self, min_distance: f64) -> Self {
self.min_distance = min_distance;
self
}
}
pub trait DynDistanceMeter: Send + Sync + Debug {
fn distance_between<S: PlanningSolution>(
&self,
score_director: &dyn Director<S>,
origin: EntityReference,
destination: EntityReference,
) -> f64;
}
pub struct NearbyEntitySelector<S, M, ES> {
child: ES,
origin_recorder: MimicRecorder,
distance_meter: M,
config: NearbySelectionConfig,
_phantom: std::marker::PhantomData<fn() -> S>,
}
impl<S, M, ES> NearbyEntitySelector<S, M, ES> {
pub fn new(
child: ES,
origin_recorder: MimicRecorder,
distance_meter: M,
config: NearbySelectionConfig,
) -> Self {
Self {
child,
origin_recorder,
distance_meter,
config,
_phantom: std::marker::PhantomData,
}
}
}
impl<S: PlanningSolution, M: DynDistanceMeter, ES: Debug> Debug for NearbyEntitySelector<S, M, ES> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NearbyEntitySelector")
.field("child", &self.child)
.field("origin_recorder_id", &self.origin_recorder.id())
.field("distance_meter", &self.distance_meter)
.field("config", &self.config)
.finish()
}
}
impl<S, M, ES> EntitySelector<S> for NearbyEntitySelector<S, M, ES>
where
S: PlanningSolution,
M: DynDistanceMeter + 'static,
ES: EntitySelector<S>,
{
fn iter<'a, D: Director<S>>(
&'a self,
score_director: &'a D,
) -> impl Iterator<Item = EntityReference> + 'a {
let origin = self.origin_recorder.get_recorded_entity();
let mut candidates: Vec<(EntityReference, f64)> = match origin {
Some(origin) => self
.child
.iter(score_director)
.filter(move |&dest| dest != origin) .map(move |dest| {
let dist = self
.distance_meter
.distance_between(score_director, origin, dest);
(dest, dist)
})
.filter(|(_, dist)| *dist >= self.config.min_distance)
.collect(),
None => Vec::new(),
};
candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal));
if let Some(max_size) = self.config.max_nearby_size {
candidates.truncate(max_size);
}
candidates.into_iter().map(|(entity, _)| entity)
}
fn size<D: Director<S>>(&self, score_director: &D) -> usize {
let child_size = self.child.size(score_director);
match self.config.max_nearby_size {
Some(max) => child_size.min(max),
None => child_size,
}
}
}