diff --git a/crates/utils/src/parser/base.rs b/crates/utils/src/parser/base.rs index 7fcb326..cf86895 100644 --- a/crates/utils/src/parser/base.rs +++ b/crates/utils/src/parser/base.rs @@ -166,17 +166,19 @@ pub trait Parser: Sized { /// # use utils::parser::{self, Parser}; /// assert_eq!( /// parser::u32() - /// .with_suffix(",".or(parser::eof())) - /// .repeat_n() // N = 3 is inferred + /// .repeat_n(",") // N = 3 is inferred /// .parse(b"12,34,56"), /// Ok(([12, 34, 56], &b""[..])) /// ); /// ``` - fn repeat_n(self) -> RepeatN + fn repeat_n(self, separator: S) -> RepeatN where for<'i> Self::Output<'i>: Copy + Default, { - RepeatN { parser: self } + RepeatN { + parser: self, + separator, + } } /// Repeat this parser while it matches, returning a [`ArrayVec`](crate::array::ArrayVec). diff --git a/crates/utils/src/parser/combinator.rs b/crates/utils/src/parser/combinator.rs index 859b0d5..c0b40f2 100644 --- a/crates/utils/src/parser/combinator.rs +++ b/crates/utils/src/parser/combinator.rs @@ -64,17 +64,20 @@ impl Parser for Optional

{ } #[derive(Copy, Clone)] -pub struct RepeatN { +pub struct RepeatN { pub(super) parser: P, + pub(super) separator: S, } -impl Parser: Copy + Default>> Parser for RepeatN { +impl Parser: Copy + Default>, S: Parser> Parser + for RepeatN +{ type Output<'i> = [P::Output<'i>; N]; type Then = Then2; #[inline] fn parse<'i>(&self, mut input: &'i [u8]) -> ParseResult<'i, Self::Output<'i>> { let mut output = [P::Output::default(); N]; - for item in &mut output { + for (i, item) in output.iter_mut().enumerate() { match self.parser.parse(input) { Ok((v, remaining)) => { *item = v; @@ -82,6 +85,13 @@ impl Parser: Copy + Default>> Parser for R } Err(e) => return Err(e), } + + if i < N - 1 { + match self.separator.parse(input) { + Ok((_, remaining)) => input = remaining, + Err(e) => return Err(e), + } + } } Ok((output, input)) } diff --git a/crates/utils/src/parser/simple.rs b/crates/utils/src/parser/simple.rs index 4cf9d86..07df3c4 100644 --- a/crates/utils/src/parser/simple.rs +++ b/crates/utils/src/parser/simple.rs @@ -162,9 +162,9 @@ impl Parser for Eof { /// assert_eq!( /// parser::u32() /// .with_suffix(b','.or(parser::eof())) -/// .repeat_n() -/// .parse(b"12,34,56"), -/// Ok(([12, 34, 56], &b""[..])) +/// .parse_all("12,34,56") +/// .unwrap(), +/// vec![12, 34, 56], /// ); /// ``` #[must_use] diff --git a/crates/utils/src/point.rs b/crates/utils/src/point.rs index 12752df..aee0cf7 100644 --- a/crates/utils/src/point.rs +++ b/crates/utils/src/point.rs @@ -1,4 +1,4 @@ -//! 2D point implementation. +//! 2D & 3D point implementations. use crate::number::{Number, Signed, SignedInteger, UnsignedInteger}; use std::fmt::Debug; @@ -155,4 +155,7 @@ impl Point2D { } } -// point_impl! {pub struct Point3D{x, y, z}} +point_impl! { + /// Struct representing a 3D point or vector. + pub struct Point3D{x, y, z} +} diff --git a/crates/year2016/src/day03.rs b/crates/year2016/src/day03.rs index cca0067..e8a6d45 100644 --- a/crates/year2016/src/day03.rs +++ b/crates/year2016/src/day03.rs @@ -11,7 +11,7 @@ impl Day03 { Ok(Self { input: parser::u32() .with_prefix(parser::take_while(u8::is_ascii_whitespace)) - .repeat_n() + .repeat_n(parser::noop()) .parse_lines(input)?, }) } diff --git a/crates/year2016/src/day22.rs b/crates/year2016/src/day22.rs index 8400e78..b011258 100644 --- a/crates/year2016/src/day22.rs +++ b/crates/year2016/src/day22.rs @@ -35,7 +35,7 @@ impl Day22 { parser::u32() .with_prefix(parser::take_while1(u8::is_ascii_whitespace)) .with_suffix(b'T') - .repeat_n::<3>(), + .repeat_n(parser::noop()), ) .map_res(|(x, y, [size, used, avail])| { if size == used + avail { diff --git a/crates/year2017/src/day20.rs b/crates/year2017/src/day20.rs new file mode 100644 index 0000000..79ac5b0 --- /dev/null +++ b/crates/year2017/src/day20.rs @@ -0,0 +1,109 @@ +use std::collections::HashMap; +use utils::point::Point3D; +use utils::prelude::*; + +/// Simulating colliding particles. +#[derive(Clone, Debug)] +pub struct Day20 { + particles: Vec, +} + +#[derive(Clone, Debug)] +struct Particle { + position: Point3D, + velocity: Point3D, + acceleration: Point3D, +} + +impl Day20 { + pub fn new(input: &str, _: InputType) -> Result { + let vector = parser::i64() + .repeat_n(b',') + .map(|[x, y, z]| Point3D::new(x, y, z)); + + Ok(Self { + particles: vector + .with_prefix("p=<") + .then(vector.with_prefix(">, v=<")) + .then(vector.with_prefix(">, a=<").with_suffix(">")) + .map(|(position, velocity, acceleration)| Particle { + position, + velocity, + acceleration, + }) + .parse_lines(input)?, + }) + } + + #[must_use] + pub fn part1(&self) -> usize { + self.particles + .iter() + .enumerate() + .min_by_key(|&(_, p)| p.position_at_time(1_000_000).manhattan_distance_unsigned()) + .unwrap() + .0 + } + + #[must_use] + pub fn part2(&self) -> usize { + let mut particles = self.particles.clone(); + let mut destroyed = vec![false; particles.len()]; + + let mut positions = HashMap::new(); + let mut last_destroyed = 0; + for t in 0.. { + positions.clear(); + + for (i, p) in particles.iter_mut().enumerate() { + if destroyed[i] { + continue; + } + + p.tick(); + + if let Some(j) = positions.insert(p.position, i) { + destroyed[i] = true; + destroyed[j] = true; + last_destroyed = t; + } + } + + // Stop when nothing has been destroyed for 10 turns and at least one particle has been + // destroyed. + if last_destroyed <= t - 10 && destroyed.iter().any(|&x| x) { + break; + } + } + + particles.len() - destroyed.iter().filter(|&&p| p).count() + } +} + +impl Particle { + fn position_at_time(&self, time: u64) -> Point3D { + self.position + + (self.velocity * time as i64) + + (self.acceleration * (time as i64 * time as i64 / 2)) + } + + fn tick(&mut self) { + self.velocity += self.acceleration; + self.position += self.velocity; + } +} + +examples!(Day20 -> (usize, usize) [ + { + input: "p=<3,0,0>, v=<2,0,0>, a=<-1,0,0>\n\ + p=<4,0,0>, v=<0,0,0>, a=<-2,0,0>", + part1: 0, + }, + { + input: "p=<-6,0,0>, v=<3,0,0>, a=<0,0,0>\n\ + p=<-4,0,0>, v=<2,0,0>, a=<0,0,0>\n\ + p=<-2,0,0>, v=<1,0,0>, a=<0,0,0>\n\ + p=<3,0,0>, v=<-1,0,0>, a=<0,0,0>", + part2: 1, + }, +]); diff --git a/crates/year2017/src/lib.rs b/crates/year2017/src/lib.rs index 2a5804e..ce6d31d 100644 --- a/crates/year2017/src/lib.rs +++ b/crates/year2017/src/lib.rs @@ -23,4 +23,5 @@ utils::year!(2017 => year2017, ${ 17 => day17::Day17, 18 => day18::Day18, 19 => day19::Day19, + 20 => day20::Day20, });