diff --git a/src/day_10/mod.rs b/src/day_10/mod.rs new file mode 100644 index 0000000..525c4ec --- /dev/null +++ b/src/day_10/mod.rs @@ -0,0 +1,62 @@ +mod model; + +use aoc_runner_derive::{aoc, aoc_generator}; +use itertools::Itertools; + +use self::model::Mistake; + +#[aoc_generator(day10)] +pub fn parse_input(input: &str) -> Vec { + input.lines().filter_map(model::find_mistake).collect() +} + +#[aoc(day10, part1)] +pub fn part1(input: &[Mistake]) -> usize { + input + .iter() + .filter_map(|mistake| mistake.unexpected_char_score()) + .sum() +} + +#[aoc(day10, part2)] +pub fn part2(input: &[Mistake]) -> usize { + let scores: Vec = input + .iter() + .filter_map(|mistake| mistake.autocomplete_score()) + .sorted() + .collect(); + scores[scores.len() / 2] +} + +#[cfg(test)] +mod test { + const EXAMPLE_INPUT: &str = "[({(<(())[]>[[{[]{<()<>> +[(()[<>])]({[<{<<[]>>( +{([(<{}[<>[]}>{[]{[(<()> +(((({<>}<{<{<>}{[]{[]{} +[[<[([]))<([[{}[[()]]] +[{[{({}]{}}([{[{{{}}([] +{<[[]]>}<{[{[{[]{()[[[] +[<(<(<(<{}))><([]([]() +<{([([[(<>()){}]>(<<{{ +<{([{{}}[<[[[<>{}]]]>[]]"; + const RESULT_PART_1: usize = 26397; + const RESULT_PART_2: usize = 288957; + + #[test] + fn part1_example() { + let result = super::part1(&super::parse_input(EXAMPLE_INPUT)); + assert_eq!(result, RESULT_PART_1); + } + + #[test] + fn part2_example() { + let result = super::part2(&super::parse_input(EXAMPLE_INPUT)); + assert_eq!(result, RESULT_PART_2); + } + + #[test] + fn parse_example() { + super::parse_input(EXAMPLE_INPUT); + } +} diff --git a/src/day_10/model.rs b/src/day_10/model.rs new file mode 100644 index 0000000..4f5f528 --- /dev/null +++ b/src/day_10/model.rs @@ -0,0 +1,217 @@ +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum Kind { + Round, + Square, + Curly, + Pointy, +} + +impl Kind { + pub fn unexpected_char_score(&self) -> usize { + match self { + Kind::Round => 3, + Kind::Square => 57, + Kind::Curly => 1197, + Kind::Pointy => 25137, + } + } +} + +pub enum Mistake { + Incomplete(Vec), + Unexpected(Kind), +} + +impl Mistake { + pub fn autocomplete_score(&self) -> Option { + if let Self::Incomplete(missing) = self { + let mut score = 0; + for kind in missing { + score *= 5; + score += match kind { + Kind::Round => 1, + Kind::Square => 2, + Kind::Curly => 3, + Kind::Pointy => 4, + }; + } + Some(score) + } else { + None + } + } + + pub fn unexpected_char_score(&self) -> Option { + if let Self::Unexpected(kind) = self { + Some(kind.unexpected_char_score()) + } else { + None + } + } +} + +pub fn find_mistake(s: &str) -> Option { + let mut vec = Vec::new(); + for kind in s.chars() { + match kind { + '(' => vec.push(Kind::Round), + '[' => vec.push(Kind::Square), + '{' => vec.push(Kind::Curly), + '<' => vec.push(Kind::Pointy), + ')' => { + if vec.pop().unwrap() != Kind::Round { + return Some(Mistake::Unexpected(Kind::Round)); + } + } + ']' => { + if vec.pop().unwrap() != Kind::Square { + return Some(Mistake::Unexpected(Kind::Square)); + } + } + '}' => { + if vec.pop().unwrap() != Kind::Curly { + return Some(Mistake::Unexpected(Kind::Curly)); + } + } + '>' => { + if vec.pop().unwrap() != Kind::Pointy { + return Some(Mistake::Unexpected(Kind::Pointy)); + } + } + _ => panic!("illegal character"), + } + } + if vec.is_empty() { + None + } else { + vec.reverse(); + Some(Mistake::Incomplete(vec)) + } +} + +//use std::{rc::Rc, sync::RwLock}; +// +//#[derive(Debug)] +//pub struct Chunk { +// kind: Kind, +// children: Vec>>, +// parent: Option>>, +// closing: Option, +//} +// +//impl Chunk { +// pub fn find_first_illegal_char(&self) -> Option { +// for child in &self.children { +// if let Some(kind) = child.try_read().unwrap().find_first_illegal_char() { +// return Some(kind); +// } +// } +// if Some(self.kind) != self.closing { +// self.closing.clone() +// } else { +// None +// } +// } +// +// pub fn new(kind: Kind, parent: Option>>) -> Rc> { +// let new = Rc::new(RwLock::new(Self { +// kind, +// children: Vec::new(), +// parent: parent.clone(), +// closing: None +// })); +// if let Some(mut parent) = parent { +// if let Ok(mut parent_locked) = RwLock::try_write(&mut parent) { +// parent_locked.children.push(new.clone()); +// } else { +// panic!("can't access parent to insert child") +// } +// } +// new +// +// } +// +// pub fn close(&mut self, kind: Kind) { +// self.closing = Some(kind); +// } +// +// pub fn parse(s: &str) -> Result>, &'static str> { +// let mut current: Option>> = None; +// +// let open = |kind, current| { +// Some(Chunk::new(kind, current)) +// }; +// let close = |kind, current: Option>>| { +// if let Some(mut current_some) = current { +// if let Ok(mut current_locked) = RwLock::try_write(&mut current_some) { +// current_locked.close(kind); +// Ok(current_locked.parent.clone()) +// } else { +// Err("can't get a lock to the current chunk") +// } +// } else { +// Ok(None) +// } +// +// }; +// +// for kind in s.chars() { +// match kind { +// '(' => current = open(Kind::Round, current), +// '[' => current = open(Kind::Square, current), +// '{' => current = open(Kind::Curly, current), +// '<' => current = open(Kind::Pointy, current), +// ')' => current = close(Kind::Round, current)?, +// ']' => current = close(Kind::Square, current)?, +// '}' => current = close(Kind::Curly, current)?, +// '>' => current = close(Kind::Pointy, current)?, +// _ => return Err("Illegal character!"), +// } +// } +// +// current.ok_or("String seems to have been empty") +// } +//} +// +//impl ToString for Chunk { +// fn to_string(&self) -> String { +// let mut ret_val = String::new(); +// match self.kind { +// Kind::Round => ret_val.push('('), +// Kind::Square => ret_val.push('['), +// Kind::Curly => ret_val.push('{'), +// Kind::Pointy => ret_val.push('<'), +// } +// for child in &self.children { +// ret_val.push_str(&child.try_read().unwrap().to_string()) +// } +// match self.closing { +// Some(Kind::Round) => ret_val.push(')'), +// Some(Kind::Square) => ret_val.push(']'), +// Some(Kind::Curly) => ret_val.push('}'), +// Some(Kind::Pointy) => ret_val.push('>'), +// None => (), +// } +// ret_val +// } +//} +// +// +#[cfg(test)] +mod test { + #[test] + fn unexpected_char() { + let res = super::find_mistake("{([(<{}[<>[]}>{[]{[(<()>"); + assert!(res.is_some()); + let res = res.unwrap(); + assert_eq!(res.unexpected_char_score(), Some(1197)); + } + + #[test] + fn incomplete_chunk() { + let res = super::find_mistake("<{([{{}}[<[[[<>{}]]]>[]]"); + assert!(res.is_some()); + let res = res.unwrap(); + assert_eq!(res.autocomplete_score(), Some(294)); + } +} diff --git a/src/lib.rs b/src/lib.rs index 3840967..5eb57f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,5 +11,6 @@ pub mod day_06; pub mod day_07; pub mod day_08; pub mod day_09; +pub mod day_10; aoc_lib! { year = 2021 }