solverforge-solver 0.15.2

Solver engine for SolverForge
Documentation
#[test]
fn dynamic_scalar_notifications_use_resolved_descriptor_index() {
    let descriptor = reversed_dynamic_descriptor();
    let scalar = DynamicScalarVariableSlot::new(
        EntityClassId(0),
        VariableId(0),
        "Task",
        "worker",
        false,
    )
    .resolved_against(&descriptor)
    .expect("dynamic scalar slot should resolve against logical IDs");
    assert_eq!(scalar.descriptor_index(), 1);

    let plan = DynamicPlan {
        score: None,
        scalar_values: vec![Some(0)],
        scalar_candidates: vec![vec![0, 1]],
        lists: Vec::new(),
        list_element_count: 0,
    };
    let mut director = ScoreDirector::with_descriptor(
        plan,
        PreferWorkerOne::for_descriptor_index(1),
        descriptor,
        |solution, descriptor_index| match descriptor_index {
            0 => solution.lists.len(),
            1 => solution.scalar_values.len(),
            _ => 0,
        },
    );
    assert_eq!(director.calculate_score(), SoftScore::of(-10));

    let mov = crate::heuristic::r#move::DynamicScalarChangeMove::new(scalar, 0, Some(1));
    crate::heuristic::r#move::Move::do_move(&mov, &mut director);

    let score = director.calculate_score();
    assert_eq!(score, SoftScore::of(0));
    assert_eq!(Director::fresh_score(&director), Some(score));
}

#[test]
fn dynamic_list_notifications_use_resolved_descriptor_index() {
    let descriptor = reversed_dynamic_descriptor();
    let list = DynamicListVariableSlot::new(EntityClassId(1), VariableId(0), "Vehicle", "visits")
        .resolved_against(&descriptor)
        .expect("dynamic list slot should resolve against logical IDs");
    assert_eq!(list.descriptor_index(), 0);

    let plan = DynamicPlan {
        score: None,
        scalar_values: Vec::new(),
        scalar_candidates: Vec::new(),
        lists: vec![vec![1, 0]],
        list_element_count: 2,
    };
    let mut director = ScoreDirector::with_descriptor(
        plan,
        PreferOrderedVisits::for_descriptor_index(0),
        descriptor,
        |solution, descriptor_index| match descriptor_index {
            0 => solution.lists.len(),
            1 => solution.scalar_values.len(),
            _ => 0,
        },
    );
    assert_eq!(director.calculate_score(), SoftScore::of(-10));

    let mov = crate::heuristic::r#move::DynamicListChangeMove::new(list, 0, 1, 0, 0);
    crate::heuristic::r#move::Move::do_move(&mov, &mut director);

    let score = director.calculate_score();
    assert_eq!(score, SoftScore::of(0));
    assert_eq!(Director::fresh_score(&director), Some(score));
}

#[test]
fn dynamic_list_change_allows_intra_list_tail_destination() {
    use crate::{Move, MoveSelector};

    let descriptor = dynamic_descriptor();
    let list = DynamicListVariableSlot::new(EntityClassId(1), VariableId(0), "Vehicle", "visits")
        .resolved_against(&descriptor)
        .expect("dynamic list slot should resolve against logical IDs");
    let plan = DynamicPlan {
        score: None,
        scalar_values: Vec::new(),
        scalar_candidates: Vec::new(),
        lists: vec![vec![0, 1, 2]],
        list_element_count: 3,
    };
    let mut director = dynamic_director(plan);

    let mov = crate::heuristic::r#move::DynamicListChangeMove::new(list.clone(), 0, 0, 0, 3);
    assert!(mov.is_doable(&director));
    mov.do_move(&mut director);
    assert_eq!(director.working_solution().lists, vec![vec![1, 2, 0]]);

    let selector = crate::DynamicListChangeMoveSelector::new(list);
    let director = dynamic_director(DynamicPlan {
        score: None,
        scalar_values: Vec::new(),
        scalar_candidates: Vec::new(),
        lists: vec![vec![0, 1, 2]],
        list_element_count: 3,
    });
    let moves = selector
        .iter_moves(&director)
        .map(|mov| {
            (
                mov.source_entity_index(),
                mov.source_position(),
                mov.dest_entity_index(),
                mov.dest_position(),
            )
        })
        .collect::<Vec<_>>();

    assert!(moves.contains(&(0, 0, 0, 3)));
    assert_eq!(selector.size(&director), moves.len());
    for mov in selector.iter_moves(&director) {
        assert!(mov.is_doable(&director), "generated move should be doable: {mov:?}");
    }
}