solverforge-solver 0.13.0

Solver engine for SolverForge
Documentation
use solverforge_core::domain::PlanningSolution;

use crate::builder::context::{ScalarAssignmentBinding, ScalarCandidate, ScalarGroupBinding};
use crate::descriptor::ResolvedVariableBinding;
use crate::heuristic::r#move::{CompoundScalarEdit, CompoundScalarMove};
use crate::heuristic::selector::EntityReference;
use crate::phase::construction::{
    ConstructionGroupSlotId, ConstructionGroupSlotKey, ConstructionSlotId, ConstructionTarget,
    Placement,
};

pub(super) fn scalar_slots_for_candidate<S>(
    group: &ScalarGroupBinding<S>,
    scalar_bindings: &[ResolvedVariableBinding<S>],
    solution: &S,
    candidate: &ScalarCandidate<S>,
) -> Option<(Vec<ConstructionSlotId>, bool, bool)>
where
    S: PlanningSolution,
{
    let mut targets = std::collections::HashSet::new();
    let mut scalar_slots = Vec::with_capacity(candidate.edits().len());
    let mut keep_current_legal = true;
    let mut has_unfinished_unassigned_slot = false;

    for edit in candidate.edits() {
        if !targets.insert((
            edit.descriptor_index(),
            edit.entity_index(),
            edit.variable_name(),
        )) {
            return None;
        }
        let member = group.member_for_edit(edit)?;
        if !member.value_is_legal(solution, edit.entity_index(), edit.to_value()) {
            return None;
        }
        let binding = scalar_bindings.iter().find(|binding| {
            binding.descriptor_index == member.descriptor_index
                && binding.variable_index == member.variable_index
        })?;
        let slot = binding.slot_id(edit.entity_index());
        if member
            .current_value(solution, edit.entity_index())
            .is_none()
        {
            has_unfinished_unassigned_slot = true;
        }
        keep_current_legal &= member.allows_unassigned;
        scalar_slots.push(slot);
    }

    scalar_slots.sort_unstable();
    scalar_slots.dedup();
    Some((
        scalar_slots,
        keep_current_legal,
        has_unfinished_unassigned_slot,
    ))
}

pub(super) fn placement_for_group_candidate<S>(
    sequence: usize,
    candidate: &ScalarCandidate<S>,
    group_slot: ConstructionGroupSlotId,
    scalar_slots: Vec<ConstructionSlotId>,
    keep_current_legal: bool,
    mov: CompoundScalarMove<S>,
) -> Placement<S, CompoundScalarMove<S>>
where
    S: PlanningSolution,
{
    let entity_ref = candidate
        .edits()
        .first()
        .map(|edit| EntityReference::new(edit.descriptor_index(), edit.entity_index()))
        .unwrap_or_else(|| EntityReference::new(0, sequence));
    Placement::new(entity_ref, vec![mov])
        .with_group_slot(group_slot)
        .with_scalar_slots(scalar_slots)
        .with_keep_current_legal(keep_current_legal)
}

pub(super) fn push_or_merge_placement<S>(
    placements: &mut Vec<Placement<S, CompoundScalarMove<S>>>,
    mut placement: Placement<S, CompoundScalarMove<S>>,
) where
    S: PlanningSolution,
{
    if let Some(existing) = placements.iter_mut().find(|existing| {
        existing.group_slot() == placement.group_slot()
            && existing.scalar_slots() == placement.scalar_slots()
    }) {
        existing.moves.append(&mut placement.moves);
        return;
    }
    placements.push(placement);
}

pub(super) fn group_slot_id<S>(
    group_index: usize,
    candidate: &ScalarCandidate<S>,
    scalar_slots: &[ConstructionSlotId],
) -> ConstructionGroupSlotId {
    let key = candidate
        .construction_slot_key()
        .map(ConstructionGroupSlotKey::Explicit)
        .unwrap_or_else(|| ConstructionGroupSlotKey::Targets(scalar_slots.to_vec()));
    ConstructionGroupSlotId::new(group_index, key)
}

pub(super) fn assignment_target_binding<'a, S>(
    assignment: &ScalarAssignmentBinding<S>,
    scalar_bindings: &'a [ResolvedVariableBinding<S>],
) -> Option<&'a ResolvedVariableBinding<S>> {
    scalar_bindings.iter().find(|binding| {
        binding.descriptor_index == assignment.target.descriptor_index
            && binding.variable_index == assignment.target.variable_index
    })
}

pub(super) fn assignment_group_slot(
    group_index: usize,
    entity_index: usize,
) -> ConstructionGroupSlotId {
    ConstructionGroupSlotId::new(
        group_index,
        ConstructionGroupSlotKey::Explicit(entity_index),
    )
}

pub(super) fn principal_assignment_edit<S>(
    mov: &CompoundScalarMove<S>,
    entity_index: usize,
) -> Option<&CompoundScalarEdit<S>> {
    mov.edits()
        .iter()
        .find(|edit| edit.entity_index == entity_index && edit.to_value.is_some())
}

pub(super) fn assignment_move_target<S>(
    target_binding: &ResolvedVariableBinding<S>,
    group_slot: &ConstructionGroupSlotId,
    mov: &CompoundScalarMove<S>,
) -> ConstructionTarget {
    let scalar_slots = mov
        .edits()
        .iter()
        .filter(|edit| {
            edit.descriptor_index == target_binding.descriptor_index
                && edit.variable_index == target_binding.variable_index
        })
        .map(|edit| target_binding.slot_id(edit.entity_index))
        .collect::<Vec<_>>();
    ConstructionTarget::new()
        .with_scalar_slots(scalar_slots)
        .with_group_slot(group_slot.clone())
}

pub(super) fn assignment_move_touches_completed_slot<S, IsCompleted>(
    mov: &CompoundScalarMove<S>,
    target_binding: &ResolvedVariableBinding<S>,
    descriptor_index: usize,
    is_completed: &mut IsCompleted,
) -> bool
where
    S: PlanningSolution,
    IsCompleted: FnMut(&Placement<S, CompoundScalarMove<S>>) -> bool,
{
    mov.edits().iter().any(|edit| {
        let slot_id = target_binding.slot_id(edit.entity_index);
        let placement = Placement::new(
            EntityReference::new(descriptor_index, edit.entity_index),
            Vec::new(),
        )
        .with_scalar_slots(vec![slot_id]);
        is_completed(&placement)
    })
}

pub(super) fn placement_entity_order_key<S>(
    placement: &Placement<S, CompoundScalarMove<S>>,
) -> Option<i64>
where
    S: PlanningSolution,
{
    placement.construction_entity_order_key()
}

pub(super) fn placement_sequence<S>(placement: &Placement<S, CompoundScalarMove<S>>) -> usize
where
    S: PlanningSolution,
{
    placement.entity_ref.entity_index
}

pub(crate) fn scalar_group_move_strength<S>(mov: &CompoundScalarMove<S>, _solution: &S) -> i64
where
    S: PlanningSolution,
{
    mov.construction_value_order_key().unwrap_or(0)
}

pub(super) fn never_completed<S>(_placement: &Placement<S, CompoundScalarMove<S>>) -> bool
where
    S: PlanningSolution,
{
    false
}