Advent of Code 2023: Day #3

For this day you’re given an input file similar to the example given:

467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..

and need to find the sum of all numbers adjacent to a symbol. This can be on the same line or a line above and below. Diagonal also counts so 467 would be a valid number to include in the sum. In this example the only numbers that should not be included in this sum are 114 and 58. To go about solving this I first parsed the input file into 2 lists. One with the numbers and their locations, and the other with the symbols locations. To do this I decided to use regular expressions from pythons re library to quickly find digits and symbols. Creating a regular expression for matching digits is easy and can be done with:

digits = re.compile(r'\d+')

Which will match any instance of 1 or more digits in a row. For the symbols, just looking over the example it appeared anything besides digits and periods could be a valid symbol. To match this I used the inverse of a set of values as:

symbols = re.compile(r'[^\d\.]+')

Here the ^ in the beginning of the set [] means to match to symbols not in the set. There it will not match to digits or periods. To use these regular expression patterns I loop through each line in the input file for the first match of both digits and symbols and, while I continue to find them, add them to their corresponding lists. Since it finds the first occurrence each time it searches the list I need to remove previously matched results. In code this all looks like:

import re

file1 = open('p1_input.txt','r')
#file1 = open('p1_input.example','r')

Lines = file1.read().splitlines()

digits = re.compile(r'\d+')
symbols = re.compile(r'[^\d\.]+')

digits_loc = []
symbols_loc = []

for line in Lines:
    tmp = []
    while True:
        found = re.search(digits,line)
        if found:
            tmp.append(found)
            start, end = found.span()
            line = line[0:start] + ('.' * (end - start)) + line[end:]
        else:
            break
    digits_loc.append(tmp)
    tmp = []
    while True:
        found = re.search(symbols,line)
        if found:
            start, end = found.span()
            tmp.append(start)
            line = line[0:start] + '.' + line[end:]
        else:
            break
    symbols_loc.append(tmp)

Where I’ve also added break conditions to exit the While True loop when no more patterns are found. After finding all the locations for the digits and symbols to be I just needed to create code to find if they are adjacent to one another. To do this I iterated through all found digits in each line and if a symbol was within the digits starting location – 1 or its ending location + 1 on the current line, or the previous or next lines (if they exist) add the digit to the sum. In code this looks like:

ans = 0
for index, nums in enumerate(digits_loc):
    for num in nums:
        loc = num.span()
        loc = (loc[0]-1,loc[1]+1)
        '''Check current line first for symbols then check previous and next line'''
        for symbol in symbols_loc[index]:
            if symbol in range(loc[0],loc[1]):
                ans += int(num[0])
        if index > 0:
            for symbol in symbols_loc[index-1]:
                if symbol in range(loc[0],loc[1]):
                    ans += int(num[0])
        if index < len(digits_loc)-1:
            for symbol in symbols_loc[index+1]:
                if symbol in range(loc[0],loc[1]):
                    ans += int(num[0])

print(ans)

Running the full code given below on the given input, gives a value of 520135.

import re

file1 = open('p1_input.txt','r')
#file1 = open('p1_input.example','r')

Lines = file1.read().splitlines()

digits = re.compile(r'\d+')
symbols = re.compile(r'[^\d\.]+')

digits_loc = []
symbols_loc = []

for line in Lines:
    tmp = []
    while True:
        found = re.search(digits,line)
        if found:
            tmp.append(found)
            start, end = found.span()
            line = line[0:start] + ('.' * (end - start)) + line[end:]
        else:
            break
    digits_loc.append(tmp)
    tmp = []
    while True:
        found = re.search(symbols,line)
        if found:
            start, end = found.span()
            tmp.append(start)
            line = line[0:start] + '.' + line[end:]
        else:
            break
    symbols_loc.append(tmp)

ans = 0
for index, nums in enumerate(digits_loc):
    for num in nums:
        loc = num.span()
        loc = (loc[0]-1,loc[1]+1)
        '''Check current line first for symbols then check previous and next line'''
        for symbol in symbols_loc[index]:
            if symbol in range(loc[0],loc[1]):
                ans += int(num[0])
        if index > 0:
            for symbol in symbols_loc[index-1]:
                if symbol in range(loc[0],loc[1]):
                    ans += int(num[0])
        if index < len(digits_loc)-1:
            for symbol in symbols_loc[index+1]:
                if symbol in range(loc[0],loc[1]):
                    ans += int(num[0])

print(ans)

Part 2:

For part 2 you’re tasked with finding the sum of all products of two numbers adjacent to *. Since the symbol is now specific I just adjusted the symbols regular expression pattern to match only *.

symbols = re.compile(r'\*')

The second part of the code where I summed up the values adjacent to symbols also needs some tweaking to solve part 2. Instead of summing up digits adjacent to symbols I’m adding them as a tuple to a running list. The tuple also contains the digits line in the input file and the symbols location. Then I create two loops; one over the full list of tuples and one starting from the next element in the outer loop to the end of the list of tuples and check to see if the elements share the same line number and symbol location. If they do the digits are adjacent to the same symbol and should be multiplied together and added to the running some. The reason both loops don’t run over the full list of tuples is this would result in adding both elements multiplied together twice. This is implemented in code as:

gear_adj = []
for index, nums in enumerate(digits_loc):
    for num in nums:
        loc = num.span()
        loc = (loc[0]-1,loc[1]+1)
        '''Check current line first for symbols then check previous and next line'''
        for symbol in symbols_loc[index]:
            if symbol in range(loc[0],loc[1]):
                gear_adj.append((int(num[0]),index,symbol))
        if index > 0:
            for symbol in symbols_loc[index-1]:
                if symbol in range(loc[0],loc[1]):
                    gear_adj.append((int(num[0]),index-1,symbol))
        if index < len(digits_loc)-1:
            for symbol in symbols_loc[index+1]:
                if symbol in range(loc[0],loc[1]):
                    gear_adj.append((int(num[0]),index+1,symbol))

ans = 0
for index, s1 in enumerate(gear_adj):
    for s2 in gear_adj[index+1:]:
        if s1[1] == s2[1] and s1[2] == s2[2]:
            ans += s1[0] * s2[0]

The full python code is available below as well as on github and when run gives an answer for this challenge of 72514855.

import re

file1 = open('p1_input.txt','r')
#file1 = open('p1_input.example','r')

Lines = file1.read().splitlines()

digits = re.compile(r'\d+')
symbols = re.compile(r'\*')

digits_loc = []
symbols_loc = []

for index, line in enumerate(Lines):
    tmp = []
    while True:
        found = re.search(digits,line)
        if found:
            tmp.append(found)
            start, end = found.span()
            line = line[0:start] + ('.' * (end - start)) + line[end:]
        else:
            break
    digits_loc.append(tmp)
    tmp = []
    while True:
        found = re.search(symbols,line)
        if found:
            start, end = found.span()
            tmp.append(start)
            line = line[0:start] + '.' + line[end:]
        else:
            break
    symbols_loc.append(tmp)

gear_adj = []
for index, nums in enumerate(digits_loc):
    for num in nums:
        loc = num.span()
        loc = (loc[0]-1,loc[1]+1)
        '''Check current line first for symbols then check previous and next line'''
        for symbol in symbols_loc[index]:
            if symbol in range(loc[0],loc[1]):
                gear_adj.append((int(num[0]),index,symbol))
        if index > 0:
            for symbol in symbols_loc[index-1]:
                if symbol in range(loc[0],loc[1]):
                    gear_adj.append((int(num[0]),index-1,symbol))
        if index < len(digits_loc)-1:
            for symbol in symbols_loc[index+1]:
                if symbol in range(loc[0],loc[1]):
                    gear_adj.append((int(num[0]),index+1,symbol))

ans = 0
for index, s1 in enumerate(gear_adj):
    for s2 in gear_adj[index+1:]:
        if s1[1] == s2[1] and s1[2] == s2[2]:
            ans += s1[0] * s2[0]

print(ans)
Tags: No tags

Add a Comment

Your email address will not be published. Required fields are marked *