year2017/
day09.rs

1use utils::parser::{ParseError, ParseResult};
2use utils::prelude::*;
3
4/// Parsing a nested structure.
5#[derive(Clone, Debug)]
6pub struct Day09 {
7    part1: u32,
8    part2: u32,
9}
10
11impl Day09 {
12    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
13        let (part1, part2) = Self::parse.parse_complete(input)?;
14        Ok(Self { part1, part2 })
15    }
16
17    // Parses either a (nested) group or a single piece of garbage.
18    fn parse(mut input: &[u8]) -> ParseResult<(u32, u32)> {
19        let mut group_depth = 0;
20        let mut in_garbage = false;
21
22        let mut group_score = 0;
23        let mut garbage_count = 0;
24
25        loop {
26            input = if in_garbage {
27                match input {
28                    [b'!', _, rest @ ..] => rest,
29                    [b'>', rest @ ..] => {
30                        in_garbage = false;
31                        if group_depth == 0 {
32                            return Ok(((group_score, garbage_count), rest));
33                        }
34                        rest
35                    }
36                    [_, rest @ ..] => {
37                        garbage_count += 1;
38                        rest
39                    }
40                    [] => return Err((ParseError::ExpectedByte(b'>'), input)),
41                }
42            } else {
43                match input {
44                    [b'{', rest @ ..] => {
45                        group_depth += 1;
46                        group_score += group_depth;
47                        rest
48                    }
49                    [b'}', rest @ ..] if group_depth > 0 => {
50                        group_depth -= 1;
51                        if group_depth == 0 {
52                            return Ok(((group_score, garbage_count), rest));
53                        }
54                        rest
55                    }
56                    [b'<', rest @ ..] => {
57                        in_garbage = true;
58                        rest
59                    }
60                    [b',', rest @ ..] if group_depth > 0 => rest,
61                    _ if group_depth > 0 => {
62                        return Err((ParseError::Custom("expected '{', '}', ',' or '<'"), input))
63                    }
64                    _ => return Err((ParseError::Custom("expected '{' or '<'"), input)),
65                }
66            }
67        }
68    }
69
70    #[must_use]
71    pub fn part1(&self) -> u32 {
72        self.part1
73    }
74
75    #[must_use]
76    pub fn part2(&self) -> u32 {
77        self.part2
78    }
79}
80
81examples!(Day09 -> (u32, u32) [
82    {input: "{}", part1: 1},
83    {input: "{{{}}}", part1: 6},
84    {input: "{{},{}}", part1: 5},
85    {input: "{{{},{},{{}}}}", part1: 16},
86    {input: "{<a>,<a>,<a>,<a>}", part1: 1},
87    {input: "{{<ab>},{<ab>},{<ab>},{<ab>}}", part1: 9},
88    {input: "{{<!!>},{<!!>},{<!!>},{<!!>}}", part1: 9},
89    {input: "{{<a!>},{<a!>},{<a!>},{<ab>}}", part1: 3},
90    {input: "<>", part2: 0},
91    {input: "<random characters>", part2: 17},
92    {input: "<<<<>", part2: 3},
93    {input: "<{!>}>", part2: 2},
94    {input: "<!!>", part2: 0},
95    {input: "<!!!>>", part2: 0},
96    {input: "<{o\"i!a,<{i<a>", part2: 10},
97]);