Add day 9 solution

This commit is contained in:
Wesley Moore 2017-12-12 21:05:25 +11:00
parent 2960b6bea6
commit cc4b2d86b0
5 changed files with 182 additions and 0 deletions

4
2017/day/9/Cargo.lock generated Normal file
View file

@ -0,0 +1,4 @@
[[package]]
name = "day9"
version = "0.1.0"

6
2017/day/9/Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[package]
name = "day9"
version = "0.1.0"
authors = ["Wesley Moore <wes@wezm.net>"]
[dependencies]

1
2017/day/9/input Normal file

File diff suppressed because one or more lines are too long

46
2017/day/9/problem.txt Normal file
View file

@ -0,0 +1,46 @@
--- Day 9: Stream Processing ---
A large stream blocks your path. According to the locals, it's not safe to cross the stream at the moment because it's full of garbage. You look down at the stream; rather than water, you discover that it's a stream of characters.
You sit for a while and record part of the stream (your puzzle input). The characters represent groups - sequences that begin with { and end with }. Within a group, there are zero or more other things, separated by commas: either another group or garbage. Since groups can contain other groups, a } only closes the most-recently-opened unclosed group - that is, they are nestable. Your puzzle input represents a single, large group which itself contains many smaller ones.
Sometimes, instead of a group, you will find garbage. Garbage begins with < and ends with >. Between those angle brackets, almost any character can appear, including { and }. Within garbage, < has no special meaning.
In a futile attempt to clean up the garbage, some program has canceled some of the characters within it using !: inside garbage, any character that comes after ! should be ignored, including <, >, and even another !.
You don't see any characters that deviate from these rules. Outside garbage, you only find well-formed groups, and garbage always terminates according to the rules above.
Here are some self-contained pieces of garbage:
<>, empty garbage.
<random characters>, garbage containing random characters.
<<<<>, because the extra < are ignored.
<{!>}>, because the first > is canceled.
<!!>, because the second ! is canceled, allowing the > to terminate the garbage.
<!!!>>, because the second ! and the first > are canceled.
<{o"i!a,<{i<a>, which ends at the first >.
Here are some examples of whole streams and the number of groups they contain:
{}, 1 group.
{{{}}}, 3 groups.
{{},{}}, also 3 groups.
{{{},{},{{}}}}, 6 groups.
{<{},{},{{}}>}, 1 group (which itself contains garbage).
{<a>,<a>,<a>,<a>}, 1 group.
{{<a>},{<a>},{<a>},{<a>}}, 5 groups.
{{<!>},{<!>},{<!>},{<a>}}, 2 groups (since all but the last > are canceled).
Your goal is to find the total score for all groups in your input. Each group is assigned a score which is one more than the score of the group that immediately contains it. (The outermost group gets a score of 1.)
{}, score of 1.
{{{}}}, score of 1 + 2 + 3 = 6.
{{},{}}, score of 1 + 2 + 2 = 5.
{{{},{},{{}}}}, score of 1 + 2 + 3 + 3 + 3 + 4 = 16.
{<a>,<a>,<a>,<a>}, score of 1.
{{<ab>},{<ab>},{<ab>},{<ab>}}, score of 1 + 2 + 2 + 2 + 2 = 9.
{{<!!>},{<!!>},{<!!>},{<!!>}}, score of 1 + 2 + 2 + 2 + 2 = 9.
{{<a!>},{<a!>},{<a!>},{<ab>}}, score of 1 + 2 = 3.
What is the total score for all groups in your input?

125
2017/day/9/src/main.rs Normal file
View file

@ -0,0 +1,125 @@
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);
println!("{}", stream(reader));
}
fn stream<R>(reader: R) -> usize where R: Read {
let mut state = State { group_depth: 0, flags: Flags::Normal };
let mut score = 0;
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 },
(Flags::Normal, Some('!')) => State { group_depth: state.group_depth, flags: Flags::CancelNext },
(_, Some(_)) => state,
(_, None) => panic!("error reading/converting byte")
};
// println!("{:?}", state);
}
score
}
// {}, score of 1.
#[test]
fn test_empty() {
use std::io::Cursor;
let cursor = Cursor::new(b"{}");
assert_eq!(stream(cursor), 1);
}
// {{{}}}, score of 1 + 2 + 3 = 6.
#[test]
fn test_empty_nested() {
use std::io::Cursor;
let cursor = Cursor::new(b"{{{}}}");
assert_eq!(stream(cursor), 6);
}
// {{},{}}, score of 1 + 2 + 2 = 5.
#[test]
fn test_empty_empty() {
use std::io::Cursor;
let cursor = Cursor::new(b"{{},{}}");
assert_eq!(stream(cursor), 5);
}
// {{{},{},{{}}}}, 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"{{{},{},{{}}}}");
assert_eq!(stream(cursor), 16);
}
// {<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>}");
assert_eq!(stream(cursor), 1);
}
// {{<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>}}");
assert_eq!(stream(cursor), 9);
}
// {{<!!>},{<!!>},{<!!>},{<!!>}}, score of 1 + 2 + 2 + 2 + 2 = 9.
#[test]
fn test_groups_with_cancellations() {
use std::io::Cursor;
let cursor = Cursor::new(b"{{<!!>},{<!!>},{<!!>},{<!!>}}");
assert_eq!(stream(cursor), 9);
}
// {{<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>}}");
assert_eq!(stream(cursor), 3);
}