Orient all pieces rotations to 0 0 to resolve #1 and clean up.
parent
367cda9e27
commit
17984773d7
|
@ -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<AbsCoord>,
|
||||
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<Vec<_>> = 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<Vec<_>> = 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(())
|
||||
}
|
||||
}
|
469
src/main.rs
469
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<RelCoord>
|
||||
}
|
||||
|
||||
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<Pattern>
|
||||
}
|
||||
|
||||
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<AbsCoord>,
|
||||
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<Piece>) -> Option<Field> {
|
||||
if field.empty_coords.is_empty() {
|
||||
|
@ -429,7 +19,7 @@ fn solve(field: Field, pieces: Vec<Piece>) -> Option<Field> {
|
|||
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<Piece>) -> Option<Field> {
|
|||
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<Piece>) -> Option<Field> {
|
|||
}
|
||||
|
||||
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);
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -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<Self> {
|
||||
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<RelCoord>,
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
|
@ -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<Pattern>,
|
||||
}
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
Loading…
Reference in New Issue