use std::fmt::Debug;
use std::marker::PhantomData;
use rand::rngs::StdRng;
use rand::{RngExt, SeedableRng};
use smallvec::SmallVec;
use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;
use crate::heuristic::r#move::RuinMove;
use super::MoveSelector;
pub struct RuinMoveSelector<S, V> {
min_ruin_count: usize,
max_ruin_count: usize,
seed: Option<u64>,
entity_count: fn(&S) -> usize,
getter: fn(&S, usize) -> Option<V>,
setter: fn(&mut S, usize, Option<V>),
variable_name: &'static str,
descriptor_index: usize,
moves_per_step: usize,
_phantom: PhantomData<fn() -> V>,
}
impl<S, V: Debug> Debug for RuinMoveSelector<S, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RuinMoveSelector")
.field("min_ruin_count", &self.min_ruin_count)
.field("max_ruin_count", &self.max_ruin_count)
.field("moves_per_step", &self.moves_per_step)
.field("variable_name", &self.variable_name)
.field("descriptor_index", &self.descriptor_index)
.finish()
}
}
impl<S, V> RuinMoveSelector<S, V> {
pub fn new(
min_ruin_count: usize,
max_ruin_count: usize,
entity_count: fn(&S) -> usize,
getter: fn(&S, usize) -> Option<V>,
setter: fn(&mut S, usize, Option<V>),
variable_name: &'static str,
descriptor_index: usize,
) -> Self {
assert!(min_ruin_count >= 1, "min_ruin_count must be at least 1");
assert!(
max_ruin_count >= min_ruin_count,
"max_ruin_count must be >= min_ruin_count"
);
Self {
min_ruin_count,
max_ruin_count,
seed: None,
entity_count,
getter,
setter,
variable_name,
descriptor_index,
moves_per_step: 10, _phantom: PhantomData,
}
}
pub fn with_moves_per_step(mut self, count: usize) -> Self {
self.moves_per_step = count;
self
}
pub fn with_seed(mut self, seed: u64) -> Self {
self.seed = Some(seed);
self
}
fn create_rng(&self) -> StdRng {
match self.seed {
Some(seed) => StdRng::seed_from_u64(seed),
None => StdRng::from_rng(&mut rand::rng()),
}
}
}
impl<S, V> MoveSelector<S, RuinMove<S, V>> for RuinMoveSelector<S, V>
where
S: PlanningSolution,
V: Clone + Send + Sync + Debug + 'static,
{
fn iter_moves<'a, D: Director<S>>(
&'a self,
score_director: &'a D,
) -> impl Iterator<Item = RuinMove<S, V>> + 'a {
let total_entities = (self.entity_count)(score_director.working_solution());
let getter = self.getter;
let setter = self.setter;
let variable_name = self.variable_name;
let descriptor_index = self.descriptor_index;
let min = self.min_ruin_count.min(total_entities);
let max = self.max_ruin_count.min(total_entities);
let moves_count = self.moves_per_step;
let mut rng = self.create_rng();
let subsets: Vec<SmallVec<[usize; 8]>> = (0..moves_count)
.map(|_| {
if total_entities == 0 {
return SmallVec::new();
}
let ruin_count = if min == max {
min
} else {
rng.random_range(min..=max)
};
let mut indices: SmallVec<[usize; 8]> = (0..total_entities).collect();
for i in 0..ruin_count {
let j = rng.random_range(i..total_entities);
indices.swap(i, j);
}
indices.truncate(ruin_count);
indices
})
.collect();
subsets.into_iter().map(move |indices| {
RuinMove::new(&indices, getter, setter, variable_name, descriptor_index)
})
}
fn size<D: Director<S>>(&self, score_director: &D) -> usize {
let total = (self.entity_count)(score_director.working_solution());
if total == 0 {
return 0;
}
self.moves_per_step
}
fn is_never_ending(&self) -> bool {
false
}
}