From 49fac3e0a91351da432a483dc5fbb254381bade3 Mon Sep 17 00:00:00 2001 From: kageru Date: Thu, 17 Dec 2020 14:04:05 +0100 Subject: [PATCH] Add 2020/17/1 --- 2020/inputs/day17 | 8 +++ 2020/src/bin/day17.rs | 80 +++++++++++++++++++++++++++ 2020/src/grid.rs | 125 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 197 insertions(+), 16 deletions(-) create mode 100644 2020/inputs/day17 create mode 100644 2020/src/bin/day17.rs diff --git a/2020/inputs/day17 b/2020/inputs/day17 new file mode 100644 index 0000000..3e25938 --- /dev/null +++ b/2020/inputs/day17 @@ -0,0 +1,8 @@ +######.# +##.###.# +#.###.## +..#..### +##.#.#.# +##...##. +#.#.##.# +.###.### diff --git a/2020/src/bin/day17.rs b/2020/src/bin/day17.rs new file mode 100644 index 0000000..f7efcc2 --- /dev/null +++ b/2020/src/bin/day17.rs @@ -0,0 +1,80 @@ +#![feature(test)] +extern crate test; +use aoc2020::{common::*, grid::*}; + +type Parsed = Grid; + +fn read_input() -> String { + read_file(17) +} + +fn parse_input(raw: &str) -> Parsed { + // TODO: implement FromIterator for Grid + Grid { + fields: raw + .lines() + .enumerate() + .flat_map(move |(y, l)| l.bytes().enumerate().map(move |(x, b)| ((x, y, 0).into(), b.into()))) + .collect(), + } +} + +fn count_live_neighbors(p: &Position3D, grid: &Parsed) -> usize { + p.neighbors().iter().filter(|&n| grid.get(*n) == Cell::Alive).count() +} + +fn make_step(mut input: Parsed) -> Parsed { + let readonly = input.clone(); + Grid { + fields: input + .fields + .keys() + .flat_map(|p| p.neighbors()) + .map(|pos| { + let cell = readonly.get(pos); + let new = match (&cell, count_live_neighbors(&pos, &readonly)) { + (Cell::Alive, 2..=3) => Cell::Alive, + (Cell::Dead, 3) => Cell::Alive, + _ => Cell::Dead, + }; + (pos, new) + }) + .collect(), + } +} + +fn part1(parsed: &Parsed) -> usize { + let mut clone = parsed.clone(); + for _ in 0..6 { + 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)); +} + +#[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); +} diff --git a/2020/src/grid.rs b/2020/src/grid.rs index 19937f7..898287b 100644 --- a/2020/src/grid.rs +++ b/2020/src/grid.rs @@ -1,6 +1,8 @@ use impl_ops::*; -use itertools::join; -use std::{collections::HashMap, fmt::Display, hash::BuildHasher, ops, ops::AddAssign}; +use itertools::{join, Itertools, iproduct}; +use std::{ + collections::HashMap, convert::TryInto, fmt::{self, Display, Formatter}, hash::{BuildHasher, Hash}, ops, ops::AddAssign +}; #[derive(Hash, PartialEq, Eq, Debug, Clone, Copy)] pub struct Position2D { @@ -8,6 +10,22 @@ pub struct Position2D { pub y: i64, } +#[derive(Hash, PartialEq, Eq, Debug, Clone, Copy)] +pub struct Position3D { + pub x: i64, + pub y: i64, + pub z: i64, +} + +impl Position3D { + pub fn neighbors(&self) -> Vec { + iproduct!((-1..=1), (-1..=1), (-1..=1)) + .filter(|t| t != &(0, 0, 0)) + .map(|(x, y, z)| *self + Position3D::from((x, y, z))) + .collect() + } +} + #[derive(Clone, Copy, Debug)] pub enum Direction { Up, @@ -16,6 +34,62 @@ pub enum Direction { Right, } +#[derive(Hash, PartialEq, Eq, Debug, Clone, Copy)] +pub enum Cell { + Alive, + Dead, +} + +impl From for Cell { + fn from(b: u8) -> Self { + match b { + b'.' => Cell::Dead, + b'#' => Cell::Alive, + _ => unreachable!(), + } + } +} + +impl Display for Cell { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Cell::Alive => ".", + Cell::Dead => "#", + }) + } +} + +impl Default for Cell { + fn default() -> Self { + Cell::Dead + } +} + +pub trait Position {} +impl Position for Position2D {} +impl Position for Position3D {} + +#[derive(Debug, Clone)] +pub struct Grid { + pub fields: HashMap, +} + +impl Grid { + pub fn get>(&self, pos: Pos) -> T { + self.fields.get(&pos.into()).copied().unwrap_or_else(|| T::default()) + } + + pub fn insert>(&mut self, pos: Pos, t: T) { + self.fields.insert(pos.into(), t); + } +} + +impl Grid { + fn draw_ascii(&self) -> String { + draw_ascii(&self.fields) + } +} + pub const ALL_DIRECTIONS: [Direction; 4] = [Direction::Up, Direction::Down, Direction::Left, Direction::Right]; struct Boundaries { @@ -126,6 +200,22 @@ impl_op!(-|a: Position2D, b: Position2D| -> Position2D { } }); +impl_op!(-|a: Position3D, b: Position3D| -> Position3D { + Position3D { + x: a.x - b.x, + y: a.y - b.y, + z: a.z - b.z, + } +}); + +impl_op!(+|a: Position3D, b: Position3D| -> Position3D { + Position3D { + x: a.x + b.x, + y: a.y + b.y, + z: a.z + b.z, + } +}); + impl_op!(+|a: Position2D, b: Direction| -> Position2D { a + Position2D::from(b) }); @@ -152,27 +242,30 @@ impl AddAssign for Position2D { } } -impl From<(usize, usize)> for Position2D { - fn from(tuple: (usize, usize)) -> Position2D { - Position2D { - x: tuple.0 as i64, - y: tuple.1 as i64, +impl From<(I, I, I)> for Position3D +where I: TryInto +{ + fn from((x, y, z): (I, I, I)) -> Position3D { + Position3D { + x: unwrap_number_result(x), + y: unwrap_number_result(y), + z: unwrap_number_result(z), } } } -impl From<(i32, i32)> for Position2D { - fn from(tuple: (i32, i32)) -> Position2D { - Position2D { - x: tuple.0 as i64, - y: tuple.1 as i64, - } +// 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 { + match i.try_into() { + Ok(i) => return i, + _ => panic!("Bad coordinate"), } } -impl From<(i64, i64)> for Position2D { - fn from(tuple: (i64, i64)) -> Position2D { - Position2D { x: tuple.0, y: tuple.1 } +impl> From<(I, I)> for Position2D { + fn from((x, y): (I, I)) -> Position2D { + Position2D { x: x.into(), y: y.into() } } }