2017-12-12 10:05:25 +00:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::{Read, BufReader};
|
|
|
|
use std::char;
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
enum Flags {
|
|
|
|
InGarbage,
|
|
|
|
CancelNext,
|
|
|
|
CancelNextInGarbage,
|
|
|
|
Normal
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct State {
|
|
|
|
group_depth: usize,
|
|
|
|
flags: Flags,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let file = File::open("input").expect("unable to open input file");
|
|
|
|
let reader = BufReader::new(file);
|
|
|
|
|
2017-12-12 10:34:42 +00:00
|
|
|
let (score, garbage_count) = stream(reader);
|
|
|
|
println!("{}", score);
|
|
|
|
println!("{}", garbage_count);
|
2017-12-12 10:05:25 +00:00
|
|
|
}
|
|
|
|
|
2017-12-12 10:34:42 +00:00
|
|
|
fn stream<R>(reader: R) -> (usize, usize) where R: Read {
|
2017-12-12 10:05:25 +00:00
|
|
|
let mut state = State { group_depth: 0, flags: Flags::Normal };
|
|
|
|
let mut score = 0;
|
2017-12-12 10:34:42 +00:00
|
|
|
let mut garbage_count = 0;
|
2017-12-12 10:05:25 +00:00
|
|
|
|
|
|
|
for byte in reader.bytes() {
|
|
|
|
state = match (state.flags, byte.ok().and_then(|b| char::from_u32(b as u32))) {
|
|
|
|
(Flags::CancelNext, _) => State { group_depth: state.group_depth, flags: Flags::Normal },
|
|
|
|
(Flags::CancelNextInGarbage, _) => State { group_depth: state.group_depth, flags: Flags::InGarbage },
|
|
|
|
(Flags::Normal, Some('{')) => State { group_depth: state.group_depth + 1, flags: Flags::Normal },
|
|
|
|
(Flags::Normal, Some('}')) => {
|
|
|
|
score += state.group_depth;
|
|
|
|
State { group_depth: state.group_depth - 1, flags: Flags::Normal }
|
|
|
|
},
|
|
|
|
(Flags::Normal, Some('<')) => State { group_depth: state.group_depth, flags: Flags::InGarbage },
|
|
|
|
(Flags::InGarbage, Some('>')) => State { group_depth: state.group_depth, flags: Flags::Normal },
|
|
|
|
(Flags::InGarbage, Some('!')) => State { group_depth: state.group_depth, flags: Flags::CancelNextInGarbage },
|
2017-12-12 10:34:42 +00:00
|
|
|
(Flags::InGarbage, Some(_)) => {
|
|
|
|
garbage_count += 1;
|
|
|
|
State { group_depth: state.group_depth, flags: Flags::InGarbage }
|
|
|
|
},
|
2017-12-12 10:05:25 +00:00
|
|
|
(Flags::Normal, Some('!')) => State { group_depth: state.group_depth, flags: Flags::CancelNext },
|
|
|
|
(_, Some(_)) => state,
|
|
|
|
(_, None) => panic!("error reading/converting byte")
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-12-12 10:34:42 +00:00
|
|
|
(score, garbage_count)
|
2017-12-12 10:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// {}, score of 1.
|
|
|
|
#[test]
|
|
|
|
fn test_empty() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{}");
|
2017-12-12 10:34:42 +00:00
|
|
|
let (score, _) = stream(cursor);
|
|
|
|
assert_eq!(score, 1);
|
2017-12-12 10:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// {{{}}}, score of 1 + 2 + 3 = 6.
|
|
|
|
#[test]
|
|
|
|
fn test_empty_nested() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{{{}}}");
|
2017-12-12 10:34:42 +00:00
|
|
|
let (score, _) = stream(cursor);
|
|
|
|
assert_eq!(score, 6);
|
2017-12-12 10:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// {{},{}}, score of 1 + 2 + 2 = 5.
|
|
|
|
#[test]
|
|
|
|
fn test_empty_empty() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{{},{}}");
|
2017-12-12 10:34:42 +00:00
|
|
|
let (score, _) = stream(cursor);
|
|
|
|
assert_eq!(score, 5);
|
2017-12-12 10:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// {{{},{},{{}}}}, score of 1 + 2 + 3 + 3 + 3 + 4 = 16.
|
|
|
|
#[test]
|
|
|
|
fn test_empty_empty_nested_empty() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{{{},{},{{}}}}");
|
2017-12-12 10:34:42 +00:00
|
|
|
let (score, _) = stream(cursor);
|
|
|
|
assert_eq!(score, 16);
|
2017-12-12 10:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// {<a>,<a>,<a>,<a>}, score of 1.
|
|
|
|
#[test]
|
|
|
|
fn test_four_garbage() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{<a>,<a>,<a>,<a>}");
|
2017-12-12 10:34:42 +00:00
|
|
|
let (score, _) = stream(cursor);
|
|
|
|
assert_eq!(score, 1);
|
2017-12-12 10:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// {{<ab>},{<ab>},{<ab>},{<ab>}}, score of 1 + 2 + 2 + 2 + 2 = 9.
|
|
|
|
#[test]
|
|
|
|
fn test_groups_with_garbage() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{{<ab>},{<ab>},{<ab>},{<ab>}}");
|
2017-12-12 10:34:42 +00:00
|
|
|
let (score, _) = stream(cursor);
|
|
|
|
assert_eq!(score, 9);
|
2017-12-12 10:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// {{<!!>},{<!!>},{<!!>},{<!!>}}, score of 1 + 2 + 2 + 2 + 2 = 9.
|
|
|
|
#[test]
|
|
|
|
fn test_groups_with_cancellations() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{{<!!>},{<!!>},{<!!>},{<!!>}}");
|
2017-12-12 10:34:42 +00:00
|
|
|
let (score, _) = stream(cursor);
|
|
|
|
assert_eq!(score, 9);
|
2017-12-12 10:05:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// {{<a!>},{<a!>},{<a!>},{<ab>}}, score of 1 + 2 = 3.
|
|
|
|
#[test]
|
|
|
|
fn test_groups_with_garbage_exclamation() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{{<a!>},{<a!>},{<a!>},{<ab>}}");
|
2017-12-12 10:34:42 +00:00
|
|
|
let (score, _) = stream(cursor);
|
|
|
|
assert_eq!(score, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Part Two
|
|
|
|
|
|
|
|
// <>, 0 characters.
|
|
|
|
#[test]
|
|
|
|
fn test_garbage_empty() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{<>}");
|
|
|
|
let (_, garbage_count) = stream(cursor);
|
|
|
|
assert_eq!(garbage_count, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// <random characters>, 17 characters.
|
|
|
|
#[test]
|
|
|
|
fn test_garbage_count_random_characters() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{<random characters>}");
|
|
|
|
let (_, garbage_count) = stream(cursor);
|
|
|
|
assert_eq!(garbage_count, 17);
|
|
|
|
}
|
|
|
|
|
|
|
|
// <<<<>, 3 characters.
|
|
|
|
#[test]
|
|
|
|
fn test_garbage_repeated_start() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{<<<<>}");
|
|
|
|
let (_, garbage_count) = stream(cursor);
|
|
|
|
assert_eq!(garbage_count, 3);
|
2017-12-12 10:05:25 +00:00
|
|
|
}
|
|
|
|
|
2017-12-12 10:34:42 +00:00
|
|
|
// <{!>}>, 2 characters.
|
|
|
|
#[test]
|
|
|
|
fn test_garbage_cancel_one() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{<{!>}>}");
|
|
|
|
let (_, garbage_count) = stream(cursor);
|
|
|
|
assert_eq!(garbage_count, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// <!!>, 0 characters.
|
|
|
|
#[test]
|
|
|
|
fn test_garbage_cancel_cancel() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{<!!>}");
|
|
|
|
let (_, garbage_count) = stream(cursor);
|
|
|
|
assert_eq!(garbage_count, 0);
|
|
|
|
}
|
2017-12-12 10:05:25 +00:00
|
|
|
|
2017-12-12 10:34:42 +00:00
|
|
|
// <!!!>>, 0 characters.
|
|
|
|
#[test]
|
|
|
|
fn test_garbage_cancel_cancel_end() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{<!!!>>}");
|
|
|
|
let (_, garbage_count) = stream(cursor);
|
|
|
|
assert_eq!(garbage_count, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// <{o"i!a,<{i<a>, 10 characters.
|
|
|
|
#[test]
|
|
|
|
fn test_garbage_gibberish() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
|
|
|
|
let cursor = Cursor::new(b"{<{o\"i!a,<{i<a>}");
|
|
|
|
let (_, garbage_count) = stream(cursor);
|
|
|
|
assert_eq!(garbage_count, 10);
|
|
|
|
}
|