advent-of-code/2020/src/bin/day17.rs

161 lines
4.8 KiB
Rust
Raw Normal View History

#![allow(incomplete_features)]
2021-11-22 14:34:40 +01:00
#![feature(test, generic_const_exprs)]
2020-12-17 14:04:05 +01:00
extern crate test;
2020-12-17 14:51:05 +01:00
use aoc2020::{
2021-07-08 19:05:31 +02:00
common::*, grid::{self, cell::Cell, *}
2020-12-17 14:51:05 +01:00
};
use itertools::Itertools;
2020-12-17 14:04:05 +01:00
fn read_input() -> String {
read_file(17)
}
2021-07-08 19:05:31 +02:00
fn parse_input<const DIMS: usize>(raw: &str) -> Grid<DIMS, Cell> {
2020-12-17 14:13:02 +01:00
raw.lines()
.enumerate()
.flat_map(move |(y, l)| {
l.bytes()
.enumerate()
.map(move |(x, b)| (PositionND::<DIMS>::from_padded(&[x as i64, y as i64]), b.into()))
})
.filter(|(_, c)| c == &Cell::Alive)
2020-12-17 14:13:02 +01:00
.collect()
2020-12-17 14:04:05 +01:00
}
2021-07-08 19:05:31 +02:00
fn count_live_neighbors<const D: usize>(p: &PositionND<D>, grid: &Grid<D, Cell>) -> usize
where [(); grid::num_neighbors(D) + 1]: Sized {
2021-07-08 19:05:31 +02:00
IntoIterator::into_iter(p.neighbors())
.filter(|n| grid.get(n) == Cell::Alive)
.count()
2020-12-17 14:04:05 +01:00
}
2021-07-08 19:05:31 +02:00
fn make_step<const D: usize>(input: Grid<D, Cell>) -> Grid<D, Cell>
where [(); grid::num_neighbors(D) + 1]: Sized {
2020-12-17 14:04:05 +01:00
let readonly = input.clone();
2020-12-17 14:13:02 +01:00
input
.fields
.keys()
2021-07-08 19:05:31 +02:00
.flat_map(|p| p.neighbors())
.unique()
2021-07-08 19:05:31 +02:00
.map(|pos| (pos, next_state(&pos, &readonly)))
.filter(|(_, c)| c == &Cell::Alive)
2020-12-17 14:13:02 +01:00
.collect()
2020-12-17 14:04:05 +01:00
}
2021-07-08 19:05:31 +02:00
fn next_state<const D: usize>(pos: &PositionND<D>, grid: &Grid<D, Cell>) -> Cell
where [(); grid::num_neighbors(D) + 1]: Sized {
2021-07-08 19:05:31 +02:00
let cell = grid.get(pos);
2021-07-08 19:35:31 +02:00
match (&cell, count_live_neighbors::<D>(pos, grid)) {
2021-07-08 19:05:31 +02:00
(Cell::Alive, 2..=3) => Cell::Alive,
(Cell::Dead, 3) => Cell::Alive,
_ => Cell::Dead,
2021-07-08 19:35:31 +02:00
}
2021-07-08 19:05:31 +02:00
}
fn solve<const D: usize>(parsed: &Grid<D, Cell>, steps: usize) -> usize
where [(); grid::num_neighbors(D) + 1]: Sized {
2020-12-17 14:04:05 +01:00
let mut clone = parsed.clone();
2020-12-17 15:15:51 +01:00
for _ in 0..steps {
2020-12-17 14:04:05 +01:00
clone = make_step(clone);
}
clone.fields.into_iter().filter(|(_, c)| c == &Cell::Alive).count()
}
fn main() {
2020-12-17 15:15:51 +01:00
let raw = read_input();
2021-07-08 19:05:31 +02:00
let input = parse_input::<3>(&raw);
2020-12-17 15:15:51 +01:00
println!("Part 1: {}", solve(&input, 6));
2021-07-08 19:05:31 +02:00
let input = parse_input::<4>(&raw);
2020-12-17 15:15:51 +01:00
println!("Part 2: {}", solve(&input, 6));
2020-12-17 14:04:05 +01:00
}
#[cfg(test)]
mod tests {
use super::*;
use aoc2020::*;
use test::black_box;
const TEST_INPUT: &str = ".#.
..#
###";
2021-07-08 19:05:31 +02:00
#[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);
}
2020-12-17 15:15:51 +01:00
#[test]
fn test_3d() {
2021-07-08 19:05:31 +02:00
let input = parse_input::<3>(TEST_INPUT);
assert_eq!(solve(&input, 1), 11);
assert_eq!(solve(&input, 2), 21);
2020-12-17 15:15:51 +01:00
assert_eq!(solve(&input, 6), 112);
}
#[test]
fn test_4d() {
2021-07-08 19:05:31 +02:00
let input = parse_input::<4>(TEST_INPUT);
2020-12-17 15:15:51 +01:00
assert_eq!(solve(&input, 6), 848);
}
#[bench]
fn bench_3d_parse(b: &mut test::Bencher) {
let raw = read_input();
2021-07-08 19:05:31 +02:00
b.iter(|| assert_eq!(parse_input::<3>(black_box(&raw)).fields.len(), 43));
2020-12-17 15:15:51 +01:00
}
#[bench]
#[rustfmt::skip]
fn bench_4d_parse(b: &mut test::Bencher) {
let raw = read_input();
2021-07-08 19:05:31 +02:00
b.iter(|| assert_eq!(parse_input::<4>(black_box(&raw)).fields.len(), 43));
2020-12-17 15:15:51 +01:00
}
#[bench]
fn bench_3d(b: &mut test::Bencher) {
2021-07-08 19:05:31 +02:00
let input = parse_input::<3>(&read_input());
2020-12-17 15:15:51 +01:00
b.iter(|| assert_eq!(solve(&input, 6), 348));
}
#[bench]
fn bench_4d(b: &mut test::Bencher) {
2021-07-08 19:05:31 +02:00
let input = parse_input::<4>(&read_input());
2020-12-17 15:15:51 +01:00
b.iter(|| assert_eq!(solve(&input, 6), 2236));
}
2020-12-17 14:04:05 +01:00
}