solverforge_solver/heuristic/move/
conflict_repair.rs1use std::fmt::{self, Debug};
2
3use solverforge_core::domain::PlanningSolution;
4use solverforge_scoring::Director;
5
6use crate::stats::CandidateTraceIdentity;
7
8use super::compound_scalar::{CompoundScalarEdit, CompoundScalarMove};
9use super::{Move, MoveAffectedEntity, MoveTabuSignature};
10
11const CONFLICT_REPAIR_VARIABLE: &str = "conflict_repair";
12
13#[derive(Clone)]
14pub struct ConflictRepairScalarEdit<S> {
15 pub descriptor_index: usize,
16 pub entity_index: usize,
17 pub variable_index: usize,
18 pub variable_name: &'static str,
19 pub to_value: Option<usize>,
20 pub getter: fn(&S, usize, usize) -> Option<usize>,
21 pub setter: fn(&mut S, usize, usize, Option<usize>),
22}
23
24impl<S> Debug for ConflictRepairScalarEdit<S> {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 f.debug_struct("ConflictRepairScalarEdit")
27 .field("descriptor_index", &self.descriptor_index)
28 .field("entity_index", &self.entity_index)
29 .field("variable_index", &self.variable_index)
30 .field("variable_name", &self.variable_name)
31 .field("to_value", &self.to_value)
32 .finish()
33 }
34}
35
36#[derive(Clone)]
37pub struct ConflictRepairMove<S> {
38 compound: CompoundScalarMove<S>,
39}
40
41impl<S> ConflictRepairMove<S> {
42 pub fn new(reason: &'static str, edits: Vec<ConflictRepairScalarEdit<S>>) -> Self {
43 let edits = edits
44 .into_iter()
45 .map(|edit| {
46 CompoundScalarEdit::static_edit(
47 edit.descriptor_index,
48 edit.entity_index,
49 edit.variable_index,
50 edit.variable_name,
51 edit.to_value,
52 edit.getter,
53 edit.setter,
54 None,
55 )
56 })
57 .collect();
58 Self {
59 compound: CompoundScalarMove::with_label(reason, CONFLICT_REPAIR_VARIABLE, edits),
60 }
61 }
62}
63
64impl<S> Debug for ConflictRepairMove<S> {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 f.debug_tuple("ConflictRepairMove")
67 .field(&self.compound)
68 .finish()
69 }
70}
71
72impl<S> Move<S> for ConflictRepairMove<S>
73where
74 S: PlanningSolution,
75{
76 type Undo = <CompoundScalarMove<S> as Move<S>>::Undo;
77
78 fn is_doable<D: Director<S>>(&self, score_director: &D) -> bool {
79 self.compound.is_doable(score_director)
80 }
81
82 fn do_move<D: Director<S>>(&self, score_director: &mut D) -> Self::Undo {
83 self.compound.do_move(score_director)
84 }
85
86 fn undo_move<D: Director<S>>(&self, score_director: &mut D, undo: Self::Undo) {
87 self.compound.undo_move(score_director, undo);
88 }
89
90 fn descriptor_index(&self) -> usize {
91 self.compound.descriptor_index()
92 }
93
94 fn entity_indices(&self) -> &[usize] {
95 self.compound.entity_indices()
96 }
97
98 fn variable_name(&self) -> &str {
99 self.compound.variable_name()
100 }
101
102 fn telemetry_label(&self) -> &'static str {
103 self.compound.reason()
104 }
105
106 fn tabu_signature<D: Director<S>>(&self, score_director: &D) -> MoveTabuSignature {
107 self.compound.tabu_signature(score_director)
108 }
109
110 fn candidate_trace_identity(&self) -> Option<CandidateTraceIdentity> {
111 self.compound.candidate_trace_identity()
112 }
113
114 fn for_each_affected_entity(&self, visitor: &mut dyn FnMut(MoveAffectedEntity<'_>)) {
115 self.compound.for_each_affected_entity(visitor);
116 }
117}