Skip to main content

solverforge_scoring/director/
traits.rs

1// Score director trait definition.
2
3use solverforge_core::domain::{PlanningSolution, SolutionDescriptor};
4use solverforge_core::ConstraintRef;
5
6use crate::api::constraint_set::ConstraintMetadata;
7
8/// Snapshot of the director's committed score state.
9///
10/// Construction and local-search trial evaluation use this to restore the
11/// wrapped director after speculative scoring.
12#[derive(Debug, PartialEq, Eq)]
13pub struct DirectorScoreState<Sc> {
14    pub solution_score: Option<Sc>,
15    pub committed_score: Option<Sc>,
16    pub initialized: bool,
17}
18
19/* The score director manages solution state and score calculation.
20
21It is responsible for:
22- Maintaining the working solution
23- Calculating scores (incrementally when possible)
24- Notifying about variable changes for incremental updates
25- Providing access to solution metadata via descriptors
26*/
27pub trait Director<S: PlanningSolution>: Send {
28    // Returns a reference to the working solution.
29    fn working_solution(&self) -> &S;
30
31    // Returns a mutable reference to the working solution.
32    fn working_solution_mut(&mut self) -> &mut S;
33
34    // Calculates and returns the current score.
35    fn calculate_score(&mut self) -> S::Score;
36
37    // Returns the solution descriptor for this solution type.
38    fn solution_descriptor(&self) -> &SolutionDescriptor;
39
40    // Clones the working solution.
41    fn clone_working_solution(&self) -> S;
42
43    // Called before a planning variable is changed.
44    fn before_variable_changed(&mut self, descriptor_index: usize, entity_index: usize);
45
46    // Called after a planning variable is changed.
47    fn after_variable_changed(&mut self, descriptor_index: usize, entity_index: usize);
48
49    // Returns the number of entities for a given descriptor index.
50    fn entity_count(&self, descriptor_index: usize) -> Option<usize>;
51
52    // Returns the total number of entities across all collections.
53    fn total_entity_count(&self) -> Option<usize>;
54
55    // Returns immutable scoring-constraint metadata known to this director.
56    fn constraint_metadata(&self) -> Vec<ConstraintMetadata<'_>>;
57
58    // Returns whether a known constraint is hard.
59    fn constraint_is_hard(&self, constraint_ref: &ConstraintRef) -> Option<bool> {
60        let metadata = self.constraint_metadata();
61        metadata
62            .iter()
63            .find(|metadata| metadata.constraint_ref == constraint_ref)
64            .map(|metadata| metadata.is_hard)
65    }
66
67    // Returns true if this score director supports incremental scoring.
68    fn is_incremental(&self) -> bool {
69        false
70    }
71
72    // Snapshots the committed score state so speculative evaluation can roll
73    // back exactly.
74    fn snapshot_score_state(&self) -> DirectorScoreState<S::Score> {
75        let solution_score = self.working_solution().score();
76        DirectorScoreState {
77            solution_score,
78            committed_score: solution_score,
79            initialized: solution_score.is_some(),
80        }
81    }
82
83    // Restores a previously snapshotted committed score state.
84    fn restore_score_state(&mut self, state: DirectorScoreState<S::Score>) {
85        self.working_solution_mut().set_score(state.solution_score);
86    }
87
88    // Resets the score director state.
89    fn reset(&mut self) {}
90
91    /* Registers a typed undo closure.
92
93    Called by moves after applying changes to enable automatic undo.
94    The closure will be called in reverse order during `undo_changes()`.
95
96    Default implementation does nothing (for non-recording directors).
97    */
98    fn register_undo(&mut self, _undo: Box<dyn FnOnce(&mut S) + Send>) {
99        // Default: no-op - only RecordingDirector stores undo closures
100    }
101}