AoC 2022 Day 1: Calorinator

Source: Calorie Counting

Part 1

Given multiple lists of numbers, find the list with the largest sum.

Stylistically, I want to ‘over engineer’ all of the solutions this year, making structs and functions on those structs that act as they ‘should’. We’ll see how that goes. For this problem, the lists in the input represent Elves with a list of snacks with so many calories.

So we’ll make an Elf:

#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)]
struct Elf {
    calories: i32,
}

impl Elf {
    fn new() -> Self {
        Elf{calories: 0}
    }
}

I know that I need the elves to be sortable (since I want the largest), thus Eq, PartialEq, Ord, and PartialOrd. Once that’s in place, we want a function to read an input file into a list of Elves:

fn read(filename: &Path) -> Vec<Elf> {
    let mut elves = Vec::new();
    let mut current = Elf::new();

    for line in read_lines(filename) {
        if line.len() == 0 {
            elves.push(current);
            current = Elf::new();
        } else {
            current.calories += line.parse::<i32>().unwrap();
        }
    }
    elves.push(current);

    return elves;
}

And with all that boilerplate out of the way, it’s not much code at all to solve part 1:

fn part1(filename: &Path) -> String {
    let elves = read(filename);
    elves
        .iter()
        .max()
        .expect("no Elves found, can't take max")
        .calories
        .to_string()
}

The expect never actually fires, since we always have at least 1 Elf with 0 calories, but so it goes.

Using our test function:

#[test]   
fn test1() { aoc_test("01", part1, "70369") }

And it’s of course crazy fast:

$ cargo build --bin 01-calorinator --release

   Compiling aoc2022 v0.1.0 (/Users/jp/Projects/advent-of-code/2022)
    Finished release [optimized] target(s) in 0.43s

$ ./target/release/01-calorinator 1 data/01.txt

70369
took 1.065458ms

Part 2

Find the total of the three lists with the individual largest sums.

That’s kind of neat. I feel like implementing Sum should do it. (Yes, I could just explicitly get the top 3). Unfortunately, I can’t get that one for free. But it’s clean enough to write:

impl Sum for Elf {
    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
        let mut calories = 0;
        for elf in iter {
            calories += elf.calories;
        }
        Elf{calories}
    }
}

And then getting the 3 largest and summing them:

fn part2(filename: &Path) -> String {
    let mut elves = read(filename);
    
    elves.sort();
    elves.reverse();

    elves.into_iter().take(3).sum::<Elf>().calories.to_string()
}

And running it:

$ ./target/release/01-calorinator 2 data/01.txt

203002
took 421.041µs

Cool. Onward!