mirror of
https://github.com/Smaug123/advent-of-code-2017
synced 2025-10-07 20:48:40 +00:00
Day 9
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -257,6 +257,13 @@ version = "0.1.0"
|
|||||||
name = "day_8"
|
name = "day_8"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day_9"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"criterion",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
|
@@ -8,6 +8,7 @@ members = [
|
|||||||
"day_6",
|
"day_6",
|
||||||
"day_7",
|
"day_7",
|
||||||
"day_8",
|
"day_8",
|
||||||
|
"day_9",
|
||||||
"day_10",
|
"day_10",
|
||||||
"day_11",
|
"day_11",
|
||||||
"day_12",
|
"day_12",
|
||||||
|
15
day_9/Cargo.toml
Normal file
15
day_9/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "day_9"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Smaug123 <patrick+github@patrickstevens.co.uk>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
[dev-dependencies]
|
||||||
|
criterion = "0.3"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "day_9"
|
||||||
|
harness = false
|
19
day_9/benches/day_9.rs
Normal file
19
day_9/benches/day_9.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
use day_9::day_9::{input, part_1, part_2};
|
||||||
|
|
||||||
|
fn criterion_benchmark(c: &mut Criterion) {
|
||||||
|
let input = input();
|
||||||
|
c.bench_function("day 9 part 1", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
part_1(&input);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
c.bench_function("day 9 part 2", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
part_2(&input);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, criterion_benchmark);
|
||||||
|
criterion_main!(benches);
|
1
day_9/input.txt
Normal file
1
day_9/input.txt
Normal file
File diff suppressed because one or more lines are too long
247
day_9/src/lib.rs
Normal file
247
day_9/src/lib.rs
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
pub mod day_9 {
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
pub(crate) struct GroupIndex {
|
||||||
|
pub(crate) i: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Group {
|
||||||
|
pub(crate) entries: Vec<GroupIndex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum GroupEntry<'a> {
|
||||||
|
Garbage(&'a str),
|
||||||
|
Group(Group),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Stream<'a> {
|
||||||
|
pub(crate) groups: Vec<GroupEntry<'a>>,
|
||||||
|
pub(crate) head_group: GroupIndex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stream<'_> {
|
||||||
|
fn cata_inner<'a, 'b, T>(
|
||||||
|
entries: &'b [GroupEntry<'a>],
|
||||||
|
head: &GroupIndex,
|
||||||
|
depth: usize,
|
||||||
|
at_garbage: fn(&'a str) -> T,
|
||||||
|
combine: fn(&mut dyn Iterator<Item = T>, usize) -> T,
|
||||||
|
) -> T
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
{
|
||||||
|
let e = &entries[head.i];
|
||||||
|
match e {
|
||||||
|
GroupEntry::Garbage(s) => at_garbage(s),
|
||||||
|
GroupEntry::Group(Group { entries: e }) => {
|
||||||
|
let mut v = e
|
||||||
|
.iter()
|
||||||
|
.map(|i| Stream::cata_inner(entries, i, depth + 1, at_garbage, combine));
|
||||||
|
combine(&mut v, depth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cata<'a, 'b, T>(
|
||||||
|
s: &'b Stream,
|
||||||
|
at_garbage: fn(&'a str) -> T,
|
||||||
|
combine: fn(&mut dyn Iterator<Item = T>, usize) -> T,
|
||||||
|
) -> T
|
||||||
|
where
|
||||||
|
'b: 'a,
|
||||||
|
T: 'static,
|
||||||
|
{
|
||||||
|
Stream::cata_inner(&s.groups, &s.head_group, 0, at_garbage, combine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse<'a>(s: &'a str) -> Stream<'a> {
|
||||||
|
let mut iter = s.chars().enumerate();
|
||||||
|
match iter.next().unwrap() {
|
||||||
|
(_, '{') => {}
|
||||||
|
(_, c) => {
|
||||||
|
panic!("Group didn't start with open-brace, but '{}'!", c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut groups: Vec<GroupEntry<'a>> = vec![];
|
||||||
|
let mut stack: Vec<Vec<GroupIndex>> = vec![vec![]];
|
||||||
|
|
||||||
|
let mut garbage = None;
|
||||||
|
let mut skip = false;
|
||||||
|
|
||||||
|
for (pos, c) in iter {
|
||||||
|
if let Some(start) = garbage {
|
||||||
|
if skip {
|
||||||
|
skip = false;
|
||||||
|
} else {
|
||||||
|
match c {
|
||||||
|
'!' => {
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
'>' => {
|
||||||
|
groups.push(GroupEntry::Garbage(&s[start + 1..pos]));
|
||||||
|
let constructing_group = stack.len() - 1;
|
||||||
|
stack[constructing_group].push(GroupIndex {
|
||||||
|
i: groups.len() - 1,
|
||||||
|
});
|
||||||
|
garbage = None;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match c {
|
||||||
|
'<' => {
|
||||||
|
// New garbage. Consume up until the next non-cancelled '>'.
|
||||||
|
garbage = Some(pos);
|
||||||
|
}
|
||||||
|
'{' => {
|
||||||
|
// New group. Consume up to the next non-cancelled '}'.
|
||||||
|
stack.push(vec![]);
|
||||||
|
}
|
||||||
|
'}' => {
|
||||||
|
let entries = stack.pop().unwrap();
|
||||||
|
groups.push(GroupEntry::Group(Group { entries }));
|
||||||
|
match stack.last_mut() {
|
||||||
|
None => {}
|
||||||
|
Some(l) => {
|
||||||
|
l.push(GroupIndex {
|
||||||
|
i: groups.len() - 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
',' => {}
|
||||||
|
c => {
|
||||||
|
panic!("Expected a known character, got: {}", c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let max = groups.len() - 1;
|
||||||
|
Stream {
|
||||||
|
groups,
|
||||||
|
head_group: GroupIndex { i: max },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn input() -> Stream<'static> {
|
||||||
|
let input = include_str!("../input.txt");
|
||||||
|
parse(input.trim())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_1(numbers: &Stream) -> usize {
|
||||||
|
Stream::cata(
|
||||||
|
numbers,
|
||||||
|
|_| 0,
|
||||||
|
|v, depth| {
|
||||||
|
let mut sum = depth + 1;
|
||||||
|
for i in v {
|
||||||
|
sum += i;
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn part_2(numbers: &Stream) -> u32 {
|
||||||
|
Stream::cata(
|
||||||
|
numbers,
|
||||||
|
|g| {
|
||||||
|
let mut skip = false;
|
||||||
|
let mut ans = 0;
|
||||||
|
for c in g.chars() {
|
||||||
|
if skip {
|
||||||
|
skip = false;
|
||||||
|
} else {
|
||||||
|
match c {
|
||||||
|
'!' => {
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
ans += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ans
|
||||||
|
},
|
||||||
|
|v, _| {
|
||||||
|
let mut sum = 0;
|
||||||
|
for i in v {
|
||||||
|
sum += i;
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::day_9::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_empty_parse() {
|
||||||
|
let result = parse("{}");
|
||||||
|
assert_eq!(result.groups.len(), 1);
|
||||||
|
match &result.groups[0] {
|
||||||
|
GroupEntry::Group(Group { entries: e }) => {
|
||||||
|
assert_eq!(e.len(), 0);
|
||||||
|
}
|
||||||
|
e => {
|
||||||
|
panic!("Wrong match: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(result.head_group, GroupIndex { i: 0 });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_triple_empty_parse() {
|
||||||
|
let result = parse("{{{}}}");
|
||||||
|
assert_eq!(result.groups.len(), 3);
|
||||||
|
let (zero_count, one_count) = Stream::cata(
|
||||||
|
&result,
|
||||||
|
|_| panic!("No garbage"),
|
||||||
|
|i, _| {
|
||||||
|
let (mut zero_count, mut one_count) = (0, 0);
|
||||||
|
let mut my_count = 0u32;
|
||||||
|
for (z, o) in i {
|
||||||
|
zero_count += z;
|
||||||
|
one_count += o;
|
||||||
|
my_count += 1;
|
||||||
|
}
|
||||||
|
match my_count {
|
||||||
|
0 => (zero_count + 1, one_count),
|
||||||
|
1 => (zero_count, one_count + 1),
|
||||||
|
l => {
|
||||||
|
panic!("Unexpected count: {}", l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert_eq!(zero_count, 1);
|
||||||
|
assert_eq!(one_count, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn part_1_known() {
|
||||||
|
assert_eq!(part_1(&parse("{}")), 1);
|
||||||
|
assert_eq!(part_1(&parse("{{{}}}")), 6);
|
||||||
|
assert_eq!(part_1(&parse("{{},{}}")), 5);
|
||||||
|
assert_eq!(part_1(&parse("{{{},{},{{}}}}")), 16);
|
||||||
|
assert_eq!(part_1(&parse("{<a>,<a>,<a>,<a>}")), 1);
|
||||||
|
assert_eq!(part_1(&parse("{{<ab>},{<ab>},{<ab>},{<ab>}}")), 9);
|
||||||
|
assert_eq!(part_1(&parse("{{<!!>},{<!!>},{<!!>},{<!!>}}")), 9);
|
||||||
|
assert_eq!(part_1(&parse("{{<a!>},{<a!>},{<a!>},{<ab>}}")), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_day_9() {
|
||||||
|
let input = input();
|
||||||
|
assert_eq!(part_1(&input), 16869);
|
||||||
|
assert_eq!(part_2(&input), 7284);
|
||||||
|
}
|
||||||
|
}
|
7
day_9/src/main.rs
Normal file
7
day_9/src/main.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use day_9::day_9;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = day_9::input();
|
||||||
|
println!("part 1 => {}", day_9::part_1(&input));
|
||||||
|
println!("part 2 => {}", day_9::part_2(&input));
|
||||||
|
}
|
Reference in New Issue
Block a user