Refactored variable constructors
This commit is contained in:
parent
dc20c9aa76
commit
656890c1be
@ -17,7 +17,7 @@ impl AllDifferent {
|
||||
///
|
||||
/// ```
|
||||
/// let mut send_more_money = puzzle_solver::Puzzle::new();
|
||||
/// let vars = send_more_money.new_vars_with_candidates_1d(8,
|
||||
/// let vars = send_more_money.new_vars(8,
|
||||
/// &[0,1,2,3,4,5,6,7,8,9]);
|
||||
///
|
||||
/// puzzle_solver::constraint::AllDifferent::new(&vars);
|
||||
@ -103,9 +103,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_contradiction() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let v0 = puzzle.new_var_with_candidates(&[1]);
|
||||
let v1 = puzzle.new_var_with_candidates(&[1]);
|
||||
let v2 = puzzle.new_var_with_candidates(&[1, 2, 3]);
|
||||
let v0 = puzzle.new_var(&[1]);
|
||||
let v1 = puzzle.new_var(&[1]);
|
||||
let v2 = puzzle.new_var(&[1, 2, 3]);
|
||||
|
||||
puzzle.all_different(&[v0, v1, v2]);
|
||||
|
||||
@ -116,9 +116,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_elimination() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let v0 = puzzle.new_var_with_candidates(&[1]);
|
||||
let v1 = puzzle.new_var_with_candidates(&[1, 2, 3]);
|
||||
let v2 = puzzle.new_var_with_candidates(&[1, 2, 3]);
|
||||
let v0 = puzzle.new_var(&[1]);
|
||||
let v1 = puzzle.new_var(&[1, 2, 3]);
|
||||
let v2 = puzzle.new_var(&[1, 2, 3]);
|
||||
|
||||
puzzle.all_different(&[v0, v1, v2]);
|
||||
|
||||
@ -131,9 +131,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_contradiction_by_length() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let v0 = puzzle.new_var_with_candidates(&[1, 2]);
|
||||
let v1 = puzzle.new_var_with_candidates(&[1, 2]);
|
||||
let v2 = puzzle.new_var_with_candidates(&[1, 2]);
|
||||
let v0 = puzzle.new_var(&[1, 2]);
|
||||
let v1 = puzzle.new_var(&[1, 2]);
|
||||
let v2 = puzzle.new_var(&[1, 2]);
|
||||
|
||||
puzzle.all_different(&[v0, v1, v2]);
|
||||
|
||||
@ -144,9 +144,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_constrain_by_value() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let v0 = puzzle.new_var_with_candidates(&[1, 2]);
|
||||
let v1 = puzzle.new_var_with_candidates(&[1, 2]);
|
||||
let v2 = puzzle.new_var_with_candidates(&[1, 2, 3]);
|
||||
let v0 = puzzle.new_var(&[1, 2]);
|
||||
let v1 = puzzle.new_var(&[1, 2]);
|
||||
let v2 = puzzle.new_var(&[1, 2, 3]);
|
||||
|
||||
puzzle.all_different(&[v0, v1, v2]);
|
||||
|
||||
|
@ -19,7 +19,7 @@ impl Equality {
|
||||
///
|
||||
/// ```
|
||||
/// let mut magic_square = puzzle_solver::Puzzle::new();
|
||||
/// let vars = magic_square.new_vars_with_candidates_2d(3, 3,
|
||||
/// let vars = magic_square.new_vars_2d(3, 3,
|
||||
/// &[1,2,3,4,5,6,7,8,9]);
|
||||
///
|
||||
/// puzzle_solver::constraint::Equality::new(
|
||||
@ -157,8 +157,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_contradiction() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let v0 = puzzle.new_var_with_candidates(&[3]);
|
||||
let v1 = puzzle.new_var_with_candidates(&[0, 1]);
|
||||
let v0 = puzzle.new_var(&[3]);
|
||||
let v1 = puzzle.new_var(&[0, 1]);
|
||||
|
||||
puzzle.equals(v0 + 2 * v1, 4);
|
||||
|
||||
@ -169,8 +169,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_assign() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let v0 = puzzle.new_var_with_candidates(&[1]);
|
||||
let v1 = puzzle.new_var_with_candidates(&[1, 2, 3]);
|
||||
let v0 = puzzle.new_var(&[1]);
|
||||
let v1 = puzzle.new_var(&[1, 2, 3]);
|
||||
|
||||
puzzle.equals(v0 + v1, 4);
|
||||
|
||||
@ -182,8 +182,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_reduce_range() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let v0 = puzzle.new_var_with_candidates(&[1, 2, 3]);
|
||||
let v1 = puzzle.new_var_with_candidates(&[3, 4, 5]);
|
||||
let v0 = puzzle.new_var(&[1, 2, 3]);
|
||||
let v1 = puzzle.new_var(&[3, 4, 5]);
|
||||
|
||||
puzzle.equals(v0 + v1, 5);
|
||||
|
||||
|
@ -18,8 +18,8 @@ impl Unify {
|
||||
///
|
||||
/// ```
|
||||
/// let mut send_more_money = puzzle_solver::Puzzle::new();
|
||||
/// let carry = send_more_money.new_vars_with_candidates_1d(4, &[0,1]);
|
||||
/// let vars = send_more_money.new_vars_with_candidates_1d(8,
|
||||
/// let carry = send_more_money.new_vars(4, &[0,1]);
|
||||
/// let vars = send_more_money.new_vars(8,
|
||||
/// &[0,1,2,3,4,5,6,7,8,9]);
|
||||
///
|
||||
/// let m = vars[4];
|
||||
@ -65,8 +65,8 @@ mod tests {
|
||||
#[test]
|
||||
fn test_unify_alldifferent() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let v0 = puzzle.new_var_with_candidates(&[1, 2]);
|
||||
let v1 = puzzle.new_var_with_candidates(&[1, 2]);
|
||||
let v0 = puzzle.new_var(&[1, 2]);
|
||||
let v1 = puzzle.new_var(&[1, 2]);
|
||||
|
||||
puzzle.all_different(&[v0, v1]);
|
||||
puzzle.unify(v0, v1);
|
||||
@ -78,9 +78,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_unify_equality() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let v0 = puzzle.new_var_with_candidates(&[1, 2, 3, 4]);
|
||||
let v1 = puzzle.new_var_with_candidates(&[1, 2, 3, 4]);
|
||||
let v2 = puzzle.new_var_with_candidates(&[1, 2, 3, 4]);
|
||||
let v0 = puzzle.new_var(&[1, 2, 3, 4]);
|
||||
let v1 = puzzle.new_var(&[1, 2, 3, 4]);
|
||||
let v2 = puzzle.new_var(&[1, 2, 3, 4]);
|
||||
|
||||
puzzle.equals(v0 + 2 * v1 + v2, 6);
|
||||
puzzle.unify(v0, v1);
|
||||
@ -94,9 +94,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_unify_unify() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let v0 = puzzle.new_var_with_candidates(&[1]);
|
||||
let v1 = puzzle.new_var_with_candidates(&[1, 2, 3, 4]);
|
||||
let v2 = puzzle.new_var_with_candidates(&[1, 2, 3, 4]);
|
||||
let v0 = puzzle.new_var(&[1]);
|
||||
let v1 = puzzle.new_var(&[1, 2, 3, 4]);
|
||||
let v2 = puzzle.new_var(&[1, 2, 3, 4]);
|
||||
|
||||
puzzle.unify(v0, v1);
|
||||
puzzle.unify(v1, v2);
|
||||
|
@ -279,8 +279,8 @@ mod tests {
|
||||
#[allow(clippy::identity_op, clippy::eq_op)]
|
||||
fn test_ops() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let x = puzzle.new_var_with_range(10..=10);
|
||||
let y = puzzle.new_var_with_range(10..=10);
|
||||
let x = puzzle.new_var(10..=10);
|
||||
let y = puzzle.new_var(10..=10);
|
||||
|
||||
// expr = var + const;
|
||||
let _ = x + 1;
|
||||
@ -337,8 +337,8 @@ mod tests {
|
||||
#[allow(clippy::erasing_op, clippy::eq_op)]
|
||||
fn test_coef_zero() {
|
||||
let mut puzzle = Puzzle::new();
|
||||
let x = puzzle.new_var_with_range(10..10);
|
||||
let y = puzzle.new_var_with_range(10..10);
|
||||
let x = puzzle.new_var(10..10);
|
||||
let y = puzzle.new_var(10..10);
|
||||
|
||||
let expr = x * 0;
|
||||
assert_eq!(expr.coef.len(), 0);
|
||||
|
196
src/puzzle.rs
196
src/puzzle.rs
@ -7,7 +7,9 @@ use std::fmt;
|
||||
use std::iter;
|
||||
use std::mem;
|
||||
use std::ops;
|
||||
use std::ops::Bound;
|
||||
use std::ops::RangeBounds;
|
||||
use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::constraint;
|
||||
@ -18,12 +20,84 @@ use crate::{Constraint, PsResult, Solution, Val, VarToken};
|
||||
|
||||
/// A collection of candidates.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
enum Candidates {
|
||||
pub enum Candidates {
|
||||
Value(Val), // A variable set to its initial value.
|
||||
Set(Rc<BTreeSet<Val>>), // A variable with a list of candidates.
|
||||
Range(Ranges), // A variable with candidate ranges.
|
||||
}
|
||||
|
||||
impl From<Val> for Candidates {
|
||||
fn from(val: Val) -> Self {
|
||||
Candidates::Value(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[Val]> for Candidates {
|
||||
fn from(set: &[Val]) -> Self {
|
||||
Candidates::Set(Rc::new(set.iter().copied().collect()))
|
||||
}
|
||||
}
|
||||
|
||||
impl <const N: usize> From<&[Val; N]> for Candidates {
|
||||
fn from(set: &[Val; N]) -> Self {
|
||||
Candidates::Set(Rc::new(set.iter().copied().collect()))
|
||||
}
|
||||
}
|
||||
|
||||
impl <const N: usize> From<[Val; N]> for Candidates {
|
||||
fn from(set: [Val; N]) -> Self {
|
||||
Candidates::Set(Rc::new(set.into_iter().collect()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Val>> for Candidates {
|
||||
fn from(set: Vec<Val>) -> Self {
|
||||
Candidates::Set(Rc::new(set.into_iter().collect()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Range<Val>> for Candidates {
|
||||
fn from(range: Range<Val>) -> Self {
|
||||
Candidates::Range(range.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeFrom<Val>> for Candidates {
|
||||
fn from(range: RangeFrom<Val>) -> Self {
|
||||
Candidates::Range(range.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeTo<Val>> for Candidates {
|
||||
fn from(range: RangeTo<Val>) -> Self {
|
||||
Candidates::Range(range.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeInclusive<Val>> for Candidates {
|
||||
fn from(range: RangeInclusive<Val>) -> Self {
|
||||
Candidates::Range(range.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeToInclusive<Val>> for Candidates {
|
||||
fn from(range: RangeToInclusive<Val>) -> Self {
|
||||
Candidates::Range(range.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeFull> for Candidates {
|
||||
fn from(range: RangeFull) -> Self {
|
||||
Candidates::Range(range.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Bound<Val>, Bound<Val>)> for Candidates {
|
||||
fn from(range: (Bound<Val>, Bound<Val>)) -> Self {
|
||||
Candidates::Range(range.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of a variable during the solution search.
|
||||
#[derive(Clone, Debug)]
|
||||
enum VarState {
|
||||
@ -110,44 +184,13 @@ impl Puzzle {
|
||||
}
|
||||
}
|
||||
|
||||
fn new_var(&mut self, candidates: Candidates) -> VarToken {
|
||||
pub fn new_var<C: Into<Candidates>>(&mut self, candidates: C) -> VarToken {
|
||||
let var = VarToken(self.num_vars);
|
||||
self.num_vars += 1;
|
||||
self.candidates.push(candidates);
|
||||
self.candidates.push(candidates.into());
|
||||
var
|
||||
}
|
||||
|
||||
/// Allocate a new puzzle variable, initialising it with potential
|
||||
/// candidates.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let mut send_more_money = puzzle_solver::Puzzle::new();
|
||||
/// send_more_money.new_var_with_candidates(&[0,1,2,3,4,5,6,7,8,9]);
|
||||
/// ```
|
||||
pub fn new_var_with_candidates(&mut self, candidates: &[Val]) -> VarToken {
|
||||
self.new_var(Candidates::Set(Rc::new(BTreeSet::from_iter(
|
||||
candidates.iter().copied(),
|
||||
))))
|
||||
}
|
||||
|
||||
/// Allocate a new puzzle variable, initialising it with potential
|
||||
/// candidates.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let mut send_more_money = puzzle_solver::Puzzle::new();
|
||||
/// send_more_money.new_var_with_candidates(&[0,1,2,3,4,5,6,7,8,9]);
|
||||
/// ```
|
||||
pub fn new_var_with_range<R: RangeBounds<Val>>(&mut self, range: R) -> VarToken {
|
||||
self.new_var(Candidates::Range(Ranges::new(
|
||||
range.start_bound().cloned(),
|
||||
range.end_bound().cloned(),
|
||||
)))
|
||||
}
|
||||
|
||||
/// Allocate a 1d vector of puzzle variables, each initialised to
|
||||
/// have the same set of potential candidates.
|
||||
///
|
||||
@ -155,33 +198,12 @@ impl Puzzle {
|
||||
///
|
||||
/// ```
|
||||
/// let mut send_more_money = puzzle_solver::Puzzle::new();
|
||||
/// send_more_money.new_vars_with_candidates_1d(8, &[0,1,2,3,4,5,6,7,8,9]);
|
||||
/// send_more_money.new_vars(8, &[0,1,2,3,4,5,6,7,8,9]);
|
||||
/// ```
|
||||
pub fn new_vars_with_candidates_1d(&mut self, n: usize, candidates: &[Val]) -> Vec<VarToken> {
|
||||
pub fn new_vars<C: Into<Candidates> + Clone>(&mut self, n: usize, candidates: C) -> Vec<VarToken> {
|
||||
let mut vars = Vec::with_capacity(n);
|
||||
for _ in 0..n {
|
||||
vars.push(self.new_var_with_candidates(candidates));
|
||||
}
|
||||
vars
|
||||
}
|
||||
|
||||
/// Allocate a 1d vector of puzzle variables, each initialised to
|
||||
/// have the same set of potential candidates.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let mut send_more_money = puzzle_solver::Puzzle::new();
|
||||
/// send_more_money.new_vars_with_candidates_1d(8, &[0,1,2,3,4,5,6,7,8,9]);
|
||||
/// ```
|
||||
pub fn new_vars_with_range_1d<R: RangeBounds<Val> + Clone>(
|
||||
&mut self,
|
||||
n: usize,
|
||||
range: R,
|
||||
) -> Vec<VarToken> {
|
||||
let mut vars = Vec::with_capacity(n);
|
||||
for _ in 0..n {
|
||||
vars.push(self.new_var_with_range(range.clone()));
|
||||
vars.push(self.new_var(candidates.clone()));
|
||||
}
|
||||
vars
|
||||
}
|
||||
@ -193,39 +215,17 @@ impl Puzzle {
|
||||
///
|
||||
/// ```
|
||||
/// let mut magic_square = puzzle_solver::Puzzle::new();
|
||||
/// magic_square.new_vars_with_candidates_2d(3, 3, &[1,2,3,4,5,6,7,8,9]);
|
||||
/// magic_square.new_vars_2d(3, 3, &[1,2,3,4,5,6,7,8,9]);
|
||||
/// ```
|
||||
pub fn new_vars_with_candidates_2d(
|
||||
pub fn new_vars_2d<C: Into<Candidates> + Clone>(
|
||||
self: &mut Puzzle,
|
||||
width: usize,
|
||||
height: usize,
|
||||
candidates: &[Val],
|
||||
candidates: C,
|
||||
) -> Vec<Vec<VarToken>> {
|
||||
let mut vars = Vec::with_capacity(height);
|
||||
for _ in 0..height {
|
||||
vars.push(self.new_vars_with_candidates_1d(width, candidates));
|
||||
}
|
||||
vars
|
||||
}
|
||||
|
||||
/// Allocate a 2d array of puzzle variables, each initialised to
|
||||
/// have the same set of potential candidates.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let mut magic_square = puzzle_solver::Puzzle::new();
|
||||
/// magic_square.new_vars_with_candidates_2d(3, 3, &[1,2,3,4,5,6,7,8,9]);
|
||||
/// ```
|
||||
pub fn new_vars_with_range_2d<R: RangeBounds<Val> + Clone>(
|
||||
self: &mut Puzzle,
|
||||
width: usize,
|
||||
height: usize,
|
||||
range: R,
|
||||
) -> Vec<Vec<VarToken>> {
|
||||
let mut vars = Vec::with_capacity(height);
|
||||
for _ in 0..height {
|
||||
vars.push(self.new_vars_with_range_1d(width, range.clone()));
|
||||
vars.push(self.new_vars(width, candidates.clone()));
|
||||
}
|
||||
vars
|
||||
}
|
||||
@ -240,7 +240,7 @@ impl Puzzle {
|
||||
///
|
||||
/// ```
|
||||
/// let mut magic_square = puzzle_solver::Puzzle::new();
|
||||
/// let vars = magic_square.new_vars_with_candidates_2d(3, 3,
|
||||
/// let vars = magic_square.new_vars_2d(3, 3,
|
||||
/// &[1,2,3,4,5,6,7,8,9]);
|
||||
///
|
||||
/// magic_square.set_value(vars[1][1], 5);
|
||||
@ -264,7 +264,7 @@ impl Puzzle {
|
||||
/// ```
|
||||
/// let mut send_more_money = puzzle_solver::Puzzle::new();
|
||||
/// for _ in 0..9 {
|
||||
/// let var = send_more_money.new_var();
|
||||
/// let var = send_more_money.new_var([]);
|
||||
/// send_more_money.insert_candidates(var, &[0,1,2,3,4,5,6,7,8,9]);
|
||||
/// }
|
||||
/// ```
|
||||
@ -289,7 +289,7 @@ impl Puzzle {
|
||||
///
|
||||
/// ```
|
||||
/// let mut send_more_money = puzzle_solver::Puzzle::new();
|
||||
/// let vars = send_more_money.new_vars_with_candidates_1d(8,
|
||||
/// let vars = send_more_money.new_vars(8,
|
||||
/// &[0,1,2,3,4,5,6,7,8,9]);
|
||||
///
|
||||
/// let s = vars[0];
|
||||
@ -319,7 +319,7 @@ impl Puzzle {
|
||||
///
|
||||
/// ```
|
||||
/// let mut send_more_money = puzzle_solver::Puzzle::new();
|
||||
/// let vars = send_more_money.new_vars_with_candidates_1d(8,
|
||||
/// let vars = send_more_money.new_vars(8,
|
||||
/// &[0,1,2,3,4,5,6,7,8,9]);
|
||||
///
|
||||
/// let m = vars[4];
|
||||
@ -370,7 +370,7 @@ impl Puzzle {
|
||||
///
|
||||
/// ```
|
||||
/// let mut send_more_money = puzzle_solver::Puzzle::new();
|
||||
/// let vars = send_more_money.new_vars_with_candidates_1d(8,
|
||||
/// let vars = send_more_money.new_vars(8,
|
||||
/// &[0,1,2,3,4,5,6,7,8,9]);
|
||||
///
|
||||
/// send_more_money.all_different(&vars);
|
||||
@ -388,7 +388,7 @@ impl Puzzle {
|
||||
///
|
||||
/// ```
|
||||
/// let mut magic_square = puzzle_solver::Puzzle::new();
|
||||
/// let vars = magic_square.new_vars_with_candidates_2d(3, 3,
|
||||
/// let vars = magic_square.new_vars_2d(3, 3,
|
||||
/// &[1,2,3,4,5,6,7,8,9]);
|
||||
///
|
||||
/// magic_square.equals(vars[0][0] + vars[0][1] + vars[0][2], 15);
|
||||
@ -407,8 +407,8 @@ impl Puzzle {
|
||||
///
|
||||
/// ```
|
||||
/// let mut send_more_money = puzzle_solver::Puzzle::new();
|
||||
/// let carry = send_more_money.new_vars_with_candidates_1d(4, &[0,1]);
|
||||
/// let vars = send_more_money.new_vars_with_candidates_1d(8,
|
||||
/// let carry = send_more_money.new_vars(4, &[0,1]);
|
||||
/// let vars = send_more_money.new_vars(8,
|
||||
/// &[0,1,2,3,4,5,6,7,8,9]);
|
||||
///
|
||||
/// let m = vars[4];
|
||||
@ -424,8 +424,8 @@ impl Puzzle {
|
||||
///
|
||||
/// ```
|
||||
/// let mut puzzle = puzzle_solver::Puzzle::new();
|
||||
/// puzzle.new_var_with_candidates(&[1,2]);
|
||||
/// puzzle.new_var_with_candidates(&[3,4]);
|
||||
/// puzzle.new_var(&[1,2]);
|
||||
/// puzzle.new_var(&[3,4]);
|
||||
///
|
||||
/// let solution = puzzle.solve_any();
|
||||
/// assert!(solution.is_some());
|
||||
@ -449,8 +449,8 @@ impl Puzzle {
|
||||
///
|
||||
/// ```
|
||||
/// let mut puzzle = puzzle_solver::Puzzle::new();
|
||||
/// puzzle.new_var_with_candidates(&[1,2]);
|
||||
/// puzzle.new_var_with_candidates(&[3,4]);
|
||||
/// puzzle.new_var(&[1,2]);
|
||||
/// puzzle.new_var(&[3,4]);
|
||||
///
|
||||
/// let solution = puzzle.solve_unique();
|
||||
/// assert!(solution.is_none());
|
||||
@ -475,8 +475,8 @@ impl Puzzle {
|
||||
///
|
||||
/// ```
|
||||
/// let mut puzzle = puzzle_solver::Puzzle::new();
|
||||
/// puzzle.new_var_with_candidates(&[1,2]);
|
||||
/// puzzle.new_var_with_candidates(&[3,4]);
|
||||
/// puzzle.new_var(&[1,2]);
|
||||
/// puzzle.new_var(&[3,4]);
|
||||
///
|
||||
/// let solutions = puzzle.solve_all();
|
||||
/// assert_eq!(solutions.len(), 4);
|
||||
|
@ -1,7 +1,5 @@
|
||||
use std::ops::{Bound, Range, RangeBounds, RangeInclusive};
|
||||
|
||||
use std::ops::{Bound, RangeBounds};
|
||||
use ranges::GenericRange;
|
||||
|
||||
use crate::Val;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
@ -96,24 +94,8 @@ impl Ranges {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Bound<Val>, Bound<Val>)> for Ranges {
|
||||
fn from(range: (Bound<Val>, Bound<Val>)) -> Self {
|
||||
Self {
|
||||
inner: range.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RangeInclusive<Val>> for Ranges {
|
||||
fn from(range: RangeInclusive<Val>) -> Self {
|
||||
Self {
|
||||
inner: range.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Range<Val>> for Ranges {
|
||||
fn from(range: Range<Val>) -> Self {
|
||||
impl<T: Into<ranges::Ranges<Val>>> From<T> for Ranges {
|
||||
fn from(range: T) -> Self {
|
||||
Self {
|
||||
inner: range.into(),
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ fn make_hidato(board: &Board) -> (Puzzle, Vec<VarToken>) {
|
||||
}
|
||||
}
|
||||
|
||||
let vars = sys.new_vars_with_candidates_1d(count, &pos);
|
||||
let vars = sys.new_vars(count, pos);
|
||||
|
||||
for y in 0..HEIGHT {
|
||||
for x in 0..WIDTH {
|
||||
@ -51,7 +51,7 @@ fn make_hidato(board: &Board) -> (Puzzle, Vec<VarToken>) {
|
||||
];
|
||||
|
||||
for i in 1..vars.len() {
|
||||
let step = sys.new_var_with_candidates(&deltas);
|
||||
let step = sys.new_var(&deltas);
|
||||
sys.equals(vars[i], vars[i - 1] + step);
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ fn make_kakuro(board: &[Rule]) -> (Puzzle, KakuroVars) {
|
||||
for y in y1..(y2 + 1) {
|
||||
for x in x1..(x2 + 1) {
|
||||
let var = vars[y][x].unwrap_or_else(|| {
|
||||
let var = sys.new_var_with_candidates(&[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
let var = sys.new_var(&[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
vars[y][x] = Some(var);
|
||||
var
|
||||
});
|
||||
|
@ -13,7 +13,7 @@ type Point = (usize, usize);
|
||||
|
||||
fn make_killer_sudoku(board: &[(Val, Vec<Point>)]) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
let mut sys = Puzzle::new();
|
||||
let vars = sys.new_vars_with_candidates_2d(SIZE, SIZE, &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
let vars = sys.new_vars_2d(SIZE, SIZE, &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
|
||||
for y in 0..SIZE {
|
||||
sys.all_different(&vars[y]);
|
||||
|
@ -2,19 +2,17 @@
|
||||
//!
|
||||
//! https://en.wikipedia.org/wiki/Magic_square
|
||||
|
||||
extern crate puzzle_solver;
|
||||
|
||||
use puzzle_solver::{LinExpr, Puzzle, Solution, Val, VarToken};
|
||||
|
||||
fn make_magic_square(n: usize) -> (Puzzle, Vec<Vec<VarToken>>, VarToken) {
|
||||
let mut sys = Puzzle::new();
|
||||
let digits: Vec<Val> = (1..(n * n + 1) as Val).collect();
|
||||
let vars = sys.new_vars_with_candidates_2d(n, n, &digits);
|
||||
let digits = 1..(n * n + 1) as Val;
|
||||
let vars = sys.new_vars_2d(n, n, digits.clone());
|
||||
|
||||
// Calculate the range of the total (in a dumb way).
|
||||
let min = digits.iter().take(n).sum();
|
||||
let max = digits.iter().rev().take(n).sum();
|
||||
let total = sys.new_var_with_candidates(&(min..max).collect::<Vec<Val>>());
|
||||
let min = digits.clone().into_iter().take(n).sum();
|
||||
let max = digits.clone().into_iter().rev().take(n).sum();
|
||||
let total = sys.new_var(min..max);
|
||||
|
||||
sys.all_different(vars.iter().flatten());
|
||||
|
||||
@ -44,7 +42,7 @@ fn make_magic_square(n: usize) -> (Puzzle, Vec<Vec<VarToken>>, VarToken) {
|
||||
}
|
||||
|
||||
// Sum of all digits = sum of all rows (columns) = total * n.
|
||||
sys.equals(total * (n as Val), digits.iter().sum::<Val>());
|
||||
sys.equals(total * (n as Val), digits.into_iter().sum::<Val>());
|
||||
|
||||
(sys, vars, total)
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ impl Constraint for Nonogram {
|
||||
fn make_nonogram(rows: &[Vec<usize>], cols: &[Vec<usize>]) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
let mut sys = Puzzle::new();
|
||||
let (w, h) = (cols.len(), rows.len());
|
||||
let vars = sys.new_vars_with_candidates_2d(w, h, &[0, 1]);
|
||||
let vars = sys.new_vars_2d(w, h, &[0, 1]);
|
||||
|
||||
for y in 0..h {
|
||||
sys.add_constraint(Nonogram::new(&vars[y], &rows[y]));
|
||||
|
@ -43,7 +43,7 @@ impl Constraint for NoDiagonal {
|
||||
fn make_queens(n: usize) -> (Puzzle, Vec<VarToken>) {
|
||||
let mut sys = Puzzle::new();
|
||||
let pos: Vec<Val> = (0..n as Val).collect();
|
||||
let vars = sys.new_vars_with_candidates_1d(n, &pos);
|
||||
let vars = sys.new_vars(n, pos);
|
||||
|
||||
sys.all_different(&vars);
|
||||
sys.add_constraint(NoDiagonal { vars: vars.clone() });
|
||||
|
@ -15,7 +15,7 @@ type SudokuVars = Vec<Vec<VarToken>>;
|
||||
type SamuraiVars = (SudokuVars, SudokuVars, SudokuVars, SudokuVars, SudokuVars);
|
||||
|
||||
fn make_sudoku(sys: &mut Puzzle) -> SudokuVars {
|
||||
let vars = sys.new_vars_with_candidates_2d(SIZE, SIZE, &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
let vars = sys.new_vars_2d(SIZE, SIZE, &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
|
||||
for y in 0..SIZE {
|
||||
sys.all_different(&vars[y]);
|
||||
|
@ -8,7 +8,8 @@ use puzzle_solver::{Puzzle, Solution, VarToken};
|
||||
|
||||
fn make_send_more_money() -> (Puzzle, Vec<VarToken>) {
|
||||
let mut sys = Puzzle::new();
|
||||
let vars = sys.new_vars_with_candidates_1d(8, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
let vars = sys.new_vars(8, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
|
||||
let (s, e, n, d) = (vars[0], vars[1], vars[2], vars[3]);
|
||||
let (m, o, r, y) = (vars[4], vars[5], vars[6], vars[7]);
|
||||
|
||||
@ -20,6 +21,7 @@ fn make_send_more_money() -> (Puzzle, Vec<VarToken>) {
|
||||
let send = 1000 * s + 100 * e + 10 * n + d;
|
||||
let more = 1000 * m + 100 * o + 10 * r + e;
|
||||
let money = 10000 * m + 1000 * o + 100 * n + 10 * e + y;
|
||||
|
||||
sys.equals(send + more, money);
|
||||
|
||||
(sys, vars)
|
||||
@ -59,9 +61,11 @@ fn sendmoremoney_carry() {
|
||||
let (mut sys, vars) = make_send_more_money();
|
||||
let (s, e, n, d) = (vars[0], vars[1], vars[2], vars[3]);
|
||||
let (m, o, r, y) = (vars[4], vars[5], vars[6], vars[7]);
|
||||
let c1 = sys.new_var_with_candidates(&carry);
|
||||
let c2 = sys.new_var_with_candidates(&carry);
|
||||
let c3 = sys.new_var_with_candidates(&carry);
|
||||
|
||||
let c1 = sys.new_var(&carry);
|
||||
let c2 = sys.new_var(&carry);
|
||||
let c3 = sys.new_var(&carry);
|
||||
|
||||
sys.intersect_candidates(m, &carry); // c4 == m.
|
||||
|
||||
sys.equals(d + e, 10 * c1 + y);
|
||||
|
@ -12,7 +12,7 @@ type Board = [[Val; SIZE]; SIZE];
|
||||
|
||||
fn make_sudoku(board: &Board) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
let mut sys = Puzzle::new();
|
||||
let vars = sys.new_vars_with_range_2d(SIZE, SIZE, 1..=9);
|
||||
let vars = sys.new_vars_2d(SIZE, SIZE, 1..=9);
|
||||
|
||||
for y in 0..SIZE {
|
||||
sys.all_different(&vars[y]);
|
||||
|
@ -12,7 +12,7 @@ type Board = [[Val; SIZE]; SIZE];
|
||||
|
||||
fn make_sujiko(board: &Board, tl: Val, tr: Val, bl: Val, br: Val) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
let mut sys = Puzzle::new();
|
||||
let vars = sys.new_vars_with_candidates_2d(3, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
let vars = sys.new_vars_2d(3, 3, &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
|
||||
sys.all_different(vars.iter().flatten());
|
||||
|
||||
|
@ -82,9 +82,9 @@ fn make_takuzu(puzzle: &[Vec<Val>]) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
let col_candidates = make_sums(width);
|
||||
|
||||
let mut sys = Puzzle::new();
|
||||
let vars = sys.new_vars_with_candidates_2d(width, height, &[0, 1]);
|
||||
let row_values = sys.new_vars_with_candidates_1d(height, &row_candidates);
|
||||
let col_values = sys.new_vars_with_candidates_1d(width, &col_candidates);
|
||||
let vars = sys.new_vars_2d(width, height, &[0, 1]);
|
||||
let row_values = sys.new_vars(height, row_candidates);
|
||||
let col_values = sys.new_vars(width, col_candidates);
|
||||
|
||||
for y in 0..height {
|
||||
let total = (height as Val) / 2;
|
||||
@ -113,14 +113,14 @@ fn make_takuzu(puzzle: &[Vec<Val>]) -> (Puzzle, Vec<Vec<VarToken>>) {
|
||||
// No three in a row, i.e. not: 000, 111.
|
||||
for y in 0..height {
|
||||
for window in vars[y].windows(3) {
|
||||
let disjunction = sys.new_var_with_candidates(&[1, 2]);
|
||||
let disjunction = sys.new_var(&[1, 2]);
|
||||
sys.equals(window[0] + window[1] + window[2], disjunction);
|
||||
}
|
||||
}
|
||||
|
||||
for x in 0..width {
|
||||
for y in 0..(height - 2) {
|
||||
let disjunction = sys.new_var_with_candidates(&[1, 2]);
|
||||
let disjunction = sys.new_var(&[1, 2]);
|
||||
sys.equals(
|
||||
vars[y + 0][x] + vars[y + 1][x] + vars[y + 2][x],
|
||||
disjunction,
|
||||
|
@ -9,7 +9,7 @@ extern crate puzzle_solver;
|
||||
|
||||
use num_rational::Ratio;
|
||||
use num_traits::ToPrimitive;
|
||||
use puzzle_solver::{LinExpr, Puzzle, Val};
|
||||
use puzzle_solver::{LinExpr, Puzzle};
|
||||
|
||||
#[test]
|
||||
fn xkcd_knapsack() {
|
||||
@ -28,7 +28,7 @@ fn xkcd_knapsack() {
|
||||
|
||||
for &(cost, _) in menu.iter() {
|
||||
let num = (total / cost).floor().to_integer();
|
||||
let var = sys.new_var_with_candidates(&(0..(num + 1)).collect::<Vec<Val>>());
|
||||
let var = sys.new_var(0..=num);
|
||||
vars.push(var)
|
||||
}
|
||||
|
||||
|
@ -64,11 +64,11 @@ fn zebra() {
|
||||
|
||||
// #1: There are five houses.
|
||||
let mut sys = Puzzle::new();
|
||||
let nat_var = sys.new_vars_with_candidates_1d(5, &[1, 2, 3, 4, 5]);
|
||||
let col_var = sys.new_vars_with_candidates_1d(5, &[1, 2, 3, 4, 5]);
|
||||
let dri_var = sys.new_vars_with_candidates_1d(5, &[1, 2, 3, 4, 5]);
|
||||
let smo_var = sys.new_vars_with_candidates_1d(5, &[1, 2, 3, 4, 5]);
|
||||
let pet_var = sys.new_vars_with_candidates_1d(5, &[1, 2, 3, 4, 5]);
|
||||
let nat_var = sys.new_vars(5, &[1, 2, 3, 4, 5]);
|
||||
let col_var = sys.new_vars(5, &[1, 2, 3, 4, 5]);
|
||||
let dri_var = sys.new_vars(5, &[1, 2, 3, 4, 5]);
|
||||
let smo_var = sys.new_vars(5, &[1, 2, 3, 4, 5]);
|
||||
let pet_var = sys.new_vars(5, &[1, 2, 3, 4, 5]);
|
||||
|
||||
let nat = |n| nat_var[n as usize];
|
||||
let col = |n| col_var[n as usize];
|
||||
@ -110,11 +110,11 @@ fn zebra() {
|
||||
sys.equals(nat(Norwegian), 1);
|
||||
|
||||
// #11: The man who smokes Blend lives in the house next to the house with cats.
|
||||
let neighbour11 = sys.new_var_with_candidates(&[-1, 1]);
|
||||
let neighbour11 = sys.new_var(&[-1, 1]);
|
||||
sys.equals(smo(Blend), pet(Cat) + neighbour11);
|
||||
|
||||
// #12: In a house next to the house where they have a horse, they smoke Dunhill.
|
||||
let neighbour12 = sys.new_var_with_candidates(&[-1, 1]);
|
||||
let neighbour12 = sys.new_var(&[-1, 1]);
|
||||
sys.equals(pet(Horse), smo(Dunhill) + neighbour12);
|
||||
|
||||
// #13: The man who smokes Blue Master drinks beer.
|
||||
@ -124,11 +124,11 @@ fn zebra() {
|
||||
sys.equals(nat(German), smo(Prince));
|
||||
|
||||
// #15: The Norwegian lives next to the blue house.
|
||||
let neighbour15 = sys.new_var_with_candidates(&[-1, 1]);
|
||||
let neighbour15 = sys.new_var(&[-1, 1]);
|
||||
sys.equals(nat(Norwegian), col(Blue) + neighbour15);
|
||||
|
||||
// #16: They drink water in a house next to the house where they smoke Blend.
|
||||
let neighbour16 = sys.new_var_with_candidates(&[-1, 1]);
|
||||
let neighbour16 = sys.new_var(&[-1, 1]);
|
||||
sys.equals(dri(Water), smo(Blend) + neighbour16);
|
||||
|
||||
let dict = sys.solve_any().expect("solution");
|
||||
|
Loading…
Reference in New Issue
Block a user