summaryrefslogtreecommitdiff
path: root/2021/src/bin/day_10.rs
blob: a9a9f7593fd8eca934915ec8bf8596921c127e7d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use nom::{
    branch::alt, character::complete::char as nom_char, combinator::map, multi::many0, IResult,
};
use std::fs;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let input = fs::read_to_string("inputs/day_10.txt")?;

    let mut syntax_error_score = 0;
    let mut autocomplete_scores = Vec::new();
    for line in input.split("\n") {
        match parse_lisp(line) {
            Ok(_) => {
                // boring
            }
            Err(nom::Err::Failure(ParseError::MismatchedExpectation(_, actual))) => {
                syntax_error_score += match actual {
                    ')' => 3,
                    ']' => 57,
                    '}' => 1197,
                    '>' => 25137,
                    _ => 0,
                }
            }
            Err(nom::Err::Failure(ParseError::EndOfInput(_))) => {
                let mut line = line.to_owned();
                let mut autocomplete_score = 0u64;
                while let Err(nom::Err::Failure(ParseError::EndOfInput(expected))) =
                    parse_lisp(&line)
                {
                    autocomplete_score *= 5;
                    autocomplete_score += match expected {
                        ')' => 1,
                        ']' => 2,
                        '}' => 3,
                        '>' => 4,
                        _ => 0,
                    };
                    line.push(expected);
                }
                autocomplete_scores.push(autocomplete_score);
            }
            Err(_) => panic!("Unexpected nom error type"),
        }
    }
    dbg!(syntax_error_score);
    autocomplete_scores.sort();
    dbg!(autocomplete_scores[autocomplete_scores.len() / 2]);

    Ok(())
}

#[derive(Debug)]
enum ParseError<'a> {
    MismatchedExpectation(char, char),
    EndOfInput(char),
    Other(nom::error::Error<&'a str>),
}

impl<'a> From<nom::error::Error<&'a str>> for ParseError<'a> {
    fn from(e: nom::error::Error<&'a str>) -> Self {
        ParseError::Other(e)
    }
}

impl<'a> nom::error::ParseError<&'a str> for ParseError<'a> {
    fn from_error_kind(input: &'a str, kind: nom::error::ErrorKind) -> Self {
        nom::error::Error::from_error_kind(input, kind).into()
    }

    fn append(_input: &'a str, _kind: nom::error::ErrorKind, other: Self) -> Self {
        other
    }

    fn from_char(input: &'a str, c: char) -> Self {
        nom::error::Error::from_char(input, c).into()
    }
}

#[derive(Debug)]
struct Lisp {
    blocks: Vec<Block>,
}

#[derive(Debug)]
struct Block {
    opening: char,
    blocks: Vec<Block>,
}

fn parse_lisp(input: &str) -> IResult<&str, Lisp, ParseError> {
    map(parse_blocks, |blocks| Lisp { blocks })(input)
}

fn parse_blocks(input: &str) -> IResult<&str, Vec<Block>, ParseError> {
    many0(parse_block)(input)
}

fn parse_block(input: &str) -> IResult<&str, Block, ParseError> {
    alt((
        block('{', '}'),
        block('[', ']'),
        block('(', ')'),
        block('<', '>'),
    ))(input)
}

fn block(opening: char, closing: char) -> impl Fn(&str) -> IResult<&str, Block, ParseError> {
    move |input: &str| {
        let (input, _) = nom_char(opening)(input)?;
        let (input, blocks) = parse_blocks(input)?;
        let (input, _) = match nom_char(closing)(input) {
            Ok((input, closing)) => (input, closing),
            Err(nom::Err::Error(_)) => {
                return Err(nom::Err::Failure(match input.chars().next() {
                    Some(actual) => ParseError::MismatchedExpectation(closing, actual),
                    None => ParseError::EndOfInput(closing),
                }))
            }
            Err(e) => return Err(e),
        };
        Ok((input, Block { opening, blocks }))
    }
}