use std::fmt::{Display, Formatter, Error};
use super::vector::{VectorAdd, VectorDifference};
use super::piece::{MinimumPosition, Position, Positionable, Translatable, Transformable, Normalizable, Piece};
use super::pieces::Bag;
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Target<T> {
collection: Vec<Position<T>>,
}
impl<T> Target<T> where T: PartialOrd + Ord + PartialEq + Eq + Clone {
pub fn new(collection: Vec<Position<T>>) -> Target<T> {
Target { collection }
}
pub fn is_packed(&self) -> bool {
self.collection.is_empty()
}
pub fn fits(&self, piece: &Piece<T>) -> bool {
piece.iter().all(|position| self.collection.contains(&position))
}
pub fn place(&self, piece: &Piece<T>) -> Target<T> {
let collection: Vec<Position<T>> = self.collection
.iter()
.filter(|position| !piece.contains(position))
.cloned()
.collect();
Target::new(collection)
}
}
impl<T> MinimumPosition<T> for Target<T> where T: PartialOrd + Ord + Clone {
fn minimum_position(&self) -> Option<Position<T>> {
self.collection.iter().min().cloned()
}
}
#[derive(Debug)]
pub struct Solution<T> {
pieces: Vec<Piece<T>>
}
impl<T> Solution<T> where T : Clone {
pub fn empty() -> Solution<T> {
Solution { pieces: vec!() }
}
pub fn record(&self, piece: &Piece<T>) -> Solution<T> {
let mut pieces: Vec<Piece<T>> = self.pieces.to_vec();
pieces.push(piece.clone());
Solution { pieces }
}
}
impl Display for Solution<(i8, i8, i8)> {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "<")?;
for piece in &self.pieces {
write!(f, "{}", piece)?;
}
write!(f, ">")
}
}
impl Display for Solution<(i8, i8)> {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
write!(f, "<")?;
for piece in &self.pieces {
write!(f, "{}", piece)?;
}
write!(f, ">")
}
}
pub fn solve<F, T>(target: &Target<T>, bag: Bag<T>, when_solved: &mut F) where F: (FnMut(Solution<T>)) + Sized, T: Clone + PartialOrd + Ord + Transformable + Normalizable<T> + VectorDifference<T> + VectorAdd<T> {
let partial_solution: Solution<T> = Solution::empty();
solve_with(target, bag, partial_solution, when_solved)
}
pub fn solve_with<F, T>(target: &Target<T>, bag: Bag<T>, partial_solution: Solution<T>, when_solved: &mut F) where F: (FnMut(Solution<T>)) + Sized, T: Clone + PartialOrd + Ord + Transformable + Normalizable<T> + VectorDifference<T> + VectorAdd<T> {
if target.is_packed() {
when_solved(partial_solution)
} else {
let open_position = target.minimum_position().unwrap();
for (template, rest_of_bag) in bag {
for mut piece in template {
let block = piece.minimum_position().unwrap();
let translation = block.to(&open_position);
piece.translate(&translation);
if target.fits(&piece) {
let remaining_target = target.place(&piece);
let candidate_solution = partial_solution.record(&piece);
solve_with(&remaining_target, rest_of_bag.clone(), candidate_solution, when_solved)
}
}
}
}
}
#[cfg(test)]
mod tests {
use std::fmt::Write;
use super::super::piece::{Position, Piece, Template};
use super::super::pieces::Bag;
use super::*;
#[test]
fn piece_should_fit_in_target() {
let target = Target::new(vec!(
Position::new(0, 0, 0),
Position::new(1, 0, 0),
Position::new(0, 1, 0),
Position::new(1, 1, 0),
Position::new(0, 0, 1),
Position::new(1, 0, 1),
Position::new(0, 1, 1),
Position::new(1, 1, 1),
));
let piece = Piece::new(vec!(
Position::new(0, 0, 0),
Position::new(1, 0, 0),
Position::new(0, 1, 0),
Position::new(0, 0, 1),
));
assert!(target.fits(&piece));
}
#[test]
fn solve_should_pack_pieces() {
let target = Target::new(vec!(
Position::new(0, 0, 0),
Position::new(1, 0, 0),
Position::new(0, 1, 0),
Position::new(1, 1, 0),
Position::new(0, 0, 1),
Position::new(1, 0, 1),
Position::new(0, 1, 1),
Position::new(1, 1, 1),
));
let bag = Bag::new(vec!(
Template::new(vec!(
Position::new(0, 0, 0),
Position::new(1, 0, 0),
Position::new(0, 1, 0),
Position::new(0, 0, 1),
)),
Template::new(vec!(
Position::new(0, 0, 0),
Position::new(1, 0, 0),
Position::new(0, 1, 0),
Position::new(0, 0, 1),
)),
));
let mut solutions: Vec<Solution<(i8, i8, i8)>> = vec!();
solve(&target, bag, &mut |solution|{ solutions.push(solution)});
assert_eq!(solutions.len(), 8);
}
#[test]
fn solutions_should_display_nicely() {
let solution =
Solution::empty()
.record(
&Piece::new(vec!(
Position::new(0, 0, 0),
Position::new(1, 0, 0),
Position::new(0, 1, 0),
Position::new(0, 0, 1),
)))
.record(
&Piece::new(vec!(
Position::new(1, 1, 1),
Position::new(0, 1, 1),
Position::new(1, 0, 1),
Position::new(1, 1, 0),
)));
let mut output: String = String::new();
write!(&mut output, "{}", solution).expect("to cleanly write solution");
assert_eq!(output, String::from("<[(0, 0, 0)(0, 0, 1)(0, 1, 0)(1, 0, 0)][(0, 1, 1)(1, 0, 1)(1, 1, 0)(1, 1, 1)]>"));
}
}