skip to content
Andy Baizon

Advent of Code 2023: Day 9

Solving Advent of Code day 9 - Scanning the oasis, or, is it really that simple?

[WARNING] this post contains spoilers

The Premise

Today’s challenge is to create a prediction for both the past and future out of a series of readings. Each line is it’s own history, so we need to bolt a number onto the beginning, and end of each line (past and future, respectively).

After a couple of cups of coffee, and shaking the dust out of the part of my brain that does math, it seemed like a nice one. I still haven’t figured out what the difficulty pattern is this year.

Solving Part 1

All in all, part one is faily simple. Grab the input, then iterate over the lines, building a Vector of Vectors of numbers out of the line values. Then we add a new Vec to the parent Vec (readings), where each value is the difference between the each of the values of the Vector before. We keep going until the line we’re adding is all 0’s .

fn part_1(input: &str) -> String {
    let mut totals: Vec<i32> = Vec::new();
    for line in input.lines() {
        let mut readings: Vec<Vec<i32>> =
            Vec::from([line.split(' ').map(|x| x.parse::<i32>().unwrap()).collect()]);
        loop {
            let r: Vec<i32> = readings
                .last()
                .unwrap()
                .windows(2)
                .map(|vals| vals[1] - vals[0])
                .collect();
            let zeroed: bool = r.iter().all(|x| *x == 0);
            readings.push(r);
            if zeroed {
                break
            }
        }

Next, we need to calculate our predictions for the line. To make the math easier, we’ll invert the parent array, so the first element will be a Vector of 0‘s. To actually do the math, we’ll iterate through the readings we took, grab the last value from each line, and add it to the one before it.

This gets pushed to our totals Vector, until every line is processed. Once we have all the values, we sum them all for the answer.

        readings.reverse();
        let v: i32 = readings.iter()
            .map(|vals| *vals.last().unwrap())
            .reduce(|acc, val| acc + val)
            .unwrap();
        totals.push(v);
    }
    let sum: i32 = totals.iter().sum();
    format!("{}", sum)
}

Tests for Part 1

Tests for part one are doing a sample line, then the sample input, and comparing the values.

    #[test]
    fn sample_input_one_line() {
        let answer = part_1("0 3 6 9 12 15");
        assert_eq!(answer, "18".to_string())
    }

    #[test]
    fn sample_input() {
        let answer = part_1(
            "0 3 6 9 12 15
1 3 6 10 15 21
10 13 16 21 30 45",
        );
        assert_eq!(answer, "114".to_string())
    }

Solving Part 2

Part two is a cakewalk. Take the same readings, grab the first value instead of the last and do some minus’ing.

  let v: i32 = readings.iter()
      .map(|vals| *vals.first().unwrap())
      .reduce(|acc, val| val - acc)
      .unwrap();

Tests for Part 2

Tests for part two are again, just checking a sample line, then the sample input, and comparing values.

    #[test]
    fn sample_input_one_line() {
        let answer = part_2("10 13 16 21 30 45");
        assert_eq!(answer, "5".to_string())
    }

    #[test]
    fn sample_input() {
        let answer = part_2(
            "0 3 6 9 12 15
1 3 6 10 15 21
10 13 16 21 30 45",
        );
        assert_eq!(answer, "2".to_string())
    }

The complete solution for day 9 requires no external crates