153 lines
3.6 KiB
Rust
153 lines
3.6 KiB
Rust
use std::collections::HashSet;
|
|
use std::str::FromStr;
|
|
use crate::Day;
|
|
|
|
pub struct Day13 {
|
|
points: HashSet<(i32, i32)>,
|
|
folds: Vec<Fold>,
|
|
}
|
|
|
|
#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Debug)]
|
|
enum Fold {
|
|
X(i32),
|
|
Y(i32),
|
|
}
|
|
|
|
impl FromStr for Fold {
|
|
type Err = ();
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let r = s.rsplit(' ').next().ok_or(())?;
|
|
let z = r.split('=').collect::<Vec<_>>();
|
|
match z[0] {
|
|
"x" => Ok(Fold::X(z[1].parse().map_err(|_| ())?)),
|
|
"y" => Ok(Fold::Y(z[1].parse().map_err(|_| ())?)),
|
|
_ => Err(()),
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
impl Day for Day13 {
|
|
fn init(content: String) -> anyhow::Result<Self> {
|
|
let l = content.lines();
|
|
let points = l.clone().take_while(|&x| !x.is_empty()).map(|st| st.split(',')
|
|
.map(str::parse).flatten().collect::<Vec<i32>>()).map(|v| (v[0], v[1])).collect::<HashSet<_>>();
|
|
|
|
Ok(Self {
|
|
points,
|
|
folds: l.filter(|st| st.starts_with("fold along")).map(str::parse).flatten().collect::<Vec<Fold>>(),
|
|
})
|
|
}
|
|
|
|
fn part1(&self) -> anyhow::Result<String> {
|
|
let fold = self.folds[0];
|
|
let map = self.points.clone();
|
|
Ok(format!("{}", flip(map, &fold).len()))
|
|
}
|
|
|
|
fn part2(&self) -> anyhow::Result<String> {
|
|
let r = self.folds.iter().fold(self.points.clone(), flip);
|
|
let max_x = r.iter().max_by(|&(xm, _), &(xn, _)| xm.cmp(xn)).ok_or_else(|| anyhow::Error::msg("X is empty!"))?;
|
|
let max_y = r.iter().max_by(|&(_, xm), &(_, xn)| xm.cmp(xn)).ok_or_else(|| anyhow::Error::msg("Y is empty!"))?;
|
|
let mut z = String::new();
|
|
for y in 0..=max_y.1 {
|
|
for x in 0..=max_x.0 {
|
|
if r.contains(&(x, y)) {
|
|
z += "█";
|
|
} else {
|
|
z += " ";
|
|
}
|
|
}
|
|
z += "\n";
|
|
}
|
|
Ok(format!("\n{}", z))
|
|
}
|
|
}
|
|
|
|
fn flip(set: HashSet<(i32, i32)>, f: &Fold) -> HashSet<(i32, i32)> {
|
|
set.iter().map(|&e| flip_one(e, *f)).collect()
|
|
}
|
|
|
|
fn flip_one((x, y): (i32, i32), f: Fold) -> (i32, i32) {
|
|
match f {
|
|
Fold::X(pos) => {
|
|
let new_x = if x > pos {
|
|
pos - x + pos
|
|
} else {
|
|
x
|
|
};
|
|
(new_x, y)
|
|
}
|
|
Fold::Y(pos) => {
|
|
let new_y = if y > pos {
|
|
pos - y + pos
|
|
} else {
|
|
y
|
|
};
|
|
(x, new_y)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::collections::HashSet;
|
|
use crate::day13::{Day13, flip, flip_one, Fold};
|
|
use crate::day::Day;
|
|
|
|
const INPUT: &str = r"6,10
|
|
0,14
|
|
9,10
|
|
0,3
|
|
10,4
|
|
4,11
|
|
6,0
|
|
6,12
|
|
4,1
|
|
0,13
|
|
10,12
|
|
3,4
|
|
3,0
|
|
8,4
|
|
1,10
|
|
2,14
|
|
8,10
|
|
9,0
|
|
|
|
fold along y=7
|
|
fold along x=5";
|
|
|
|
#[test]
|
|
fn flip_test() {
|
|
assert_eq!(flip_one((0, 0), Fold::X(3)), (0, 0));
|
|
assert_eq!(flip_one((3, 0), Fold::X(3)), (3, 0));
|
|
assert_eq!(flip_one((6, 0), Fold::X(3)), (0, 0));
|
|
}
|
|
|
|
#[test]
|
|
fn flip_set_test() {
|
|
let set = HashSet::from([(0, 3), (3, 2), (6, 3)]);
|
|
let r = flip(set, &Fold::X(3));
|
|
assert!(r.contains(&(0, 3)));
|
|
assert!(r.contains(&(3, 2)));
|
|
assert!(!r.contains(&(6, 3)));
|
|
assert_eq!(r.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn part1_test() -> anyhow::Result<()> {
|
|
let d = Day13::init(INPUT.to_string())?;
|
|
assert_eq!("17", d.part1()?);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn part1_real() -> anyhow::Result<()> {
|
|
let d = Day13::init(crate::load_input("13")?)?;
|
|
assert_eq!("781", d.part1()?);
|
|
Ok(())
|
|
}
|
|
}
|
|
|