1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
//! # [Day 12: JSAbacusFramework.io](https://adventofcode.com/2015/day/12)
//!
//! Santa's Accounting-Elves need help balancing the books after a recent order.
//! Unfortunately, their accounting software uses a peculiar storage format.
//! That's where you come in.
//!
//! They have a [JSON](http://json.org/) document which contains a variety of things:
//! arrays (`[1,2,3]`), objects (`{"a":1, "b":2}`), numbers, and strings.
//! Your first job is to simply find all of the numbers throughout the document
//! and add them together.
//!
//! For example:
//!
//! - `[1,2,3]` and `{"a":2,"b":4}` both have a sum of `6`.
//! - `[[[3]]]` and `{"a":{"b":4},"c":-1}` both have a sum of `3`.
//! - `{"a":[-1,1]}` and `[-1,{"a":1}]` both have a sum of `0`.
//! - `[]` and `{}` both have a sum of `0`.
//!
//! You will not encounter any strings containing numbers.
//!
//! **What is the sum of all numbers in the document?**
//!
//! # Part Two
//!
//! Uh oh - the Accounting-Elves have realized that they double-counted everything red.
//!
//! **Ignore any object (and all of its children) which has any property with the value `"red"`.
//! Do this only for objects (`{...}`), not arrays (`[...]`).**
//!
//! - `[1,2,3]` still has a sum of `6`.
//! - `[1,{"c":"red","b":2},3]` now has a sum of `4`, because the middle object is ignored.
//! - `{"d":"red","e":[1,2,3,4],"f":5}` now has a sum of `0`, because the entire structure is ignored.
//! - `[1,"red",5]` has a sum of `6`, because `"red"` in an array has no effect.
use serde_json::Value;
#[aoc_generator(day12)]
fn parse_input(input: &str) -> anyhow::Result<Value> {
Ok(serde_json::from_str(input)?)
}
/// Part 1: What is the sum of all numbers in the document?
#[aoc(day12, part1)]
fn part1(input: &Value) -> i64 {
count(input)
}
/// Part 2: Ignore any object (and all of its children) which has any property with the value `"red"`.
/// Do this only for objects (`{...}`), not arrays (`[...]`).
#[aoc(day12, part2)]
fn part2(input: &Value) -> i64 {
count_no_red(input)
}
fn count(value: &Value) -> i64 {
match value {
Value::Number(n) => n.as_i64().unwrap(),
Value::Array(arr) => arr.iter().map(count).sum(),
Value::Object(object) => object.values().map(count).sum(),
_ => 0,
}
}
fn count_no_red(value: &Value) -> i64 {
match value {
Value::Number(n) => n.as_i64().unwrap(),
Value::Array(arr) => arr.iter().map(count_no_red).sum(),
Value::Object(object) => {
let mut sum = 0;
for value in object.values() {
if let Value::String(value) = value {
if value == "red" {
return 0;
}
}
sum += count_no_red(value)
}
sum
}
_ => 0,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn part1_examples() {
// `[1,2,3]` and `{"a":2,"b":4}` both have a sum of `6`.
assert_eq!(count(&json!([1, 2, 3])), 6);
assert_eq!(count(&json!( {"a":2,"b":4})), 6);
// `[[[3]]]` and `{"a":{"b":4},"c":-1}` both have a sum of `3`.
assert_eq!(count(&json!([[[3]]])), 3);
assert_eq!(count(&json!({"a":{"b":4},"c":-1})), 3);
// `{"a":[-1,1]}` and `[-1,{"a":1}]` both have a sum of `0`.
assert_eq!(count(&json!( {"a":[-1,1]})), 0);
assert_eq!(count(&json!( [-1,{"a":1}])), 0);
// `[]` and `{}` both have a sum of `0`.
assert_eq!(count(&json!([])), 0);
assert_eq!(count(&json!({})), 0);
}
#[test]
fn part2_examples() {
// `[1,2,3]` still has a sum of `6`.
assert_eq!(count_no_red(&json!([1, 2, 3])), 6);
// `[1,{"c":"red","b":2},3]` now has a sum of `4`, because the middle object is ignored.
assert_eq!(count_no_red(&json!([1,{"c":"red","b":2},3])), 4);
// `{"d":"red","e":[1,2,3,4],"f":5}` now has a sum of `0`, because the entire structure is ignored.
assert_eq!(count_no_red(&json!({"d":"red","e":[1,2,3,4],"f":5})), 0);
// `[1,"red",5]` has a sum of `6`, because `"red"` in an array has no effect.
assert_eq!(count_no_red(&json!([1, "red", 5])), 6);
}
}