Orient all pieces rotations to 0 0 to resolve #1 and clean up.
This commit is contained in:
111
src/field.rs
Normal file
111
src/field.rs
Normal file
@@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
467
src/main.rs
467
src/main.rs
@@ -1,422 +1,12 @@
|
|||||||
use std::fmt;
|
mod field;
|
||||||
use colored::{Color, Colorize};
|
mod pattern;
|
||||||
|
mod piece;
|
||||||
|
|
||||||
#[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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests;
|
||||||
use crate::AbsCoord;
|
|
||||||
use crate::Field;
|
|
||||||
use crate::Piece;
|
|
||||||
use crate::solve;
|
|
||||||
|
|
||||||
#[test]
|
use field::Field;
|
||||||
fn test_add_pattern() {
|
use piece::Piece;
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn solve(field: Field, pieces: Vec<Piece>) -> Option<Field> {
|
fn solve(field: Field, pieces: Vec<Piece>) -> Option<Field> {
|
||||||
if field.empty_coords.is_empty() {
|
if field.empty_coords.is_empty() {
|
||||||
@@ -429,7 +19,7 @@ fn solve(field: Field, pieces: Vec<Piece>) -> Option<Field> {
|
|||||||
return None;
|
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 (piece_index, piece) in pieces.iter().enumerate() {
|
||||||
for pattern in &piece.patterns {
|
for pattern in &piece.patterns {
|
||||||
if field.fits(&empty_coord, &pattern) {
|
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);
|
new_field.add_pattern(&empty_coord, &pattern);
|
||||||
match solve(new_field, new_pieces) {
|
match solve(new_field, new_pieces) {
|
||||||
Some(field) => return Some(field),
|
Some(field) => return Some(field),
|
||||||
None => {},
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,32 +39,35 @@ fn solve(field: Field, pieces: Vec<Piece>) -> Option<Field> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let field = Field::new(6, 6);
|
let field = Field::new(8, 7);
|
||||||
let pieces = vec![
|
|
||||||
Piece::cleveland_z(), // -_
|
// All pieces as reference:
|
||||||
Piece::cleveland_z(), // -_
|
|
||||||
Piece::cleveland_z(), // -_
|
|
||||||
Piece::orange_ricky(), // ___|
|
|
||||||
Piece::orange_ricky(), // ___|
|
|
||||||
Piece::blue_ricky(), // |___
|
|
||||||
Piece::blue_ricky(), // |___
|
|
||||||
Piece::teewee(),
|
|
||||||
Piece::teewee(),
|
|
||||||
// Piece::cleveland_z(), // -_
|
// Piece::cleveland_z(), // -_
|
||||||
// Piece::rhode_island_z(), // _-
|
// Piece::rhode_island_z(), // _-
|
||||||
// Piece::teewee(),
|
// Piece::teewee(), // _|_
|
||||||
// Piece::hero(),
|
// Piece::hero(), // ____
|
||||||
// Piece::orange_ricky(), // ___|
|
// Piece::orange_ricky(), // ___|
|
||||||
// Piece::blue_ricky(), // |___
|
// Piece::blue_ricky(), // |___
|
||||||
// Piece::smashboy(),
|
// Piece::smashboy(), // o
|
||||||
|
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);
|
let result = solve(field, pieces);
|
||||||
match result {
|
match result {
|
||||||
Some(field) => println!("{}", field),
|
Some(field) => println!("{}", field),
|
||||||
None => println!("No solution",)
|
None => println!("No solution",),
|
||||||
};
|
};
|
||||||
|
|
||||||
// for piece in &pieces {
|
|
||||||
// print!("{}", piece);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
53
src/pattern.rs
Normal file
53
src/pattern.rs
Normal file
@@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
235
src/piece.rs
Normal file
235
src/piece.rs
Normal file
@@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
105
src/tests.rs
Normal file
105
src/tests.rs
Normal file
@@ -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());
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user