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::RuinMove;
use super::move_selector::{
CandidateId, CandidateStore, MoveCandidateRef, MoveCursor, MoveSelector,
};
pub struct RuinVariableAccess<S, V> {
entity_count: fn(&S) -> usize,
getter: fn(&S, usize, usize) -> Option<V>,
setter: fn(&mut S, usize, usize, Option<V>),
variable_index: usize,
variable_name: &'static str,
descriptor_index: usize,
_phantom: PhantomData<fn() -> V>,
}
impl<S, V> Clone for RuinVariableAccess<S, V> {
fn clone(&self) -> Self {
*self
}
}
impl<S, V> Copy for RuinVariableAccess<S, V> {}
impl<S, V> RuinVariableAccess<S, V> {
pub fn new(
entity_count: fn(&S) -> usize,
getter: fn(&S, usize, usize) -> Option<V>,
setter: fn(&mut S, usize, usize, Option<V>),
variable_index: usize,
variable_name: &'static str,
descriptor_index: usize,
) -> Self {
Self {
entity_count,
getter,
setter,
variable_index,
variable_name,
descriptor_index,
_phantom: PhantomData,
}
}
}
pub struct RuinMoveSelector<S, V> {
min_ruin_count: usize,
max_ruin_count: usize,
rng: RefCell<SmallRng>,
access: RuinVariableAccess<S, V>,
moves_per_step: usize,
}
pub struct RuinMoveCursor<S, V>
where
S: PlanningSolution,
V: Clone + Send + Sync + Debug + 'static,
{
store: CandidateStore<S, RuinMove<S, V>>,
subsets: std::vec::IntoIter<SmallVec<[usize; 8]>>,
access: RuinVariableAccess<S, V>,
}
impl<S, V> RuinMoveCursor<S, V>
where
S: PlanningSolution,
V: Clone + Send + Sync + Debug + 'static,
{
pub(crate) fn next_subset(&mut self) -> Option<SmallVec<[usize; 8]>> {
self.subsets.next()
}
fn next_move(&mut self) -> Option<RuinMove<S, V>> {
let indices = self.next_subset()?;
Some(RuinMove::from_indices(
indices,
self.access.getter,
self.access.setter,
self.access.variable_index,
self.access.variable_name,
self.access.descriptor_index,
))
}
}
impl<S, V> MoveCursor<S, RuinMove<S, V>> for RuinMoveCursor<S, V>
where
S: PlanningSolution,
V: Clone + Send + Sync + Debug + 'static,
{
fn next_candidate(&mut self) -> Option<CandidateId> {
let mov = self.next_move()?;
Some(self.store.push(mov))
}
fn candidate(&self, id: CandidateId) -> Option<MoveCandidateRef<'_, S, RuinMove<S, V>>> {
self.store.candidate(id)
}
fn take_candidate(&mut self, id: CandidateId) -> RuinMove<S, V> {
self.store.take_candidate(id)
}
fn next_owned_candidate(&mut self) -> Option<RuinMove<S, V>> {
self.next_move()
}
#[inline(always)]
fn next_owned_candidate_matching(
&mut self,
predicate: for<'a> fn(MoveCandidateRef<'a, S, RuinMove<S, V>>) -> bool,
) -> Option<RuinMove<S, V>> {
loop {
let mov = self.next_move()?;
if predicate(MoveCandidateRef::Borrowed(&mov)) {
return Some(mov);
}
}
}
fn release_candidate(&mut self, id: CandidateId) -> bool {
self.store.release_candidate(id)
}
}
impl<S, V> Iterator for RuinMoveCursor<S, V>
where
S: PlanningSolution,
V: Clone + Send + Sync + Debug + 'static,
{
type Item = RuinMove<S, V>;
fn next(&mut self) -> Option<Self::Item> {
self.next_owned_candidate()
}
}
unsafe impl<S, V> Send for RuinMoveSelector<S, 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.access.variable_name)
.field("descriptor_index", &self.access.descriptor_index)
.finish()
}
}
impl<S, V> RuinMoveSelector<S, V> {
pub fn new(
min_ruin_count: usize,
max_ruin_count: usize,
access: RuinVariableAccess<S, V>,
) -> 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())),
access,
moves_per_step: 10, }
}
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, RuinMove<S, V>> for RuinMoveSelector<S, V>
where
S: PlanningSolution,
V: Clone + Send + Sync + Debug + 'static,
{
type Cursor<'a>
= RuinMoveCursor<S, V>
where
Self: 'a;
fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
let access = self.access;
let total_entities = (access.entity_count)(score_director.working_solution());
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.rng.borrow_mut();
let mut permutation: Vec<usize> = (0..total_entities).collect();
let subsets: Vec<SmallVec<[usize; 8]>> = (0..moves_count)
.map(|_| {
if total_entities == 0 {
return SmallVec::new();
}
for (index, entity) in permutation.iter_mut().enumerate() {
*entity = index;
}
let ruin_count = if min == max {
min
} else {
rng.random_range(min..=max)
};
for i in 0..ruin_count {
let j = rng.random_range(i..total_entities);
permutation.swap(i, j);
}
permutation[..ruin_count].iter().copied().collect()
})
.collect();
RuinMoveCursor {
store: CandidateStore::new(),
subsets: subsets.into_iter(),
access,
}
}
fn size<D: Director<S>>(&self, score_director: &D) -> usize {
let total = (self.access.entity_count)(score_director.working_solution());
if total == 0 {
return 0;
}
self.moves_per_step
}
fn is_never_ending(&self) -> bool {
false
}
}