--- Day 12: JSAbacusFramework.io ---

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 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.

Rust Solution

use rustgym_util::*;
use serde_json::json;
use serde_json::Value;
use std::fmt::Write;
use std::io::*;

pub fn solve(reader: &mut dyn BufRead, writer: &mut dyn Write) {
    let mut it = reader.lines().map(|l| l.unwrap());
    let line = it.next().unwrap();
    let val = serde_json::from_str(&line).unwrap();
    let res1 = dfs1(&val);
    let red: Value = json!("red");
    let res2 = dfs2(&val, &red);
    writeln!(writer, "{}", res1).unwrap();
    writeln!(writer, "{}", res2).unwrap();
}

fn dfs1(val: &Value) -> i64 {
    match val {
        Value::Number(num) => num.as_i64().unwrap(),
        Value::Object(obj) => obj.iter().map(|(_, v)| dfs1(v)).sum(),
        Value::Array(arr) => arr.iter().map(|v| dfs1(v)).sum(),
        _ => 0,
    }
}

fn dfs2(val: &Value, red: &Value) -> i64 {
    match val {
        Value::Number(num) => num.as_i64().unwrap(),
        Value::Object(obj) => {
            if obj.iter().any(|(_, v)| v == red) {
                0
            } else {
                obj.iter().map(|(_, v)| dfs2(v, red)).sum()
            }
        }
        Value::Array(arr) => arr.iter().map(|v| dfs2(v, red)).sum(),
        _ => 0,
    }
}

advent_of_code!(test, "input.txt", "output.txt");

Having problems with this solution? Click here to submit an issue on github.