use std::cell::RefCell;
use std::fmt::Debug;
use std::marker::PhantomData;
use rand::rngs::SmallRng;
use rand::{RngExt, SeedableRng};
use smallvec::SmallVec;
use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;
use crate::heuristic::r#move::ListRuinMove;
use super::MoveSelector;
pub struct ListRuinMoveSelector<S, V> {
min_ruin_count: usize,
max_ruin_count: usize,
rng: RefCell<SmallRng>,
entity_count: fn(&S) -> usize,
list_len: fn(&S, usize) -> usize,
list_remove: fn(&mut S, usize, usize) -> V,
list_insert: fn(&mut S, usize, usize, V),
variable_name: &'static str,
descriptor_index: usize,
moves_per_step: usize,
_phantom: PhantomData<fn() -> V>,
}
unsafe impl<S, V> Send for ListRuinMoveSelector<S, V> {}
impl<S, V: Debug> Debug for ListRuinMoveSelector<S, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ListRuinMoveSelector")
.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> ListRuinMoveSelector<S, V> {
#[allow(clippy::too_many_arguments)]
pub fn new(
min_ruin_count: usize,
max_ruin_count: usize,
entity_count: fn(&S) -> usize,
list_len: fn(&S, usize) -> usize,
list_remove: fn(&mut S, usize, usize) -> V,
list_insert: fn(&mut S, usize, usize, 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,
rng: RefCell::new(SmallRng::from_rng(&mut rand::rng())),
entity_count,
list_len,
list_remove,
list_insert,
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.rng = RefCell::new(SmallRng::seed_from_u64(seed));
self
}
}
impl<S, V> MoveSelector<S, ListRuinMove<S, V>> for ListRuinMoveSelector<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 = ListRuinMove<S, V>> + 'a {
let solution = score_director.working_solution();
let total_entities = (self.entity_count)(solution);
let list_len = self.list_len;
let list_remove = self.list_remove;
let list_insert = self.list_insert;
let variable_name = self.variable_name;
let descriptor_index = self.descriptor_index;
let min_ruin = self.min_ruin_count;
let max_ruin = self.max_ruin_count;
let moves_count = self.moves_per_step;
let mut rng = self.rng.borrow_mut();
let moves: Vec<ListRuinMove<S, V>> = if total_entities == 0 {
Vec::new()
} else {
(0..moves_count)
.filter_map(|_| {
let entity_idx = rng.random_range(0..total_entities);
let list_length = list_len(solution, entity_idx);
if list_length == 0 {
return None;
}
let min = min_ruin.min(list_length);
let max = max_ruin.min(list_length);
let ruin_count = if min == max {
min
} else {
rng.random_range(min..=max)
};
let mut indices: SmallVec<[usize; 8]> = (0..list_length).collect();
for i in 0..ruin_count {
let j = rng.random_range(i..list_length);
indices.swap(i, j);
}
indices.truncate(ruin_count);
Some(ListRuinMove::new(
entity_idx,
&indices,
self.entity_count,
list_len,
list_remove,
list_insert,
variable_name,
descriptor_index,
))
})
.collect()
};
moves.into_iter()
}
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
}
}