Refactored variable constructors

This commit is contained in:
Andrey Tkachenko 2021-12-10 13:20:36 +04:00
parent dc20c9aa76
commit 656890c1be
19 changed files with 174 additions and 190 deletions

View File

@ -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]);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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(),
}

View File

@ -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);
}

View File

@ -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
});

View File

@ -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]);

View File

@ -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)
}

View File

@ -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]));

View File

@ -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() });

View File

@ -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]);

View File

@ -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);

View File

@ -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]);

View File

@ -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());

View File

@ -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,

View File

@ -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)
}

View File

@ -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");