From a5c274d9b7b27419cd49ca8be64caf7b0112106a Mon Sep 17 00:00:00 2001 From: David Wang Date: Mon, 20 Feb 2017 08:16:41 +1100 Subject: [PATCH] Add puzzle search helper type. The puzzle search type is a puzzle with some variables assigned. It will be passed (mutably) to constraints for them to do their thing. All changes to a variables' candidates must go through this object so that we can wake up affected constraints. --- src/lib.rs | 1 + src/puzzle.rs | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 26b11b3..6a2ca4c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,7 @@ extern crate bit_set; use std::ops::Index; pub use puzzle::Puzzle; +pub use puzzle::PuzzleSearch; /// A puzzle variable token. #[derive(Copy,Clone,Debug,Eq,PartialEq)] diff --git a/src/puzzle.rs b/src/puzzle.rs index 50dd4ae..4da492c 100644 --- a/src/puzzle.rs +++ b/src/puzzle.rs @@ -1,6 +1,7 @@ //! The puzzle's state and rules. use std::collections::BTreeSet; +use std::ops::Index; use std::rc::Rc; use ::{Val,VarToken}; @@ -13,6 +14,14 @@ enum Candidates { Set(Rc>), // A variable with a list of candidates. } +/// The state of a variable during the solution search. +#[derive(Clone,Debug)] +#[allow(dead_code)] +enum VarState { + Assigned(Val), + Unassigned(Candidates), +} + /// The puzzle to be solved. pub struct Puzzle { // The number of variables in the puzzle. @@ -22,6 +31,15 @@ pub struct Puzzle { candidates: Vec, } +/// Intermediate puzzle search state. +#[derive(Clone)] +pub struct PuzzleSearch<'a> { + puzzle: &'a Puzzle, + vars: Vec, +} + +/*--------------------------------------------------------------*/ + impl Puzzle { /// Allocate a new puzzle. /// @@ -227,3 +245,80 @@ impl Puzzle { } } } + +/*--------------------------------------------------------------*/ + +impl<'a> PuzzleSearch<'a> { + /// Allocate a new puzzle searcher. + #[allow(dead_code)] + fn new(puzzle: &'a Puzzle) -> Self { + let mut vars = Vec::with_capacity(puzzle.num_vars); + for c in puzzle.candidates.iter() { + vars.push(VarState::Unassigned(c.clone())); + } + + PuzzleSearch { + puzzle: puzzle, + vars: vars, + } + } + + /// Check if the variable has been assigned to a value. + pub fn is_assigned(&self, var: VarToken) -> bool { + let VarToken(idx) = var; + match &self.vars[idx] { + &VarState::Assigned(_) => true, + &VarState::Unassigned(_) => false, + } + } + + /// Get the value assigned to a variable, or None. + /// + /// This should be used if the variable may potentially be + /// unassigned. For example, when implementing constraints. + pub fn get_assigned(&self, var: VarToken) -> Option { + let VarToken(idx) = var; + match &self.vars[idx] { + &VarState::Assigned(val) => Some(val), + &VarState::Unassigned(_) => None, + } + } + + /// Remove a single candidate from an unassigned variable. + pub fn remove_candidate(&mut self, var: VarToken, val: Val) { + let VarToken(idx) = var; + if let VarState::Unassigned(ref mut cs) = self.vars[idx] { + match cs { + &mut Candidates::None => return, + &mut Candidates::Value(v) => { + if v == val { + *cs = Candidates::None; + } + }, + &mut Candidates::Set(ref mut rc) => { + if rc.contains(&val) { + let mut set = Rc::make_mut(rc); + set.remove(&val); + } + }, + } + } + } +} + +impl<'a> Index for PuzzleSearch<'a> { + type Output = Val; + + /// Get the value assigned to a variable. + /// + /// # Panics + /// + /// Panics if the variable has not been assigned. + fn index(&self, var: VarToken) -> &Val { + let VarToken(idx) = var; + match &self.vars[idx] { + &VarState::Assigned(ref val) => val, + &VarState::Unassigned(_) => panic!("unassigned"), + } + } +}