Day 1: Trebuchet?!

AdventuRust - AoC 2023

This is Day 1 of Advent of Code 2023! If you would like to solve the problems before looking at the solution, you can find them here.

In part 1 of Day 1, we're looking to grab the first and last decimal digits from each line. We want to take those digits and put them together to make a two-digit decimal number. The plan is to add up all these numbers from every line to get our end result. This is my first time writing rust code for real - I've only played around with Rust by Example before. I'm using some boilerplate code to read the file using the read_lines() function, and then I'm iterating over the input. For each line in the file, I'm using the get_first() function that uses a regular expression to obtain the first digit in the line.

The regex used here is "^([^0-9]*)([0-9])(.*)$". Let's take a look at what that does.

  • ^ Asserts the start of the string.
  • ([^0-9]*) Capturing group that matches zero or more occurrences of any character that is not a digit (0-9). The ^ inside the square brackets negates the character class, meaning it matches anything that is not a digit. The * quantifier allows for zero or more occurrences.
  • ([0-9]) Capturing group that matches a single digit (0-9).
  • (.*) Capturing group that matches zero or more occurrences of any character (except for a newline).
  • $ Asserts the end of the string.

In summary, the regex captures three groups: non-digits at the beginning of the string, a single digit, and the rest of the string. We use the second group as our first digit. Next, instead of finding the last digit in the line, I reverse the line and obtain the first digit in that line. Summing both up gives us our answer for part 1. Click here to jump to the part 2 discussion.

For part 2 of Day 1, digits can now be represented in words, making it a bit more difficult to parse. I'm using a HashMap, Rust's equivalent to a Python dict to store word to digit mapping in both the forward and backward direction. The reverse mapping allows us to keep using the reverse functionality that we were using in part 1 to find the last digit in the line. The regex is slightly different this time - I realized that I didn't need to check if the first group was non-digits if I use a lazy quantifier ?. The regex used here is "^(.*?)([0-9]|one|two|three|four|five|six|seven|eight|nine)(.*)$" for the forward direction. Let's take a look at what that does.

  • ^ Asserts the start of the string.
  • (.*?) The .*? is a non-greedy quantifier that matches any character (except for a newline) zero or more times, but as few times as possible. This is a capturing group that represents any characters (or none) at the beginning of the string.
  • ([0-9]|one|two|three|four|five|six|seven|eight|nine) This is another capturing group that matches either a single digit (0-9) or one of the words "one," "two," "three," "four," "five," "six," "seven," "eight," or "nine."
  • (.*) Another capturing group that matches any characters (or none) for the rest of the string.
  • $ Asserts the end of the string.

In summary, the regex captures three main groups: the initial characters, the digit (or one of the spelled-out numbers), and the remaining characters. The rest is the same as part 1.

That's all folks! If you made it this far, enjoy some AI art generated by u/dimpopo using the prompt for this puzzle.

Cheers,
Devang