Skip to content

Commit 6cde2bb

Browse files
committed
Year 2015 Day 12
1 parent 536776f commit 6cde2bb

File tree

8 files changed

+138
-0
lines changed

8 files changed

+138
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,4 @@ pie
247247
| 9 | [All in a Single Night](https://adventofcode.com/2015/day/9) | [Source](src/year2015/day09.rs) | 35 |
248248
| 10 | [Elves Look, Elves Say](https://adventofcode.com/2015/day/10) | [Source](src/year2015/day10.rs) | 14 |
249249
| 11 | [Corporate Policy](https://adventofcode.com/2015/day/11) | [Source](src/year2015/day11.rs) | 1 |
250+
| 12 | [JSAbacusFramework.io](https://adventofcode.com/2015/day/12) | [Source](src/year2015/day12.rs) | 80 |

benches/benchmark.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ mod year2015 {
4747
benchmark!(year2015, day09);
4848
benchmark!(year2015, day10);
4949
benchmark!(year2015, day11);
50+
benchmark!(year2015, day12);
5051
}
5152

5253
mod year2019 {

input/year2015/day12.txt

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ pub mod year2015 {
171171
pub mod day09;
172172
pub mod day10;
173173
pub mod day11;
174+
pub mod day12;
174175
}
175176

176177
/// # Rescue Santa from deep space with a solar system adventure.

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ fn all_solutions() -> Vec<Solution> {
8787
solution!(year2015, day09),
8888
solution!(year2015, day10),
8989
solution!(year2015, day11),
90+
solution!(year2015, day12),
9091
// 2019
9192
solution!(year2019, day01),
9293
solution!(year2019, day02),

src/year2015/day12.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//! # JSAbacusFramework.io
2+
//!
3+
//! ## Part One
4+
//!
5+
//! The utility [`iter_signed`] method extracts numbers from surrounding text and is used directly.
6+
//!
7+
//! ## Part Two
8+
//!
9+
//! We build a tiny custom JSON parser using a
10+
//! [parser combinator](https://en.wikipedia.org/wiki/Parser_combinator) approach,
11+
//! making some simplifying assumptions:
12+
//! * The input is always well formed and does not contain any whitespace.
13+
//! * Arrays and objects contain at least one item.
14+
//! * We don't care about the content of strings, only if they equal "red" or not.
15+
//!
16+
//! Each parsing function returns a [`Result`] struct which has 3 fields:
17+
//! * `next` The index of the character *after* this object. For example parsing "123," returns
18+
//! a value of 3 for next.
19+
//! * `ignore`: Only true for strings that exactly equals "red", false otherwise and always
20+
//! false for numbers, arrays and objects.
21+
//! * `value`: For numbers the literal value, for string zero, for arrays the sum of child
22+
//! items, for objects the sum of child items if no "red" property is present, otherwise zero.
23+
//!
24+
//! [`iter_signed`]: crate::util::parse
25+
use crate::util::parse::*;
26+
27+
const RED: &[u8] = "red".as_bytes();
28+
29+
struct Result {
30+
next: usize,
31+
ignore: bool,
32+
value: i32,
33+
}
34+
35+
pub fn parse(input: &str) -> &str {
36+
input
37+
}
38+
39+
pub fn part1(input: &str) -> i32 {
40+
input.iter_signed::<i32>().sum()
41+
}
42+
43+
pub fn part2(input: &str) -> i32 {
44+
parse_json(input.as_bytes(), 0).value
45+
}
46+
47+
/// Parse JSON that has no whitespace.
48+
fn parse_json(input: &[u8], start: usize) -> Result {
49+
match input[start] {
50+
b'[' => parse_array(input, start),
51+
b'{' => parse_object(input, start),
52+
b'"' => parse_string(input, start),
53+
_ => parse_number(input, start),
54+
}
55+
}
56+
57+
/// Parse array assuming it contains at least one element.
58+
fn parse_array(input: &[u8], start: usize) -> Result {
59+
let mut index = start;
60+
let mut total = 0;
61+
62+
while input[index] != b']' {
63+
let Result { next, value, .. } = parse_json(input, index + 1);
64+
index = next;
65+
total += value;
66+
}
67+
68+
Result { next: index + 1, ignore: false, value: total }
69+
}
70+
71+
/// Parse object assuming it contains at least one key/value pair.
72+
fn parse_object(input: &[u8], start: usize) -> Result {
73+
let mut index = start;
74+
let mut total = 0;
75+
let mut ignore = false;
76+
77+
while input[index] != b'}' {
78+
let Result { next: first, .. } = parse_json(input, index + 1);
79+
let Result { next: second, ignore: red, value } = parse_json(input, first + 1);
80+
index = second;
81+
total += value;
82+
ignore |= red;
83+
}
84+
85+
Result { next: index + 1, ignore: false, value: if ignore { 0 } else { total } }
86+
}
87+
88+
/// Parse a string evaluating only if it equals "red".
89+
fn parse_string(input: &[u8], start: usize) -> Result {
90+
let start = start + 1;
91+
let mut end = start;
92+
93+
while input[end] != b'"' {
94+
end += 1;
95+
}
96+
97+
Result { next: end + 1, ignore: &input[start..end] == RED, value: 0 }
98+
}
99+
100+
/// Parse an integer value.
101+
fn parse_number(input: &[u8], start: usize) -> Result {
102+
let mut end = start;
103+
let mut neg = false;
104+
let mut acc = 0;
105+
106+
if input[end] == b'-' {
107+
neg = true;
108+
end += 1;
109+
}
110+
111+
while input[end].is_ascii_digit() {
112+
acc = 10 * acc + (input[end] - b'0') as i32;
113+
end += 1;
114+
}
115+
116+
Result { next: end, ignore: false, value: if neg { -acc } else { acc } }
117+
}

tests/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ mod year2015 {
4040
mod day09_test;
4141
mod day10_test;
4242
mod day11_test;
43+
mod day12_test;
4344
}
4445

4546
mod year2019 {

tests/year2015/day12_test.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use aoc::year2015::day12::*;
2+
3+
const EXAMPLE: &str = r#"[1,{"c":"red","b":2},3]"#;
4+
5+
#[test]
6+
fn part1_test() {
7+
let input = parse(EXAMPLE);
8+
assert_eq!(part1(input), 6);
9+
}
10+
11+
#[test]
12+
fn part2_test() {
13+
let input = parse(EXAMPLE);
14+
assert_eq!(part2(input), 4);
15+
}

0 commit comments

Comments
 (0)