From eef4aa7643414f338eb38c010138dfde7060ccac Mon Sep 17 00:00:00 2001 From: kageru Date: Thu, 17 Dec 2020 15:15:51 +0100 Subject: [PATCH] Add 2020/17/2 --- 2020/src/bin/day17.rs | 68 +++++++++++++++++++++++++++------------ 2020/src/grid/position.rs | 54 ++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 22 deletions(-) diff --git a/2020/src/bin/day17.rs b/2020/src/bin/day17.rs index a091e5b..9bed3a6 100644 --- a/2020/src/bin/day17.rs +++ b/2020/src/bin/day17.rs @@ -4,24 +4,22 @@ use aoc2020::{ common::*, grid::{cell::Cell, *} }; -type Parsed = Grid; - fn read_input() -> String { read_file(17) } -fn parse_input(raw: &str) -> Parsed { +fn parse_input P + Copy>(raw: &str, mut pos_gen: F) -> Grid { raw.lines() .enumerate() - .flat_map(move |(y, l)| l.bytes().enumerate().map(move |(x, b)| ((x, y, 0).into(), b.into()))) + .flat_map(move |(y, l)| l.bytes().enumerate().map(move |(x, b)| (pos_gen((x, y)), b.into()))) .collect() } -fn count_live_neighbors(p: &Position3D, grid: &Parsed) -> usize { +fn count_live_neighbors(p: &P, grid: &Grid) -> usize { p.neighbors().iter().filter(|&n| grid.get(*n) == Cell::Alive).count() } -fn make_step(input: Parsed) -> Parsed { +fn make_step(input: Grid) -> Grid { let readonly = input.clone(); input .fields @@ -39,38 +37,66 @@ fn make_step(input: Parsed) -> Parsed { .collect() } -fn part1(parsed: &Parsed) -> usize { +fn solve(parsed: &Grid, steps: usize) -> usize { let mut clone = parsed.clone(); - for _ in 0..6 { + for _ in 0..steps { clone = make_step(clone); } clone.fields.into_iter().filter(|(_, c)| c == &Cell::Alive).count() } -fn part2(parsed: &Parsed) -> usize { - unimplemented!() -} - fn main() { - let input = parse_input(&read_input()); - println!("Part 1: {}", part1(&input)); - println!("Part 2: {}", part2(&input)); + let raw = read_input(); + let input = parse_input(&raw, |(x, y)| Position3D::from((x, y, 0))); + println!("Part 1: {}", solve(&input, 6)); + let input = parse_input(&raw, |(x, y)| Position4D::from((x, y, 0, 0))); + println!("Part 2: {}", solve(&input, 6)); } #[cfg(test)] mod tests { use super::*; use aoc2020::*; - use paste::paste; use test::black_box; const TEST_INPUT: &str = ".#. ..# ###"; - test!(part1() == 112); - //test!(part2() == 0); - bench!(part1() == 348); - //bench!(part2() == 0); - // bench_input!(fields == 0); + #[test] + fn test_3d() { + let input = parse_input(TEST_INPUT, |(x, y)| Position3D::from((x, y, 0))); + assert_eq!(solve(&input, 6), 112); + } + + #[test] + fn test_4d() { + let input = parse_input(TEST_INPUT, |(x, y)| Position4D::from((x, y, 0, 0))); + assert_eq!(solve(&input, 6), 848); + } + + #[bench] + fn bench_3d_parse(b: &mut test::Bencher) { + let raw = read_input(); + b.iter(|| assert_eq!(parse_input(black_box(&raw), |(x, y)| Position3D::from((x, y, 0))).fields.len(), 64)); + } + + #[bench] + #[rustfmt::skip] + fn bench_4d_parse(b: &mut test::Bencher) { + let raw = read_input(); + b.iter(|| assert_eq!(parse_input(black_box(&raw), |(x, y)| Position4D::from((x, y, 0, 0))).fields.len(), 64)); + } + + #[bench] + fn bench_3d(b: &mut test::Bencher) { + let input = parse_input(&read_input(), |(x, y)| Position3D::from((x, y, 0))); + b.iter(|| assert_eq!(solve(&input, 6), 348)); + } + + #[bench] + fn bench_4d(b: &mut test::Bencher) { + let input = parse_input(&read_input(), |(x, y)| Position4D::from((x, y, 0, 0))); + b.iter(|| assert_eq!(solve(&input, 6), 2236)); + } } diff --git a/2020/src/grid/position.rs b/2020/src/grid/position.rs index 83b27e4..074d308 100644 --- a/2020/src/grid/position.rs +++ b/2020/src/grid/position.rs @@ -4,7 +4,7 @@ use itertools::iproduct; use std::{convert::TryInto, hash::Hash, ops, ops::AddAssign}; pub trait Position -where Self: Sized + Hash + Eq +where Self: Sized + Hash + PartialEq + Eq + Clone + Copy { fn neighbors(&self) -> Vec; } @@ -22,6 +22,14 @@ pub struct Position3D { pub z: i64, } +#[derive(Hash, PartialEq, Eq, Debug, Clone, Copy)] +pub struct Position4D { + pub x: i64, + pub y: i64, + pub z: i64, + pub w: i64, +} + mod p2d { use super::*; @@ -131,6 +139,50 @@ mod p3d { }); } +mod p4d { + use super::*; + + impl Position for Position4D { + fn neighbors(&self) -> Vec { + iproduct!((-1..=1), (-1..=1), (-1..=1), (-1..=1)) + .filter(|t| t != &(0, 0, 0, 0)) + .map(|(x, y, z, w)| *self + Position4D::from((x, y, z, w))) + .collect() + } + } + + impl From<(I, I, I, I)> for Position4D + where I: TryInto + { + fn from((x, y, z, w): (I, I, I, I)) -> Position4D { + Position4D { + x: unwrap_number_result(x), + y: unwrap_number_result(y), + z: unwrap_number_result(z), + w: unwrap_number_result(w), + } + } + } + + impl_op!(-|a: Position4D, b: Position4D| -> Position4D { + Position4D { + x: a.x - b.x, + y: a.y - b.y, + z: a.z - b.z, + w: a.w - b.w, + } + }); + + impl_op!(+|a: Position4D, b: Position4D| -> Position4D { + Position4D { + x: a.x + b.x, + y: a.y + b.y, + z: a.z + b.z, + w: a.w + b.w, + } + }); +} + // because calling .unwrap() on a TryInto result isn’t possible without trait bounds on the // associated Error type. fn unwrap_number_result>(i: I) -> i64 {