Advent of Code 2022
Let's use the Advent of Code 2022, a series of programming challenges of increasing difficulty, to learn more about the Rust programming language.
Series overview
Two years ago, I did part of Advent of Code 2020 using the Rust language. It was a lot of fun, so let's try it again!
The problem statement
Our input looks something like this:
1000 2000 3000 4000 5000 6000 7000 8000 9000 10000
Each group of lines separated by an empty line is a list of food items an elf is carrying: each line corresponds to the number of calories in that food.
Part 1
In the day 2 challenge, we're playing Rock Papers Scissors.
We're given a strategy guide like so:
A Y B X C Z
Left column is "their move": A means Rock, B means Paper, C means Scissors. Right column is "our move": X means Rock, Y means Paper, Z means Scissors.
Each line corresponds to a turn, and we must calculate the total score we get. Picking "Rock" gives 1 point, "Paper" gives 2 points, and "Scissors" gives 3. Losing the round gives 0 points, drawing gives 3, winning it gives 6.
Part 1
I'm not sure where the day 3 challenge is going, because the problem statement for the first part is kinda convoluted.
As usual we have an input, like this:
vJrwpWtwJgWrhcsFMMfFFhFp jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL PmmdzqPrVvPwwTWBwg wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn ttgJtRGJQctTZtZT CrZsJsPPZsGzwwsLwLmpwMDw
Each line represents the contents of a "rucksack", divided in two halves (which are called "compartments"), so for line 1:
Part 1
Let's tackle the day 4 challenge!
In this one, we get an input like this:
2-4,6-8 2-3,4-5 5-7,7-9 2-8,3-7 6-6,4-6 2-6,4-8
Each line has two ranges: the first line has ranges containing 2, 3, 4, and 6, 7, 8. We must count how many pairs have ranges where one fully contains the other.
In Rust, we can express this with "inclusive ranges" (), and those implement , so we can do:
Part 1
The day 5 challenge actually looks fun!
Our input looks like this:
[D] [N] [C] [Z] [M] [P] 1 2 3 move 1 from 2 to 1 move 3 from 1 to 3 move 2 from 2 to 1 move 1 from 1 to 2
Which is a visual representation of stacks, and so, for once, we have some serious parsing to do, and that means I finally have a good reason to bust out the nom crate.
Today I am joining you from the relative discomfort of my living room (since my better half has commandeered the home office due to Way Too Many Calls) to tackle the day 6 challenge, which I'm excited about: maybe despite, maybe because of, the low-grade fever I'm under.
Part 1
Our input is a jumble of letters, and we're supposed to find the position of the first substring that's "four different characters".
The day 7 challenge talks about trees! File trees that is.
The temptation to solve it before starting to write this article so I don't look silly is high, but I'm explicitly not doing so, so that we can bang our collective heads against any walls at the same time, and see how we can get out of it! Trees are serious business!
Part 1
The sample input looks like this:
In the day 8 problem, our input is a height map:
30373 25512 65332 33549 35390
This is a 5x5 grid, and every number denotes the height of a tree. For part 1, we must find out how many trees are visible from the outside of the grid.
If we consider the first row, from the left: only the 3
is visible: it
obscures the 0
. From the right, 3
and 7
are visible.
The Advent of Code is not a sprint: it's a marathon: sometimes you've got to stop and smell the roses.
I... what? That's not.. have you done a marathon before?
No, and I haven't taken any creative writing classes either, I think you can tell. Anyway: Day 8 was a bit aggravating for me. In 2020 I gave up AoC after Day 14 I think, and then I skipped a year. It doesn't help that it overlaps some holidays and stuff, but!
Onwards! To the day 10 puzzle.
I don't see a way to make part 1 especially fun — so let's just get to it.
Parsing
As usual, let's reach for the nom crate...
$ cargo add nom@7 (cut)
...to parse the input into nicely-organized Rust data structures:
// in `src/main.rs` use nom::{ branch::alt, bytes::complete::tag combinatormap value sequencepreceded IResult Noop Addx -> noop = addx = nomcharactercompletei32 Noop noop addx Addxi -> Noop => Addx_ =>
It's a new day, it's a new advent of code puzzle.
In that one, we have to apparently cosplay as an IBM mainframe and just.. crunch them numbers. This doesn't look fun, and I can't think of a clever twist to make it fun, so let's try to make it short and sweet.
Parsing
Our input looks like this:
Monkey 0: Starting items Operation new = old Test divisible by If true throw to monkey If false throw to monkey Monkey Starting items Operation new = old + Test divisible by If true throw to monkey If false throw to monkey etc
Alright! The day 12 puzzle involves path finding, and it seems like a good time to lean more heavily on the WASM embeds I've set up for the previous parts.
Let's start by setting up the types we'll want!
Types and parsing
Our input is a heightmap, like so:
Sabqponm abcryxxl accszExk acctuvwj abdefghi
Where 'a'..='z'
is a square with a given elevation (from lowest to highest),
is the start, and is the end.
The day 13 puzzle needs a speech therapist.
???
...because it has an awful lisp!! Ahhhahahahhhh
Are you ok? What is.. what is going on with you?
No but seriously we have what are ostensibly S-expressions, except they use JSON-adjacent notation:
[1,1,3,1,1] [1,1,5,1,1] [[1],[2,3,4]] [[1],4] [9] [[8,7,6]] [[4,4],4,4] [[4,4],4,4,4] [7,7,7,7] [7,7,7] [] [3] [[[]]] [[]] [1,[2,[3,[4,[5,6,7]]]],8,9] [1,[2,[3,[4,[5,6,0]]]],8,9]
I like how the day 14 puzzle sounds, because I think it'll give me an opportunity to show off yet another way to have Rust embedded in a web page.
But first...
Let me guess: parsing?
You bet your furry ass, parsing.
Parsing
The input looks something like this:
498,4 -> 498,6 -> 496,6 503,4 -> 502,4 -> 502,9 -> 494,9
And each line is essentially... a polyline: we're supposed to draw lines between every point on the path, and that determines rocks on the map.
The day 15 puzzle falls into the "math puzzle" territory more than "let's learn something new about Rust", but since several folks asked if I was going to continue... let's continue.
The sample input is as follows:
Sensor at x=2, y=18: closest beacon is at x=-2, y=15 Sensor at x=9, y=16: closest beacon is at x=10, y=16 Sensor at x=13, y=2: closest beacon is at x=15, y=3 Sensor at x=12, y=14: closest beacon is at x=10, y=16 Sensor at x=10, y=20: closest beacon is at x=10, y=16 Sensor at x=14, y=17: closest beacon is at x=10, y=16 Sensor at x=8, y=7: closest beacon is at x=2, y=10 Sensor at x=2, y=0: closest beacon is at x=2, y=10 Sensor at x=0, y=11: closest beacon is at x=2, y=10 Sensor at x=20, y=14: closest beacon is at x=25, y=17 Sensor at x=17, y=20: closest beacon is at x=21, y=22 Sensor at x=16, y=7: closest beacon is at x=15, y=3 Sensor at x=14, y=3: closest beacon is at x=15, y=3 Sensor at x=20, y=1: closest beacon is at x=15, y=3
Let's tackle the day 16 puzzle!
Parsing
The input looks like this:
Valve AA has flow rate=0; tunnels lead to valves DD, II, BB Valve BB has flow rate=13; tunnels lead to valves CC, AA Valve CC has flow rate=2; tunnels lead to valves DD, BB Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE Valve EE has flow rate=3; tunnels lead to valves FF, DD Valve FF has flow rate=0; tunnels lead to valves EE, GG Valve GG has flow rate=0; tunnels lead to valves FF, HH Valve HH has flow rate=22; tunnel leads to valve GG Valve II has flow rate=0; tunnels lead to valves AA, JJ Valve JJ has flow rate=21; tunnel leads to valve II
Advent of Code gets harder and harder, and I'm not getting any smarter. Or any more free time. So, in order to close out this series anyway, I'm going to try and port other people's solutions from "language X" to Rust. That way, they already figured out the hard stuff, and we can just focus on the Rust bits!
Sounds good? Good. Let's proceed.
This time around, we're porting a solution from C++ to Rust and seeing how it feels, how it performs, and what we can learn about both languages by doing that.
See Day 17 for the rationale re: porting solutions rather than writing my own from scratch. TL;DR is: it's better than nothing, and we can still focus about learning Rust rather than spending entire days fighting off-by-one errors.
This series is complete.