AoC2021/src/day13.rs

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(())
}
}