AOC 23 - Day 01 - Trebuchet?!

Link to Advent of Code day 1

This year, we'll have to help the Elves to fix the global snow production. We can do this by visiting the top fifty locations that are likely to be having problems.

For the first day of this year, we'll have to find the calibration values so the Elves can send us to the first destination with a trebuchet.

Part 1

Rules

For the first part, we need to extract a number from each line. Each number is composed from the first and the last digit on the line.

1abc2  
pqr3stu8vwx  
a1b2c3d4e5f  
treb7uchet  

In this example, the numbers from each line are 12, 38, 15 and 77. The line treb7uchet contains only one digit, 7. So it is considered both as the first and the last digit on this line, forming the number 77.

Code

We can do that easily by filtering the elements of the list with isdigit, and then use indexing to retrieve the first and last digit:

def part_one(input_file: str) -> int:
    calibration_values_sum = 0

    with open(input_file) as f:
        for line in f.readlines():
            line_digits = [char for char in line if char.isdigit()]
            calibration_values_sum += int(line_digits[0] + line_digits[-1])
    return calibration_values_sum

Part 2

Rules

Like for part one, we have to find the first and last digit but this time, the digit can now be expressed as words.

For example, the line two1nine is composed of 3 digits: 2, 1, 9.

Code

I first thought about using a recursive function to return an array of digits by iterating on the string slice by slice.

But then, I remembered that the first day is usually not that tricky, so I tried the simplest thing I could think about: replacing each word by its digit, and then applying the same logic as for part one. So I tried this function:

def convert_letters_to_digits(string: str) -> str:
    """ Replace each spelled letter in the string with its digit equivalent.

    Example:
        >>> convert_letters_to_digits("one2nine")
        "129"
    """
    digits = {
        "one": "1",
        "two": "2",
        "three": "3",
        "four": "4",
        "five": "5",
        "six": "6",
        "seven": "7",
        "eight": "8",
        "nine": "9",
    }
    for word, digit in digits.items():
        string = string.replace(word, digit)
    return string

... But it didn't work.

In the example, there is the line eightwothree. With the function I used, the first digit to be replaced it two, and thus the result is eigh23, making interpreting the first eight impossible.

I started fiddling with the recursive function I talked about earlier, when I realized there not that many cases of overlapping numbers. So I just added the corner cases to the dictionary, which now looks like this:

digits = {
        "twone": "21",
        "oneight": "18",
        "eightwo": "82",
        "eighthree": "83",
        "sevenine": "79",
        "one": "1",
        "two": "2",
        "three": "3",
        "four": "4",
        "five": "5",
        "six": "6",
        "seven": "7",
        "eight": "8",
        "nine": "9",
        "zero": "0",
    }

And it worked ! While keeping the code short enough.


All the code is available on github