use std::marker::PhantomData;
use std::time::Duration;
use solverforge_config::SolverConfig;
use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;
use crate::phase::Phase;
use crate::solver::NoTermination;
use crate::termination::{OrTermination, StepCountTermination, Termination, TimeTermination};
use super::{PhaseFactory, SolverFactory};
pub struct SolverFactoryBuilder<S, D, C, P, T>
where
S: PlanningSolution,
{
score_calculator: C,
phases: P,
termination: T,
_marker: PhantomData<fn(S, D)>,
}
impl<S, D, C> SolverFactoryBuilder<S, D, C, (), NoTermination>
where
S: PlanningSolution,
C: Fn(&S) -> S::Score + Send + Sync,
{
pub fn new(score_calculator: C) -> Self {
Self {
score_calculator,
phases: (),
termination: NoTermination,
_marker: PhantomData,
}
}
}
impl<S, D, C, P, T> SolverFactoryBuilder<S, D, C, P, T>
where
S: PlanningSolution,
{
pub fn with_phase<P2>(self, phase: P2) -> SolverFactoryBuilder<S, D, C, (P, P2), T> {
SolverFactoryBuilder {
score_calculator: self.score_calculator,
phases: (self.phases, phase),
termination: self.termination,
_marker: PhantomData,
}
}
pub fn with_phase_factory<F>(
self,
factory: F,
) -> SolverFactoryBuilder<S, D, C, (P, F::Phase), T>
where
D: Director<S>,
F: PhaseFactory<S, D>,
{
let phase = factory.create();
SolverFactoryBuilder {
score_calculator: self.score_calculator,
phases: (self.phases, phase),
termination: self.termination,
_marker: PhantomData,
}
}
pub fn with_config(
self,
config: SolverConfig,
) -> SolverFactoryBuilder<S, D, C, P, TimeTermination> {
let term = config.termination.unwrap_or_default();
let duration = term.time_limit().unwrap_or(Duration::from_secs(30));
SolverFactoryBuilder {
score_calculator: self.score_calculator,
phases: self.phases,
termination: TimeTermination::new(duration),
_marker: PhantomData,
}
}
pub fn with_time_limit(
self,
duration: Duration,
) -> SolverFactoryBuilder<S, D, C, P, TimeTermination> {
SolverFactoryBuilder {
score_calculator: self.score_calculator,
phases: self.phases,
termination: TimeTermination::new(duration),
_marker: PhantomData,
}
}
pub fn with_step_limit(
self,
steps: u64,
) -> SolverFactoryBuilder<S, D, C, P, StepCountTermination> {
SolverFactoryBuilder {
score_calculator: self.score_calculator,
phases: self.phases,
termination: StepCountTermination::new(steps),
_marker: PhantomData,
}
}
#[allow(clippy::type_complexity)]
pub fn with_time_limit_or(
self,
duration: Duration,
) -> SolverFactoryBuilder<S, D, C, P, OrTermination<(T, TimeTermination), S, D>>
where
D: Director<S>,
{
SolverFactoryBuilder {
score_calculator: self.score_calculator,
phases: self.phases,
termination: OrTermination::new((self.termination, TimeTermination::new(duration))),
_marker: PhantomData,
}
}
}
impl<S, D, C, P, T> SolverFactoryBuilder<S, D, C, P, T>
where
S: PlanningSolution,
D: Director<S>,
C: Fn(&S) -> S::Score + Send + Sync,
P: Phase<S, D>,
T: Termination<S, D>,
{
pub fn build(self) -> Result<SolverFactory<S, D, C, P, T>, SolverBuildError> {
Ok(SolverFactory::new(
self.score_calculator,
self.phases,
self.termination,
))
}
}
#[derive(Debug, Clone)]
pub enum SolverBuildError {
InvalidConfig(String),
}
impl std::fmt::Display for SolverBuildError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SolverBuildError::InvalidConfig(msg) => {
write!(f, "Invalid solver configuration: {}", msg)
}
}
}
}
impl std::error::Error for SolverBuildError {}