Hey guys! Today, we're diving into Advent of Code 2023, specifically Day 1, Part 2. If you tackled Part 1, get ready for a twist! This time, it's not just about finding the first and last digit; we've got textual numbers to contend with. Fun, right? Let's break down the challenge and nail this puzzle together.

    Understanding the Challenge: Textual Digits

    In this part of the challenge, we need to consider cases where numbers are spelled out as words like "one", "two", "three", etc., in addition to the regular numeric digits. The goal remains the same: find the first and last digit in each line of the input and combine them to form a two-digit number. The catch is that these digits can now be represented either as numerals or as words. This introduces a new layer of complexity because we need to identify and convert these text representations into their numerical equivalents.

    For example, consider the following lines:

    • two1nine
    • eightwothree
    • abcone2threexyz
    • xtwone3four
    • 4nineeightseven2
    • zoneight234
    • 7pqrstsixteen

    In the first line, two1nine, the first digit is "two" which is 2, and the last digit is "nine" which is 9. So, the combined number is 29.

    In the second line, eightwothree, the first digit is "eight" which is 8, and the last digit is "three" which is 3. So, the combined number is 83.

    In the third line, abcone2threexyz, the first digit is "one" which is 1, and the last digit is "three" which is 3. So, the combined number is 13.

    In the fourth line, xtwone3four, the first digit is "two" which is 2, and the last digit is "four" which is 4. So, the combined number is 24.

    In the fifth line, 4nineeightseven2, the first digit is 4, and the last digit is 2. So, the combined number is 42.

    In the sixth line, zoneight234, the first digit is "one" which is 1, and the last digit is 4. So, the combined number is 14.

    In the seventh line, 7pqrstsixteen, the first digit is 7, and the last digit is "six" which is 6. So, the combined number is 76.

    The ultimate goal is to process each line, extract these combined numbers, and then sum them up to get the final answer. This task requires a bit more cleverness in identifying the digits since we need to handle both numeric and alphabetic representations.

    Breaking Down the Solution

    To solve this puzzle effectively, we can break it down into the following steps:

    1. Read the Input: Start by reading the input data, where each line contains a string that may include digits and/or number words.
    2. Identify Digits (Numeric and Textual): For each line, search for the first and last digit. This involves checking for both numeric characters ('0' to '9') and number words ("one", "two", ..., "nine").
    3. Convert Textual Digits to Numeric: When a number word is found, convert it to its corresponding numeric value. For example, convert "one" to 1, "two" to 2, and so on.
    4. Combine the First and Last Digits: Once you have the first and last digits (in numeric form), combine them to form a two-digit number. The first digit is multiplied by 10 and then added to the last digit.
    5. Sum the Numbers: Keep a running total of these two-digit numbers. After processing all lines, the final sum is the answer to the puzzle.

    Key Techniques and Considerations

    • Overlapping Number Words: Be careful with overlapping number words, such as in the string "eightwo." The word "eight" is followed immediately by "two." Your algorithm should correctly identify both numbers.
    • Order of Operations: Ensure that you're capturing the very first and very last occurrences of digits (either numeric or textual) in each line.
    • Edge Cases: Consider lines where the same number word appears multiple times or lines that contain only number words or only numeric digits.

    Crafting the Code: A Practical Approach

    Let's outline a Python-based approach to tackle this puzzle. We'll create a function that processes each line and extracts the necessary digits.

    import re
    
    def find_calibration_value(line):
        """Finds the calibration value of a line by identifying the first and last digit.
        Digits can be represented numerically or as text.
        """
    
        # Dictionary to map number words to their numeric values
        number_words = {
            'one': '1',
            'two': '2',
            'three': '3',
            'four': '4',
            'five': '5',
            'six': '6',
            'seven': '7',
            'eight': '8',
            'nine': '9'
        }
    
        # Create a regex pattern that matches either a digit or a number word
        pattern = r'(\d|' + '|'.join(number_words.keys()) + r')'
    
        # Find all matches in the line
        matches = re.findall(pattern, line)
    
        # If no matches are found, return 0
        if not matches:
            return 0
    
        # Get the first and last match
        first_match = matches[0]
        last_match = matches[-1]
    
        # Convert the matches to digits if they are number words
        first_digit = number_words.get(first_match, first_match)
        last_digit = number_words.get(last_match, last_match)
    
        # Combine the digits to form the calibration value
        calibration_value = int(first_digit + last_digit)
    
        return calibration_value
    
    
    def sum_calibration_values(input_data):
        """Sums the calibration values from a list of input strings."""
        total_sum = 0
        for line in input_data:
            total_sum += find_calibration_value(line)
        return total_sum
    
    # Example usage:
    input_data = [
        "two1nine",
        "eightwothree",
        "abcone2threexyz",
        "xtwone3four",
        "4nineeightseven2",
        "zoneight234",
        "7pqrstsixteen"
    ]
    
    result = sum_calibration_values(input_data)
    print(result) # Output should be 281
    

    Code Explanation

    1. find_calibration_value(line) Function:

      • Takes a line of text as input.
      • Defines a dictionary number_words to map text representations of numbers to their corresponding digit characters.
      • Constructs a regular expression pattern to find either numeric digits (\d) or any of the number words.
      • Uses re.findall(pattern, line) to find all matches of the pattern in the input line.
      • If no matches are found, it returns 0.
      • Retrieves the first and last matches from the list of matches.
      • If the matches are number words, it converts them to their numeric equivalents using the number_words dictionary.
      • Combines the first and last digits to form a two-digit number and returns it as an integer.
    2. sum_calibration_values(input_data) Function:

      • Takes a list of input strings (input_data).
      • Iterates through each line in the input data and calls find_calibration_value to get the calibration value for each line.
      • Accumulates the calibration values into total_sum.
      • Returns the total sum of all calibration values.
    3. Example Usage:

      • Defines a list of input strings input_data.
      • Calls sum_calibration_values with the input data to calculate the sum of calibration values.
      • Prints the result, which should be 281 for the given example input.

    This code snippet efficiently identifies both numeric digits and number words in each line, converts them to their numeric values, and computes the sum of the combined first and last digits.

    Debugging and Testing

    Debugging is super important. Use print statements or a debugger to see what's happening at each step. Make sure your regex is catching all the right stuff and that your conversions are spot-on. Create more test cases, especially ones with tricky overlaps or edge cases, to ensure your solution is robust.

    Optimizing for Performance

    If you're dealing with a massive dataset, you might want to think about performance. Regular expressions can sometimes be slow, so explore alternative methods if needed. Caching frequently used values or optimizing your search algorithm can also help.

    Conclusion: You Nailed It!

    So, there you have it! Day 1, Part 2 of Advent of Code 2023, cracked. Remember, the key is breaking down the problem, handling those tricky text digits, and testing thoroughly. Keep coding, keep learning, and I'll catch you in the next puzzle. Good luck, and have fun!