use std::collections::VecDeque; pub fn day10() { let corrupted = "{([(<{}[<>[]}>{[]{[(<()>"; let result = parse_line(corrupted); println!( "corrupted: {:?}, score: {:?}", &result, error_score(&result) ); let incomplete = "[(()[<>])]({[<{<<[]>>("; println!("incomplete: {:#?}", parse_line(incomplete)); // let input = r#" // [({(<(())[]>[[{[]{<()<>> // [(()[<>])]({[<{<<[]>>( // {([(<{}[<>[]}>{[]{[(<()> // (((({<>}<{<{<>}{[]{[]{} // [[<[([]))<([[{}[[()]]] // [{[{({}]{}}([{[{{{}}([] // {<[[]]>}<{[{[{[]{()[[[] // [<(<(<(<{}))><([]([]() // <{([([[(<>()){}]>(<<{{ // <{([{{}}[<[[[<>{}]]]>[]] // "#; let input = include_str!("../input/day10.txt"); let parsed_lines = input.trim().lines().map(parse_line); let total_error_score: u64 = parsed_lines .clone() .filter_map(|res| error_score(&res)) .sum(); println!("total error score: {}", total_error_score); let completion = autocomplete(&parse_line(incomplete)).unwrap(); println!( "to complete: {:?}, score: {}", &completion, autocomplete_score(&completion) ); let mut autocomplete_scores = parsed_lines .filter_map(|res| autocomplete(&res)) .map(|completion| autocomplete_score(&completion)) .collect::>(); autocomplete_scores.sort(); println!( "middle score: {}", autocomplete_scores[autocomplete_scores.len() / 2] ); } static OPENERS: &[char] = &['(', '[', '{', '<']; static CLOSERS: &[char] = &[')', ']', '}', '>']; fn parse_line(line: &str) -> ParseResult { let mut iter = line.trim().chars().peekable(); let mut finished = vec![]; let mut in_progress: VecDeque = VecDeque::new(); while let Some(c) = iter.next() { if OPENERS.contains(&c) { let index = OPENERS.iter().position(|&e| e == c).unwrap(); let new = Chunk { opener: c, closer: CLOSERS[index], chunks: vec![], }; in_progress.push_front(new); } else if CLOSERS.contains(&c) { if in_progress.front().unwrap().closer == c { let closed = in_progress.pop_front().unwrap(); match in_progress.front_mut() { Some(new_cur) => new_cur.chunks.push(closed), None => { finished.push(closed); } } } else { return ParseResult::Error { expected: in_progress.front().unwrap().closer, found: c, }; } } else { panic!("unreachable"); } } if in_progress.is_empty() { ParseResult::Chunks(finished) } else { ParseResult::Incomplete { finished, in_progress, } } } fn error_score(result: &ParseResult) -> Option { match result { ParseResult::Error { expected: _, found } => Some(match found { ')' => 3, ']' => 57, '}' => 1197, '>' => 25137, _ => 0, }), _ => None, } } fn autocomplete(result: &ParseResult) -> Option> { match result { ParseResult::Incomplete { finished: _, in_progress, } => Some(in_progress.iter().map(|chunk| chunk.closer).collect()), _ => None, } } fn autocomplete_score(completion: &[char]) -> u64 { let mut score = 0; for c in completion { score *= 5; score += match c { ')' => 1, ']' => 2, '}' => 3, '>' => 4, _ => 0, }; } score } #[derive(Debug)] struct Chunk { opener: char, closer: char, chunks: Vec, } #[derive(Debug)] enum ParseResult { Chunks(Vec), Error { expected: char, found: char, }, Incomplete { finished: Vec, in_progress: VecDeque, }, }