solverforge-solver 0.18.0

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

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

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 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_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: &crate::builder::ScalarGroupMemberBinding<S>,
    group_slot: &ConstructionGroupSlotId,
    mov: &CompoundScalarMove<S>,
) -> ConstructionTarget {
    let scalar_slots = mov
        .edits()
        .iter()
        .filter(|edit| {
            edit.descriptor_index == target.descriptor_index
                && edit.variable_index == target.variable_index
        })
        .map(|edit| ConstructionSlotId::new(target.construction_binding_index(), edit.entity_index))
        .collect::<Vec<_>>();
    ConstructionTarget::new()
        .with_scalar_slots(scalar_slots)
        .with_group_slot(group_slot.clone())
}

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)
}