Hero Image
- Philipp Ludewig

Advent of Code 2021 Day 2

Aloha people,

it's day two already, and today I have to pilot the submarine. How cool is that? I believe this year Santa will end up in Atlantis. The puzzle for today is super simple, and it reminds me of the interview coding exercise I had to do when I applied for a job at ThoughtWorks. The puzzle input for today is similar to those lines:

forward 5
down 5
forward 8
up 3
down 8
forward 2

There are three different commands: forward, up and down. All of them adjust the two attributes of the submarine: depth and horizontal position a little differently. Important is that up means decreasing the depth and down increases the depth. So far so easy to understand. Yet again I am working with the Rust language to solve the puzzle. It was super helpful yesterday, maybe it also has a neat support method for today's exercise.

The first part to solve this puzzle is to translate the lines to understandable commands for the machine. For that I am using an enum that contains the commands and the values. In the get_command() method I split the line at the whitespace to get the command word and the value. For matching the strings to the enum I am using match and let the statement panic!() if something would go wrong. That will never be the case though. That the enum implements the PartialEq and Debug trait is only for the tests.

#[derive(PartialEq, Debug)]
enum Command {
    Forward(i32),
    Up(i32),
    Down(i32),
}

fn get_command(input: &String) -> Command {
    let mut split = input.split(" ");
    let command_string = split.next().unwrap();
    let value = split.next().unwrap().parse().unwrap();
    return match command_string {
        "forward" => Command::Forward(value),
        "up" => Command::Up(value),
        "down" => Command::Down(value),
        _ => panic!(),
    };
}

After finishing the translation of the input I can finally solve part one. The match comes in handy again and very quickly I got my puzzle solution.

pub fn get_solution_for_part_one(input_lines: &Vec<String>) -> i32 {
    let mut horizontal_position = 0;
    let mut depth = 0;

    for line in input_lines {
        let command = get_command(line);
        match command {
            Command::Forward(correction) => {
                horizontal_position += correction;
            }
            Command::Up(correction) => depth -= correction,
            Command::Down(correction) => depth += correction,
        }
    }

    return depth * horizontal_position;
}

The second part of today's puzzle only introduces a new variable to the calculation. Still super simple. I am wondering how hard tomorrow is going to be. Oh boy!

I adjusted the calculation and bam I am done.

pub fn get_solution_for_part_two(input_lines: &Vec<String>) -> i32 {
    let mut horizontal_position = 0;
    let mut depth = 0;
    let mut aim = 0;
    for line in input_lines {
        let command = get_command(line);
        match command {
            Command::Forward(correction) => {
                horizontal_position += correction;
                depth += correction * aim;
            }
            Command::Up(correction) => aim -= correction,
            Command::Down(correction) => aim += correction,
        }
    }

    return depth * horizontal_position;
}

So to be honest that was a really quick one. I still want to learn something each day and this wasn't enough for me. Yesterday there was windows(n) but what today? I tell ya: file structures.....Whaaat? Yes I was curious about the benchmark tool I saw in a recent Rust workshop and tried to use it. Nothing worked and I had no idea what was happening. The project file structure looked like this:

adc_d2
├── Cargo.toml
└─┬ src
  ├── main.rs
  ├─┬ benches
  │ └── benchmark.rs
  └─┬ tests
    └── main_test.rs

When you read my post from yesterday then you may remember how I said that IntelliJ makes it so easy to create a Rust project. Well it created a project for a binary with a main.rs but what I needed is a lib.rs. Only with the methods in the lib.rs was I able to import them in my benchmark.rs file. I consulted Stack Overflow for some time and then just played around till I got it. This took longer than I want to admit. 😅 See for yourself how it turned out.

Ok let;s talk about SPEED. If you want to have awesome benchmarks of your Rust code you can add the following lines to your Cargo.toml file:

[dev-dependencies]
criterion = { version = "0.3", features = ["html_reports"] }

[[bench]]
name = "benchmarks"
harness = false

The criterion crate enables you to run benchmarks over your code, and it will generate an elegant summary in the /target/criterion/report/index.html. I am benchmarking each part of the puzzle separate from each other and together. If you want to know more about the crate go to its homepage.

use adc_d2::util::get_input_lines_without_buffer;
use adc_d2::{get_solution_for_part_one, get_solution_for_part_two};
use criterion::{black_box, criterion_group, criterion_main, Criterion};

pub fn day_two_benchmark(c: &mut Criterion) {
    c.bench_function("day_two", |b| {
        b.iter(|| {
            let input_lines = get_input_lines_without_buffer();
            get_solution_for_part_one(black_box(&input_lines));
            get_solution_for_part_two(black_box(&input_lines))
        })
    });
}

pub fn day_two_part_one_benchmark(c: &mut Criterion) {
    c.bench_function("day_two part 1", |b| {
        b.iter(|| {
            let input_lines = get_input_lines_without_buffer();
            get_solution_for_part_one(black_box(&input_lines));
        })
    });
}

pub fn day_two_part_two_benchmark(c: &mut Criterion) {
    c.bench_function("day_two part 2", |b| {
        b.iter(|| {
            let input_lines = get_input_lines_without_buffer();
            get_solution_for_part_two(black_box(&input_lines));
        })
    });
}

criterion_group!(
    benches,
    day_two_benchmark,
    day_two_part_one_benchmark,
    day_two_part_two_benchmark
);
criterion_main!(benches);

Well that's it for today's puzzle. Learning about the benchmark tool and how to structure a Rust project was cool. I am curious about tomorrow as I read a Reddit post from the puzzle author Eric Wastl yesterday in which he explained how the puzzles are created and tested. They involve several people to find out how hard the puzzles are for each person. In the post he then describes how the puzzles are sorted so that not too many hard puzzles are on subsequent days and vice versa. Long text, short meaning: Tomorrow is going to be hard!