diff --git a/src/field.rs b/src/field.rs new file mode 100644 index 0000000..4065154 --- /dev/null +++ b/src/field.rs @@ -0,0 +1,111 @@ +use crate::pattern::{AbsCoord, Pattern}; +use colored::{Color, Colorize}; +use std::fmt; + +/// A Field is a list of available (empty) coordinates. +#[derive(Debug, Clone)] +pub struct Field { + width: u32, + height: u32, + pub empty_coords: Vec, + patterns: Vec<(AbsCoord, Pattern)>, +} + +impl Field { + pub fn new(width: u32, height: u32) -> Self { + let mut empty_coords = Vec::new(); + for y in 0..height { + for x in 0..width { + empty_coords.push(AbsCoord { x, y }); + } + } + Self { + width, + height, + empty_coords, + patterns: Vec::new(), + } + } + + pub fn add_pattern(&mut self, location: &AbsCoord, pattern: &Pattern) -> bool { + // Convert the pattern's relative coordinates to absolute coordinates + let abs_coords: Option> = pattern + .coords + .iter() + .map(|rel_coord| location.add(rel_coord)) + .collect(); + match abs_coords { + Some(abs_coords) => { + if abs_coords + .iter() + .all(|abs_coord| self.empty_coords.contains(abs_coord)) + { + self.empty_coords + .retain(|empty_coord| !abs_coords.contains(empty_coord)); + self.patterns.push((location.clone(), pattern.clone())); + true + } else { + false + } + } + None => false, + } + } + + pub fn fits(&self, location: &AbsCoord, pattern: &Pattern) -> bool { + let abs_coords: Option> = pattern + .coords + .iter() + .map(|rel_coord| location.add(rel_coord)) + .collect(); + match abs_coords { + Some(abs_coords) => { + if abs_coords + .iter() + .all(|abs_coord| self.empty_coords.contains(abs_coord)) + { + true + } else { + false + } + } + None => false, + } + } +} + +impl fmt::Display for Field { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut grid = vec![ + vec![' '.to_string().color(Color::White); self.width as usize]; + self.height as usize + ]; + + let colors = vec![ + Color::Red, + Color::Green, + Color::Yellow, + Color::Blue, + Color::Magenta, + Color::Cyan, + Color::White, + Color::Black, + ]; + for (index, (location, pattern)) in self.patterns.iter().enumerate() { + let color = colors[index % colors.len()]; // This makes sure we loop back to the start if there are more patterns than colors. + for rel_coord in &pattern.coords { + let abs_coord = location.add(rel_coord).unwrap(); + grid[abs_coord.y as usize][abs_coord.x as usize] = '#'.to_string().color(color); + } + } + + for row in grid.iter() { + for cell in row.iter() { + write!(f, "{}", cell)?; + } + writeln!(f)?; + } + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index 48c0ffc..708cbb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,422 +1,12 @@ -use std::fmt; -use colored::{Color, Colorize}; - - -#[derive(Debug, PartialEq, Clone)] -struct RelCoord { - x: u32, - y: u32, -} - -#[derive(Debug, PartialEq, Clone)] -struct AbsCoord { - x: u32, - y: u32, -} - -impl AbsCoord { - fn add(&self, offset: &RelCoord) -> Self { - Self { - x: self.x + offset.x, - y: self.y + offset.y, - } - } -} - -#[derive(Debug, PartialEq, Clone)] -struct Pattern { - coords: Vec -} - -impl fmt::Display for Pattern { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Determine the bounding box for the pattern. - let max_x = self.coords.iter().map(|coord| coord.x).max().unwrap_or(0); - let max_y = self.coords.iter().map(|coord| coord.y).max().unwrap_or(0); - - // Initialize a 2D grid to hold the pattern representation. - let mut grid = vec![vec![' '; (max_x + 1) as usize]; (max_y + 1) as usize]; - - // Place the blocks of the pattern onto the grid. - for coord in &self.coords { - grid[coord.y as usize][coord.x as usize] = '#'; - } - - // Convert the 2D grid to a string. - for row in grid.iter() { - for cell in row.iter() { - write!(f, "{}", cell)?; - } - writeln!(f)?; - } - - Ok(()) - } -} - -#[derive(Debug, Clone)] -struct Piece { - name: String, - patterns: Vec -} - -impl Piece { - #[allow(dead_code)] - pub fn hero () -> Self { - let coords_vert = vec![ - RelCoord {x: 0, y: 0}, - RelCoord {x: 0, y: 1}, - RelCoord {x: 0, y: 2}, - RelCoord {x: 0, y: 3}, - ]; - let coords_hori = vec![ - RelCoord {x: 0, y: 0}, - RelCoord {x: 1, y: 0}, - RelCoord {x: 2, y: 0}, - RelCoord {x: 3, y: 0}, - ]; - Self { - name: "Hero".to_string(), - patterns: vec![ - Pattern { coords: coords_vert }, - Pattern { coords: coords_hori }, - ] - } - } - - #[allow(dead_code)] - pub fn teewee () -> Self { - let coords_up = vec![ - RelCoord {x: 1, y: 0}, - RelCoord {x: 0, y: 1}, - RelCoord {x: 1, y: 1}, - RelCoord {x: 2, y: 1}, - ]; - let coords_right = vec![ - RelCoord {x: 0, y: 0}, - RelCoord {x: 0, y: 1}, - RelCoord {x: 0, y: 2}, - RelCoord {x: 1, y: 1}, - ]; - let coords_down = vec![ - RelCoord {x: 0, y: 0}, - RelCoord {x: 1, y: 0}, - RelCoord {x: 2, y: 0}, - RelCoord {x: 1, y: 1}, - ]; - let coords_left = vec![ - RelCoord {x: 0, y: 1}, - RelCoord {x: 1, y: 0}, - RelCoord {x: 1, y: 1}, - RelCoord {x: 1, y: 2}, - ]; - Self { - name: "Teewee".to_string(), - patterns: vec![ - Pattern { coords: coords_up }, - Pattern { coords: coords_right }, - Pattern { coords: coords_down }, - Pattern { coords: coords_left }, - ] - } - } - - #[allow(dead_code)] - pub fn blue_ricky() -> Self { - let coords_upright = vec![ - RelCoord { x: 1, y: 0 }, - RelCoord { x: 1, y: 1 }, - RelCoord { x: 1, y: 2 }, - RelCoord { x: 0, y: 2 }, - ]; - let coords_right = vec![ - RelCoord { x: 0, y: 0 }, - RelCoord { x: 0, y: 1 }, - RelCoord { x: 1, y: 1 }, - RelCoord { x: 2, y: 1 }, - ]; - let coords_downright = vec![ - RelCoord { x: 0, y: 0 }, - RelCoord { x: 0, y: 1 }, - RelCoord { x: 0, y: 2 }, - RelCoord { x: 1, y: 0 }, - ]; - let coords_left = vec![ - RelCoord { x: 0, y: 0 }, - RelCoord { x: 1, y: 0 }, - RelCoord { x: 2, y: 0 }, - RelCoord { x: 2, y: 1 }, - ]; - Self { - name: "Blue Ricky".to_string(), - patterns: vec![ - Pattern { coords: coords_upright }, - Pattern { coords: coords_right }, - Pattern { coords: coords_downright }, - Pattern { coords: coords_left }, - ] - } - } - - #[allow(dead_code)] - pub fn orange_ricky() -> Self { - let coords_upright = vec![ - RelCoord { x: 0, y: 0 }, - RelCoord { x: 0, y: 1 }, - RelCoord { x: 0, y: 2 }, - RelCoord { x: 1, y: 2 }, - ]; - let coords_right = vec![ - RelCoord { x: 0, y: 0 }, - RelCoord { x: 1, y: 0 }, - RelCoord { x: 2, y: 0 }, - RelCoord { x: 0, y: 1 }, - ]; - let coords_downright = vec![ - RelCoord { x: 0, y: 0 }, - RelCoord { x: 1, y: 0 }, - RelCoord { x: 1, y: 1 }, - RelCoord { x: 1, y: 2 }, - ]; - let coords_left = vec![ - RelCoord { x: 2, y: 0 }, - RelCoord { x: 0, y: 1 }, - RelCoord { x: 1, y: 1 }, - RelCoord { x: 2, y: 1 }, - ]; - Self { - name: "Orange Ricky".to_string(), - patterns: vec![ - Pattern { coords: coords_upright }, - Pattern { coords: coords_right }, - Pattern { coords: coords_downright }, - Pattern { coords: coords_left }, - ] - } - } - - #[allow(dead_code)] - pub fn smashboy() -> Self { - let coords = vec![ - RelCoord { x: 0, y: 0 }, - RelCoord { x: 0, y: 1 }, - RelCoord { x: 1, y: 0 }, - RelCoord { x: 1, y: 1 }, - ]; - Self { - name: "Smashboy".to_string(), - patterns: vec![ - Pattern { coords }, - ] - } - } - - #[allow(dead_code)] - pub fn cleveland_z () -> Self { - let flat = vec![ - RelCoord {x: 0, y: 0}, - RelCoord {x: 1, y: 0}, - RelCoord {x: 1, y: 1}, - RelCoord {x: 2, y: 1}, - ]; - let upright = vec![ - RelCoord {x: 1, y: 0}, - RelCoord {x: 1, y: 1}, - RelCoord {x: 0, y: 1}, - RelCoord {x: 0, y: 2}, - ]; - Self { - name: "Cleveland Z".to_string(), - patterns: vec![ - Pattern { coords: flat }, - Pattern { coords: upright }, - ] - } - } - - #[allow(dead_code)] - pub fn rhode_island_z () -> Self { - let flat = vec![ - RelCoord {x: 0, y: 1}, - RelCoord {x: 1, y: 0}, - RelCoord {x: 1, y: 1}, - RelCoord {x: 2, y: 0}, - ]; - let upright = vec![ - RelCoord {x: 0, y: 0}, - RelCoord {x: 0, y: 1}, - RelCoord {x: 1, y: 1}, - RelCoord {x: 1, y: 2}, - ]; - Self { - name: "Rhode Island Z".to_string(), - patterns: vec![ - Pattern { coords: flat }, - Pattern { coords: upright }, - ] - } - } - - -} - - -impl fmt::Display for Piece { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}:\n\n", self.name.red())?; - for pattern in self.patterns.iter() { - write!(f, "{}\n", pattern)?; - } - Ok(()) - } -} - -/// A Field is a list of available (empty) coordinates. -#[derive(Debug, Clone)] -struct Field { - width: u32, - height: u32, - empty_coords: Vec, - patterns: Vec<(AbsCoord, Pattern)>, -} - -impl Field { - pub fn new(width: u32, height: u32) -> Self { - let mut empty_coords = Vec::new(); - for x in 0..width { - for y in 0..height { - empty_coords.push(AbsCoord { x: x, y: y }); - } - } - Self { width, height, empty_coords, patterns: Vec::new() } - } - - fn add_pattern(&mut self, location: &AbsCoord, pattern: &Pattern) -> bool { - // Convert the pattern's relative coordinates to absolute coordinates - let abs_coords: Vec<_> = pattern.coords.iter().map(|rel_coord| location.add(rel_coord)).collect(); - if abs_coords.iter().all(|abs_coord| self.empty_coords.contains(abs_coord)) { - self.empty_coords.retain(|empty_coord| !abs_coords.contains(empty_coord)); - self.patterns.push((location.clone(), pattern.clone())); - true - } else { - false - } - } - fn fits(&self, location: &AbsCoord, pattern: &Pattern) -> bool { - let abs_coords: Vec<_> = pattern.coords.iter().map(|rel_coord| location.add(rel_coord)).collect(); - if abs_coords.iter().all(|abs_coord| self.empty_coords.contains(abs_coord)) { - true - } else { - false - } - } -} - -impl fmt::Display for Field { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut grid = vec![vec![' '.to_string().color(Color::White); self.width as usize]; self.height as usize]; - - let colors = vec![Color::Red, Color::Green, Color::Yellow, Color::Blue, Color::Magenta, Color::Cyan, Color::White, Color::Black]; - for (index, (location, pattern)) in self.patterns.iter().enumerate() { - let color = colors[index % colors.len()]; // This makes sure we loop back to the start if there are more patterns than colors. - for rel_coord in &pattern.coords { - let abs_coord = location.add(rel_coord); - grid[abs_coord.y as usize][abs_coord.x as usize] = '#'.to_string().color(color); - } - } - - for row in grid.iter() { - for cell in row.iter() { - write!(f, "{}", cell)?; - } - writeln!(f)?; - } - - Ok(()) - } -} +mod field; +mod pattern; +mod piece; #[cfg(test)] -mod tests { - use crate::AbsCoord; - use crate::Field; - use crate::Piece; - use crate::solve; +mod tests; - #[test] - fn test_add_pattern() { - let mut field = Field::new(3, 3); - assert_eq!(field.add_pattern(&AbsCoord {x: 0, y: 0 }, &Piece::blue_ricky().patterns[0]), true); - assert_eq!(field.add_pattern(&AbsCoord {x: 0, y: 0 }, &Piece::blue_ricky().patterns[1]), false); - } - - #[test] - fn test_solve_no_pieces() { - let field = Field::new(5, 5); - let pieces = vec![]; - let result = solve(field, pieces); - assert!(result.is_none()); - } - - #[test] - fn test_solve_no_space() { - let field = Field::new(0, 0); - let pieces = vec![ - Piece::smashboy(), - ]; - let result = solve(field, pieces); - assert!(result.is_none()); - } - - #[test] - fn test_solve_base_case() { - let field = Field::new(0, 0); - let pieces = vec![]; - let result = solve(field, pieces); - assert!(result.is_some()); - } - - #[test] - fn test_solve_four_smashboys() { - let field = Field::new(4, 4); - let pieces = vec![ - Piece::smashboy(), - Piece::smashboy(), - Piece::smashboy(), - Piece::smashboy(), - ]; - let result = solve(field, pieces); - assert!(result.is_some()); - } - - #[test] - fn test_two_two_one() { - let field = Field::new(4, 5); - let pieces = vec![ - Piece::smashboy(), - Piece::smashboy(), - Piece::blue_ricky(), - Piece::blue_ricky(), - Piece::hero(), - ]; - let result = solve(field, pieces); - assert!(result.is_some()); - } - - #[test] - fn test_two_two_one_fail() { - let field = Field::new(4, 5); - let pieces = vec![ - Piece::smashboy(), - Piece::smashboy(), - Piece::blue_ricky(), - Piece::orange_ricky(), - Piece::hero(), - ]; - let result = solve(field, pieces); - assert!(result.is_some()); - } -} +use field::Field; +use piece::Piece; fn solve(field: Field, pieces: Vec) -> Option { if field.empty_coords.is_empty() { @@ -429,7 +19,7 @@ fn solve(field: Field, pieces: Vec) -> Option { return None; } - for empty_coord in &field.empty_coords.as_slice()[0..2] { + for empty_coord in &field.empty_coords { for (piece_index, piece) in pieces.iter().enumerate() { for pattern in &piece.patterns { if field.fits(&empty_coord, &pattern) { @@ -439,7 +29,7 @@ fn solve(field: Field, pieces: Vec) -> Option { new_field.add_pattern(&empty_coord, &pattern); match solve(new_field, new_pieces) { Some(field) => return Some(field), - None => {}, + None => {} } } } @@ -449,32 +39,35 @@ fn solve(field: Field, pieces: Vec) -> Option { } fn main() { - let field = Field::new(6, 6); + let field = Field::new(8, 7); + + // All pieces as reference: + // Piece::cleveland_z(), // -_ + // Piece::rhode_island_z(), // _- + // Piece::teewee(), // _|_ + // Piece::hero(), // ____ + // Piece::orange_ricky(), // ___| + // Piece::blue_ricky(), // |___ + // Piece::smashboy(), // o let pieces = vec![ - Piece::cleveland_z(), // -_ - Piece::cleveland_z(), // -_ - Piece::cleveland_z(), // -_ - Piece::orange_ricky(), // ___| - Piece::orange_ricky(), // ___| - Piece::blue_ricky(), // |___ - Piece::blue_ricky(), // |___ + Piece::hero(), // ---- + Piece::hero(), // ---- + Piece::smashboy(), + Piece::smashboy(), + Piece::smashboy(), + Piece::smashboy(), Piece::teewee(), Piece::teewee(), - // Piece::cleveland_z(), // -_ - // Piece::rhode_island_z(), // _- - // Piece::teewee(), - // Piece::hero(), - // Piece::orange_ricky(), // ___| - // Piece::blue_ricky(), // |___ - // Piece::smashboy(), + Piece::teewee(), + Piece::teewee(), + Piece::cleveland_z(), // -_ + Piece::rhode_island_z(), // _- + Piece::orange_ricky(), // ___| + Piece::blue_ricky(), // |___ ]; let result = solve(field, pieces); match result { Some(field) => println!("{}", field), - None => println!("No solution",) + None => println!("No solution",), }; - - // for piece in &pieces { - // print!("{}", piece); - // } } diff --git a/src/pattern.rs b/src/pattern.rs new file mode 100644 index 0000000..033193d --- /dev/null +++ b/src/pattern.rs @@ -0,0 +1,53 @@ +use std::fmt; + +#[derive(Debug, PartialEq, Clone)] +pub struct RelCoord { + pub x: i32, + pub y: i32, +} + +#[derive(Debug, PartialEq, Clone)] +pub struct AbsCoord { + pub x: u32, + pub y: u32, +} + +impl AbsCoord { + pub fn add(&self, offset: &RelCoord) -> Option { + Some(Self { + x: ((self.x as i32) + offset.x).try_into().ok()?, + y: ((self.y as i32) + offset.y).try_into().ok()?, + }) + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Pattern { + pub coords: Vec, +} + +impl fmt::Display for Pattern { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Determine the bounding box for the pattern. + let max_x = self.coords.iter().map(|coord| coord.x).max().unwrap_or(0); + let max_y = self.coords.iter().map(|coord| coord.y).max().unwrap_or(0); + + // Initialize a 2D grid to hold the pattern representation. + let mut grid = vec![vec![' '; (max_x + 1) as usize]; (max_y + 1) as usize]; + + // Place the blocks of the pattern onto the grid. + for coord in &self.coords { + grid[coord.y as usize][coord.x as usize] = '#'; + } + + // Convert the 2D grid to a string. + for row in grid.iter() { + for cell in row.iter() { + write!(f, "{}", cell)?; + } + writeln!(f)?; + } + + Ok(()) + } +} diff --git a/src/piece.rs b/src/piece.rs new file mode 100644 index 0000000..dfe2b5c --- /dev/null +++ b/src/piece.rs @@ -0,0 +1,235 @@ +use crate::pattern::{Pattern, RelCoord}; +use colored::Colorize; +use std::fmt; + +#[derive(Debug, Clone)] +pub struct Piece { + name: String, + pub patterns: Vec, +} + +impl Piece { + #[allow(dead_code)] + pub fn hero() -> Self { + let coords_vert = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: 0, y: 2 }, + RelCoord { x: 0, y: 3 }, + ]; + let coords_hori = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 1, y: 0 }, + RelCoord { x: 2, y: 0 }, + RelCoord { x: 3, y: 0 }, + ]; + Self { + name: "Hero".to_string(), + patterns: vec![ + Pattern { + coords: coords_vert, + }, + Pattern { + coords: coords_hori, + }, + ], + } + } + + #[allow(dead_code)] + pub fn teewee() -> Self { + let coords_up = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: -1, y: 1 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: 1, y: 1 }, + ]; + let coords_right = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: 0, y: 2 }, + RelCoord { x: 1, y: 1 }, + ]; + let coords_down = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 1, y: 0 }, + RelCoord { x: 2, y: 0 }, + RelCoord { x: 1, y: 1 }, + ]; + let coords_left = vec![ + RelCoord { x: -1, y: 1 }, + RelCoord { x: 0, y: 0 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: 0, y: 2 }, + ]; + Self { + name: "Teewee".to_string(), + patterns: vec![ + Pattern { coords: coords_up }, + Pattern { + coords: coords_right, + }, + Pattern { + coords: coords_down, + }, + Pattern { + coords: coords_left, + }, + ], + } + } + + #[allow(dead_code)] + pub fn blue_ricky() -> Self { + let coords_upright = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: 0, y: 2 }, + RelCoord { x: -1, y: 2 }, + ]; + let coords_right = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: 1, y: 1 }, + RelCoord { x: 2, y: 1 }, + ]; + let coords_downright = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: 0, y: 2 }, + RelCoord { x: 1, y: 0 }, + ]; + let coords_left = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 1, y: 0 }, + RelCoord { x: 2, y: 0 }, + RelCoord { x: 2, y: 1 }, + ]; + Self { + name: "Blue Ricky".to_string(), + patterns: vec![ + Pattern { + coords: coords_upright, + }, + Pattern { + coords: coords_right, + }, + Pattern { + coords: coords_downright, + }, + Pattern { + coords: coords_left, + }, + ], + } + } + + #[allow(dead_code)] + pub fn orange_ricky() -> Self { + let coords_upright = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: 0, y: 2 }, + RelCoord { x: 1, y: 2 }, + ]; + let coords_right = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 1, y: 0 }, + RelCoord { x: 2, y: 0 }, + RelCoord { x: 0, y: 1 }, + ]; + let coords_downright = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 1, y: 0 }, + RelCoord { x: 1, y: 1 }, + RelCoord { x: 1, y: 2 }, + ]; + let coords_left = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: -1, y: 1 }, + RelCoord { x: -2, y: 1 }, + ]; + Self { + name: "Orange Ricky".to_string(), + patterns: vec![ + Pattern { + coords: coords_upright, + }, + Pattern { + coords: coords_right, + }, + Pattern { + coords: coords_downright, + }, + Pattern { + coords: coords_left, + }, + ], + } + } + + #[allow(dead_code)] + pub fn smashboy() -> Self { + let coords = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: 1, y: 0 }, + RelCoord { x: 1, y: 1 }, + ]; + Self { + name: "Smashboy".to_string(), + patterns: vec![Pattern { coords }], + } + } + + #[allow(dead_code)] + pub fn cleveland_z() -> Self { + let flat = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 1, y: 0 }, + RelCoord { x: 1, y: 1 }, + RelCoord { x: 2, y: 1 }, + ]; + let upright = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: -1, y: 1 }, + RelCoord { x: -1, y: 2 }, + ]; + Self { + name: "Cleveland Z".to_string(), + patterns: vec![Pattern { coords: flat }, Pattern { coords: upright }], + } + } + + #[allow(dead_code)] + pub fn rhode_island_z() -> Self { + let flat = vec![ + RelCoord { x: -1, y: 1 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: 0, y: 0 }, + RelCoord { x: 1, y: 0 }, + ]; + let upright = vec![ + RelCoord { x: 0, y: 0 }, + RelCoord { x: 0, y: 1 }, + RelCoord { x: 1, y: 1 }, + RelCoord { x: 1, y: 2 }, + ]; + Self { + name: "Rhode Island Z".to_string(), + patterns: vec![Pattern { coords: flat }, Pattern { coords: upright }], + } + } +} + +impl fmt::Display for Piece { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}:\n\n", self.name.red())?; + for pattern in self.patterns.iter() { + write!(f, "{}\n", pattern)?; + } + Ok(()) + } +} diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..43315bc --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,105 @@ +use crate::pattern::AbsCoord; +use crate::solve; +use crate::Field; +use crate::Piece; + +#[test] +fn test_add_pattern() { + let mut field = Field::new(3, 3); + assert_eq!( + field.add_pattern(&AbsCoord { x: 0, y: 0 }, &Piece::blue_ricky().patterns[0]), + false + ); + assert_eq!( + field.add_pattern(&AbsCoord { x: 0, y: 0 }, &Piece::blue_ricky().patterns[1]), + true + ); +} + +#[test] +fn test_solve_no_pieces() { + let field = Field::new(5, 5); + let pieces = vec![]; + let result = solve(field, pieces); + assert!(result.is_none()); +} + +#[test] +fn test_solve_no_space() { + let field = Field::new(0, 0); + let pieces = vec![Piece::smashboy()]; + let result = solve(field, pieces); + assert!(result.is_none()); +} + +#[test] +fn test_solve_base_case() { + let field = Field::new(0, 0); + let pieces = vec![]; + let result = solve(field, pieces); + assert!(result.is_some()); +} + +#[test] +fn test_solve_four_smashboys() { + let field = Field::new(4, 4); + let pieces = vec![ + Piece::smashboy(), + Piece::smashboy(), + Piece::smashboy(), + Piece::smashboy(), + ]; + let result = solve(field, pieces); + assert!(result.is_some()); +} + +#[test] +fn test_two_two_one() { + let field = Field::new(4, 5); + let pieces = vec![ + Piece::smashboy(), + Piece::smashboy(), + Piece::blue_ricky(), + Piece::blue_ricky(), + Piece::hero(), + ]; + let result = solve(field, pieces); + assert!(result.is_some()); +} + +#[test] +fn test_two_two_one_fail() { + let field = Field::new(4, 5); + let pieces = vec![ + Piece::smashboy(), + Piece::smashboy(), + Piece::blue_ricky(), + Piece::orange_ricky(), + Piece::hero(), + ]; + let result = solve(field, pieces); + assert!(result.is_some()); +} + +#[test] +fn test_eight_seven() { + let field = Field::new(8, 7); + let pieces = vec![ + Piece::hero(), // ---- + Piece::hero(), // ---- + Piece::smashboy(), + Piece::smashboy(), + Piece::smashboy(), + Piece::smashboy(), + Piece::teewee(), + Piece::teewee(), + Piece::teewee(), + Piece::teewee(), + Piece::cleveland_z(), // -_ + Piece::rhode_island_z(), // _- + Piece::orange_ricky(), // ___| + Piece::blue_ricky(), // |___ + ]; + let result = solve(field, pieces); + assert!(result.is_some()); +}