day 18


today's puzzle is doing maths with a different order of operations

usual input stuff:

# input
with open('18.txt', 'r') as file:
    input = file.read()
# turn the input into a list
input_list = list(input.split('\n'))

the rules for the first part are:

  • if you come across brackets, evaluate the expression in the brackets first
  • otherwise, just do everything left to right

my first thought for this was just 'maths go brrr' so.. i wrote a recursive function called go_brrr. with a little bit of googling i discovered the eval function which takes in a string as if it were code, so eval('2 + 3') returns 5, which made life a lot easier for this.

first we count the number of open brackets.

if there are no brackets, then we count the number of times each operation shows up - num_plus for '+' and num_times for '*'. if either of these values is 0, then we can just evaluate the expression because addition and multiplication are each associative (a + (b + c) = (a + b) + c). otherwise, we have both addition and multiplication, so we evaluate the outcome of the first operation by splitting on the first three spaces, applying the first operation, and then calling go_brrr again - for example, '12 * 4 + 5 * 7' would become ['12', '*', '4', '+ 5 * 7'], and then we run go_brrr('48 + 5 * 7').

if there are brackets, we want to grab an expression in an innermost bracket and evaluate it - this can be done by first taking the first instance of a close bracket ')' and splitting on this. for example, '((2 * 9 + 4 + 5) * 5) + 2' would become ['((2 * 9 + 4 + 5', ' * 5) + 2']. we would then split the first string in this list on the last instance of an open bracket (rsplit was really handy for this), so '((2 * 9 + 4 + 5' would become ['(', '2 * 9 + 4 + 5'].

using these, we can get the expression before the brackets, the expression in the brackets, and the expression after the brackets separately - in this case, we would get

before_expr = '('
expr = '2 * 9 + 4 + 5'
after_expr = ' * 5) + 2'

then we run go_brrr on expr, since this will run in the no-brackets case, giving 27, then put all of the strings together to give '(27 * 5) + 2', and then run go_brrr on this.

def go_brrr(string):
    num_brackets = len([char for char in string if char == '('])
    if num_brackets == 0:
        num_plus = len([char for char in string if char == '+'])
        num_times = len([char for char in string if char == '*'])
        if num_times == 0:
            return eval(string)
        elif num_plus == 0:
            return eval(string)
        else:
            ns = string.split(' ',3)
            return go_brrr(str(eval(ns[0]+ns[1]+ns[2])) + ' ' + ns[3])
    else: # we have brackets
        # get the expression between the first close bracket
        # and the last open bracket preceding it
        tmp1 = string.split(')',1)
        tmp2 = tmp1[0].rsplit('(',1)
        before_expr = ''.join(tmp2[:-1])
        expr = tmp2[-1]
        after_expr = ''.join(tmp1[1:])
        return go_brrr(before_expr + str(go_brrr(expr)) + after_expr)

we now just need to call this on each line in input_list. the output we want is the sum of all of the expressions calculated with the order of operations given.

sum = 0
for line in input_list:
    sum += int(go_brrr(line))
print(sum)

for the second part, we get an additional rule: prioritising addition over multiplication. with the implementation above, the only section of code that needs to change is evaluating an expression with both addition and multiplication and no brackets. for this, i did the following:

  • split on the first instance of ' + ' to get tmp1
  • split the string to the left (tmp1[0]) on the last ' ' to get tmp2l
  • split the string to the right (tmp1[1]) on the first ' ' to get tmp2r

some examples of the original string and tmp2l, tmp2r:

'3 * 4 + 8 + 5' -> ['3 *', '4'], ['8', '+ 5']
'5 * 11 * 3 + 6' -> ['5 * 11 *', '3'], ['6']
'4 + 3 * 5 * 6' -> ['4'], ['3', '* 5 * 6']

we then want to add together the two summands, which are the last element of tmp2l and the first element of tmp2r, put this value between what remains of the other two lists (probably should've popped from the list instead of slicing, but i didn't notice any performance issues so i didn't change it), and then evaluate go_brrr on the new string.

my code for this part was the same but with the clause that you hit if num_brackets == 0, num_times =/= 0, and num_plus =/= 0 replaced by this:

tmp1 = string.split(' + ',1)
tmp2l = tmp1[0].rsplit(' ', 1)
tmp2r = tmp1[1].split(' ',1)
tmp3 = tmp2l[:-1] + [' ', str(eval(tmp2l[-1] + ' + ' + tmp2r[0])), ' '] + tmp2r[1:]
return go_brrr(''.join(tmp3))

this one wasn't too bad, but as a mathematician it low-key hurt my soul

Files

18a.py 1.7 kB
Dec 18, 2020
18b.py 1.9 kB
Dec 18, 2020

Get aoc 2020

Leave a comment

Log in with itch.io to leave a comment.