summaryrefslogtreecommitdiff
path: root/2021/src/bin/day_10.rs
diff options
context:
space:
mode:
Diffstat (limited to '2021/src/bin/day_10.rs')
-rw-r--r--2021/src/bin/day_10.rs124
1 files changed, 124 insertions, 0 deletions
diff --git a/2021/src/bin/day_10.rs b/2021/src/bin/day_10.rs
new file mode 100644
index 0000000..a9a9f75
--- /dev/null
+++ b/2021/src/bin/day_10.rs
@@ -0,0 +1,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 }))
+ }
+}