AoC21/src/day10.rs

157 lines
4.0 KiB
Rust

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::<Vec<_>>();
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<Chunk> = 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<u64> {
match result {
ParseResult::Error { expected: _, found } => Some(match found {
')' => 3,
']' => 57,
'}' => 1197,
'>' => 25137,
_ => 0,
}),
_ => None,
}
}
fn autocomplete(result: &ParseResult) -> Option<Vec<char>> {
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<Chunk>,
}
#[derive(Debug)]
enum ParseResult {
Chunks(Vec<Chunk>),
Error {
expected: char,
found: char,
},
Incomplete {
finished: Vec<Chunk>,
in_progress: VecDeque<Chunk>,
},
}