From e838f132c23f4facae33566354fd89db05c522e5 Mon Sep 17 00:00:00 2001 From: kageru Date: Thu, 8 Jul 2021 19:05:31 +0200 Subject: [PATCH] Make day 17 work with const generics --- 2020/src/bin/day17.rs | 98 +++++++++++++++++++++++++++++---------- 2020/src/grid.rs | 12 ++--- 2020/src/grid/position.rs | 89 +++++++++++++++++++++++++++++++++-- 2020/src/lib.rs | 2 +- 4 files changed, 163 insertions(+), 38 deletions(-) diff --git a/2020/src/bin/day17.rs b/2020/src/bin/day17.rs index 3adcad8..48f01de 100644 --- a/2020/src/bin/day17.rs +++ b/2020/src/bin/day17.rs @@ -2,7 +2,7 @@ #![feature(test, const_generics, const_evaluatable_checked)] extern crate test; use aoc2020::{ - common::*, grid::{cell::Cell, *} + common::*, grid::{self, cell::Cell, *} }; use itertools::Itertools; @@ -10,7 +10,7 @@ fn read_input() -> String { read_file(17) } -fn parse_input PositionND + Copy>(raw: &str, mut pos_gen: F) -> Grid { +fn parse_input(raw: &str) -> Grid { raw.lines() .enumerate() .flat_map(move |(y, l)| { @@ -22,31 +22,39 @@ fn parse_input PositionND + .collect() } -fn count_live_neighbors(p: PositionND, grid: &Grid) -> usize { - p.neighbors().iter().filter(|&n| grid.get(n) == Cell::Alive).count() +fn count_live_neighbors(p: &PositionND, grid: &Grid) -> usize +where [(); grid::num_neighbors(D)]: Sized { + IntoIterator::into_iter(p.neighbors()) + .filter(|n| grid.get(n) == Cell::Alive) + .count() } -fn make_step(input: Grid) -> Grid { +fn make_step(input: Grid) -> Grid +where [(); grid::num_neighbors(D)]: Sized { let readonly = input.clone(); input .fields .keys() - .flat_map(|p| p.neighbors().iter()) + .flat_map(|p| p.neighbors()) .unique() - .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) - }) + .map(|pos| (pos, next_state(&pos, &readonly))) .filter(|(_, c)| c == &Cell::Alive) .collect() } -fn solve(parsed: &Grid, steps: usize) -> usize { +fn next_state(pos: &PositionND, grid: &Grid) -> Cell +where [(); grid::num_neighbors(D)]: Sized { + let cell = grid.get(pos); + let new = match (&cell, count_live_neighbors::(pos, &grid)) { + (Cell::Alive, 2..=3) => Cell::Alive, + (Cell::Dead, 3) => Cell::Alive, + _ => Cell::Dead, + }; + new +} + +fn solve(parsed: &Grid, steps: usize) -> usize +where [(); grid::num_neighbors(D)]: Sized { let mut clone = parsed.clone(); for _ in 0..steps { clone = make_step(clone); @@ -56,9 +64,9 @@ fn solve(parsed: &Grid, steps: usize) -> usize { fn main() { let raw = read_input(); - let input = parse_input(&raw, |(x, y)| Position3D::from((x, y, 0))); + let input = parse_input::<3>(&raw); println!("Part 1: {}", solve(&input, 6)); - let input = parse_input(&raw, |(x, y)| Position4D::from((x, y, 0, 0))); + let input = parse_input::<4>(&raw); println!("Part 2: {}", solve(&input, 6)); } @@ -72,40 +80,82 @@ mod tests { ..# ###"; + #[test] + fn test_make_step() { + let input = parse_input::<3>(TEST_INPUT); + let changed = make_step(input.clone()); + let expected = Grid { + fields: IntoIterator::into_iter([ + (PositionND { points: [1, 3, 0] }, Cell::Alive), + (PositionND { points: [0, 1, 0] }, Cell::Alive), + (PositionND { points: [2, 2, 0] }, Cell::Alive), + (PositionND { points: [2, 2, 1] }, Cell::Alive), + (PositionND { points: [0, 1, 1] }, Cell::Alive), + (PositionND { points: [2, 1, 0] }, Cell::Alive), + (PositionND { points: [1, 3, -1] }, Cell::Alive), + (PositionND { points: [0, 1, -1] }, Cell::Alive), + (PositionND { points: [1, 2, 0] }, Cell::Alive), + (PositionND { points: [1, 3, 1] }, Cell::Alive), + (PositionND { points: [2, 2, -1] }, Cell::Alive), + ]) + .collect(), + }; + assert_eq!(changed, expected); + } + + #[test] + fn test_count_live_neighbors() { + let input = parse_input::<2>(TEST_INPUT); + let one_one = PositionND { points: [1, 1] }; + let live = count_live_neighbors(&one_one, &input); + assert_eq!(live, 5); + } + + #[test] + fn test_next_state() { + let input = parse_input::<2>(TEST_INPUT); + let one_one = PositionND { points: [1, 1] }; + assert_eq!(next_state(&one_one, &input), Cell::Dead); + let one_three = PositionND { points: [1, 3] }; + assert_eq!(next_state(&one_three, &input), Cell::Alive); + } + #[test] fn test_3d() { - let input = parse_input(TEST_INPUT, |(x, y)| Position3D::from((x, y, 0))); + let input = parse_input::<3>(TEST_INPUT); + assert_eq!(solve(&input, 1), 11); + assert_eq!(solve(&input, 2), 21); assert_eq!(solve(&input, 6), 112); } #[test] fn test_4d() { - let input = parse_input(TEST_INPUT, |(x, y)| Position4D::from((x, y, 0, 0))); + let input = parse_input::<4>(TEST_INPUT); 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(), 43)); + b.iter(|| assert_eq!(parse_input::<3>(black_box(&raw)).fields.len(), 43)); } #[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(), 43)); + b.iter(|| assert_eq!(parse_input::<4>(black_box(&raw)).fields.len(), 43)); } #[bench] fn bench_3d(b: &mut test::Bencher) { - let input = parse_input(&read_input(), |(x, y)| Position3D::from((x, y, 0))); + let input = parse_input::<3>(&read_input()); 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))); + let input = parse_input::<4>(&read_input()); b.iter(|| assert_eq!(solve(&input, 6), 2236)); } } diff --git a/2020/src/grid.rs b/2020/src/grid.rs index eb0a69a..ac43b93 100644 --- a/2020/src/grid.rs +++ b/2020/src/grid.rs @@ -7,16 +7,12 @@ pub use position::*; use itertools::join; use std::{collections::HashMap, fmt::Display, hash::BuildHasher}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Grid { pub fields: HashMap, T>, } impl Grid { - //pub fn get_convert>(&self, pos: Pos) -> T { - //self.fields.get(&pos.into()).copied().unwrap_or_else(|| T::default()) - //} - pub fn get(&self, pos: &PositionND) -> T { self.fields.get(pos).copied().unwrap_or_else(|| T::default()) } @@ -35,9 +31,9 @@ impl std::iter::FromIterator<(PositionND Grid { - // fn draw_ascii(&self) -> String { - // draw_ascii(&self.fields) - // } +// fn draw_ascii(&self) -> String { +// draw_ascii(&self.fields) +// } // } struct Boundaries { diff --git a/2020/src/grid/position.rs b/2020/src/grid/position.rs index f7d7268..5972bfb 100644 --- a/2020/src/grid/position.rs +++ b/2020/src/grid/position.rs @@ -1,3 +1,4 @@ +extern crate test; use super::direction::*; use impl_ops::*; use itertools::iproduct; @@ -34,7 +35,7 @@ pub struct Position4D { #[derive(Hash, PartialEq, Eq, Debug, Clone, Copy)] pub struct PositionND { - points: [i64; DIMS], + pub points: [i64; DIMS], } impl From<[I; D]> for PositionND @@ -49,7 +50,7 @@ where I: TryInto + Copy } } -const fn num_neighbors(d: usize) -> usize { +pub const fn num_neighbors(d: usize) -> usize { 3usize.pow(d as u32) - 1 } @@ -70,13 +71,14 @@ impl PositionND { pub fn neighbors(&self) -> [PositionND; num_neighbors(DIMS)] where [PositionND; num_neighbors(DIMS)]: Sized, + [(); num_neighbors(DIMS)]: Sized, { let mut out = [PositionND::zero(); num_neighbors(DIMS)]; match DIMS { 2 => { for (i, n) in iproduct!((-1..=1), (-1..=1)) .filter(|t| t != &(0, 0)) - .map(|(x, y)| PositionND::::from_padded(&[x, y])) + .map(|(x, y)| PositionND::::from_padded(&[self.points[0] + x, self.points[1] + y])) .enumerate() { out[i] = n; @@ -85,7 +87,7 @@ impl PositionND { 3 => { for (i, n) in iproduct!((-1..=1), (-1..=1), (-1..=1)) .filter(|t| t != &(0, 0, 0)) - .map(|(x, y, z)| PositionND::::from_padded(&[x, y, z])) + .map(|(x, y, z)| PositionND::::from_padded(&[self.points[0]+x, self.points[1]+y, self.points[2]+z])) .enumerate() { out[i] = n; @@ -94,7 +96,7 @@ impl PositionND { 4 => { for (i, n) in iproduct!((-1..=1), (-1..=1), (-1..=1), (-1..=1)) .filter(|t| t != &(0, 0, 0, 0)) - .map(|(x, y, z, w)| PositionND::::from_padded(&[x, y, z, w])) + .map(|(x, y, z, w)| PositionND::::from_padded(&[self.points[0]+x, self.points[1]+y, self.points[2]+z, self.points[3]+w])) .enumerate() { out[i] = n; @@ -296,3 +298,80 @@ fn unwrap_number_result>(i: I) -> i64 { _ => panic!("Bad coordinate"), } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_neighbors_2d() { + let p = PositionND { points: [0, 0] }; + let n = p.neighbors(); + assert_eq!( + n, + [ + PositionND { points: [-1, -1] }, + PositionND { points: [-1, 0] }, + PositionND { points: [-1, 1] }, + PositionND { points: [0, -1] }, + PositionND { points: [0, 1] }, + PositionND { points: [1, -1] }, + PositionND { points: [1, 0] }, + PositionND { points: [1, 1] }, + ] + ); + + let p = PositionND { points: [1, 1] }; + let n = p.neighbors(); + assert_eq!( + n, + [ + PositionND { points: [0, 0] }, + PositionND { points: [0, 1] }, + PositionND { points: [0, 2] }, + PositionND { points: [1, 0] }, + PositionND { points: [1, 2] }, + PositionND { points: [2, 0] }, + PositionND { points: [2, 1] }, + PositionND { points: [2, 2] }, + ] + ) + } + + #[test] + fn test_neighbors_3d() { + let p = PositionND { points: [0, 0, 0] }; + let n = p.neighbors(); + assert_eq!( + n, + [ + PositionND { points: [-1, -1, -1] }, + PositionND { points: [-1, -1, 0] }, + PositionND { points: [-1, -1, 1] }, + PositionND { points: [-1, 0, -1] }, + PositionND { points: [-1, 0, 0] }, + PositionND { points: [-1, 0, 1] }, + PositionND { points: [-1, 1, -1] }, + PositionND { points: [-1, 1, 0] }, + PositionND { points: [-1, 1, 1] }, + PositionND { points: [0, -1, -1] }, + PositionND { points: [0, -1, 0] }, + PositionND { points: [0, -1, 1] }, + PositionND { points: [0, 0, -1] }, + PositionND { points: [0, 0, 1] }, + PositionND { points: [0, 1, -1] }, + PositionND { points: [0, 1, 0] }, + PositionND { points: [0, 1, 1] }, + PositionND { points: [1, -1, -1] }, + PositionND { points: [1, -1, 0] }, + PositionND { points: [1, -1, 1] }, + PositionND { points: [1, 0, -1] }, + PositionND { points: [1, 0, 0] }, + PositionND { points: [1, 0, 1] }, + PositionND { points: [1, 1, -1] }, + PositionND { points: [1, 1, 0] }, + PositionND { points: [1, 1, 1] }, + ] + ); + } +} diff --git a/2020/src/lib.rs b/2020/src/lib.rs index 76b81db..a0264e2 100644 --- a/2020/src/lib.rs +++ b/2020/src/lib.rs @@ -1,5 +1,5 @@ #![allow(incomplete_features)] -#![feature(const_generics, const_evaluatable_checked)] +#![feature(const_generics, const_evaluatable_checked, test)] pub mod common; pub mod grid; pub mod teststuff;