use std::fmt::Debug;
use std::marker::PhantomData;
use smallvec::SmallVec;
use solverforge_core::domain::PlanningSolution;
use solverforge_scoring::Director;
use super::Move;
pub struct ListRuinMove<S, V> {
entity_index: usize,
element_indices: SmallVec<[usize; 8]>,
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,
_phantom: PhantomData<fn() -> V>,
}
impl<S, V> Clone for ListRuinMove<S, V> {
fn clone(&self) -> Self {
Self {
entity_index: self.entity_index,
element_indices: self.element_indices.clone(),
entity_count: self.entity_count,
list_len: self.list_len,
list_remove: self.list_remove,
list_insert: self.list_insert,
variable_name: self.variable_name,
descriptor_index: self.descriptor_index,
_phantom: PhantomData,
}
}
}
impl<S, V: Debug> Debug for ListRuinMove<S, V> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ListRuinMove")
.field("entity", &self.entity_index)
.field("elements", &self.element_indices.as_slice())
.field("variable_name", &self.variable_name)
.finish()
}
}
impl<S, V> ListRuinMove<S, V> {
#[allow(clippy::too_many_arguments)]
pub fn new(
entity_index: usize,
element_indices: &[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 {
let mut indices: SmallVec<[usize; 8]> = SmallVec::from_slice(element_indices);
indices.sort_unstable();
Self {
entity_index,
element_indices: indices,
entity_count,
list_len,
list_remove,
list_insert,
variable_name,
descriptor_index,
_phantom: PhantomData,
}
}
pub fn entity_index(&self) -> usize {
self.entity_index
}
pub fn element_indices(&self) -> &[usize] {
&self.element_indices
}
pub fn ruin_count(&self) -> usize {
self.element_indices.len()
}
}
impl<S, V> Move<S> for ListRuinMove<S, V>
where
S: PlanningSolution,
V: Clone + Send + Sync + Debug + 'static,
{
fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
if self.element_indices.is_empty() {
return false;
}
let solution = score_director.working_solution();
let len = (self.list_len)(solution, self.entity_index);
self.element_indices.iter().all(|&idx| idx < len)
}
fn do_move<D: Director<S>>(&self, score_director: &mut D) {
let list_remove = self.list_remove;
let list_insert = self.list_insert;
let list_len = self.list_len;
let entity_count = self.entity_count;
let src = self.entity_index;
let descriptor = self.descriptor_index;
score_director.before_variable_changed(descriptor, src);
let mut removed: SmallVec<[V; 8]> = SmallVec::new();
for &idx in self.element_indices.iter().rev() {
let value = list_remove(score_director.working_solution_mut(), src, idx);
removed.push(value);
}
removed.reverse();
score_director.after_variable_changed(descriptor, src);
let mut placements: SmallVec<[(usize, usize); 8]> = SmallVec::new();
let n_entities = entity_count(score_director.working_solution());
for elem in removed.iter() {
let mut best_score: Option<S::Score> = None;
let mut best_entity = src;
let mut best_pos = list_len(score_director.working_solution(), src);
for e in 0..n_entities {
let len = list_len(score_director.working_solution(), e);
for pos in 0..=len {
score_director.before_variable_changed(descriptor, e);
list_insert(score_director.working_solution_mut(), e, pos, elem.clone());
score_director.after_variable_changed(descriptor, e);
let candidate_score = score_director.calculate_score();
if best_score.is_none_or(|b| candidate_score > b) {
best_score = Some(candidate_score);
best_entity = e;
best_pos = pos;
}
score_director.before_variable_changed(descriptor, e);
list_remove(score_director.working_solution_mut(), e, pos);
score_director.after_variable_changed(descriptor, e);
}
}
score_director.before_variable_changed(descriptor, best_entity);
list_insert(
score_director.working_solution_mut(),
best_entity,
best_pos,
elem.clone(),
);
score_director.after_variable_changed(descriptor, best_entity);
placements.push((best_entity, best_pos));
}
let orig_entity = src;
let orig_indices: SmallVec<[usize; 8]> = self.element_indices.clone();
score_director.register_undo(Box::new(move |s: &mut S| {
let n = placements.len();
let mut current_pos: SmallVec<[usize; 8]> = SmallVec::with_capacity(n);
for i in 0..n {
let (e_i, p_i) = placements[i];
let shifted = placements[i + 1..]
.iter()
.filter(|&&(ej, pj)| ej == e_i && pj <= p_i)
.count();
current_pos.push(p_i + shifted);
}
let mut vals: SmallVec<[V; 8]> = SmallVec::with_capacity(n);
for i in (0..n).rev() {
let (e_i, _) = placements[i];
let left_shifts = placements[i + 1..]
.iter()
.zip(current_pos[i + 1..].iter())
.filter(|&(&(ej, _), &cpj)| ej == e_i && cpj < current_pos[i])
.count();
let actual_pos = current_pos[i] - left_shifts;
vals.push(list_remove(s, e_i, actual_pos));
}
vals.reverse();
for (&idx, val) in orig_indices.iter().zip(vals.into_iter()) {
list_insert(s, orig_entity, idx, val);
}
}));
}
fn descriptor_index(&self) -> usize {
self.descriptor_index
}
fn entity_indices(&self) -> &[usize] {
std::slice::from_ref(&self.entity_index)
}
fn variable_name(&self) -> &str {
self.variable_name
}
}