Advertisement
cybertestpilot

AOC - Python - Day 18

Dec 18th, 2020
716
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.95 KB | None | 0 0
  1. """
  2. --- Day 18: Operation Order ---
  3. """
  4.  
  5. import re
  6. import doctest
  7.  
  8. from aocd import get_data  # https://github.com/wimglenn/advent-of-code-data
  9. day = 18
  10. year = 2020
  11.  
  12. def match_parens(str):
  13.     """
  14.    Get all the parenthesis depths for a given string
  15.    """
  16.     depth = 0
  17.     tmp = [0,] * len(str)
  18.     for index, char in enumerate(str):
  19.         if char == "(":
  20.             depth += 1
  21.             tmp[index] = depth
  22.         elif char == ")":
  23.             tmp[index] = depth
  24.             depth -= 1
  25.     return tmp
  26.  
  27. def calculate(line, advanced=False):
  28.     """
  29.    Calculate the elf math total of the provided line
  30.    :param line: the line as read from the inout file
  31.    :param advanced: should addition happen before multiplication?
  32.    :return: the calculated total
  33.    """
  34.     parens = match_parens(line)
  35.     depth = max(parens)
  36.     if depth == 0:
  37.         return calculate_left_to_right(line, advanced)
  38.     else:
  39.         depth_index = parens.index(max(parens))
  40.         closing = parens[depth_index+1:].index(depth)
  41.         value = calculate_left_to_right(line[depth_index+1: depth_index+closing+1], advanced)
  42.         str_replace = line[depth_index: depth_index+closing+2]
  43.         line = line.replace(str_replace, str(value))
  44.         return calculate(line, advanced)
  45.    
  46.  
  47. def calculate_left_to_right(eq, advanced=False):
  48.     """
  49.    Calculate a given sequence as either strict left to right or if advanced than left to right with addition
  50.    being performed first.
  51.    :param eq: the immediate equation to be calculated
  52.    :param advanced: addition performed BEFORE multiplication?
  53.    :return: the calculated value
  54.    """
  55.     total = 0
  56.  
  57.     numbers = [int(x) for x in re.split(r"\*|\+", eq)]  # get all the numbers
  58.     operands = [x for x in re.split(r"[0-9]", eq) if x != '']   # get all the operands
  59.  
  60.     if not advanced:
  61.         total = numbers[0]
  62.         for i in range(1, len(numbers)):
  63.             operand = operands[i-1]
  64.             right = numbers[i]
  65.             if operand == "+":
  66.                 total += right
  67.             elif operand == "*":
  68.                 total *= right
  69.         return total
  70.  
  71.     else:
  72.         # Calculate left to right EXCEPT with + first
  73.         total = 0
  74.  
  75.         while "+" in operands:
  76.             numbers, operands = find_perform_addition(numbers, operands)
  77.  
  78.         if len(operands) == 0:
  79.             total = numbers[0]
  80.             return total
  81.  
  82.         else:
  83.             total = numbers[0]
  84.             for i in range(1, len(numbers)):
  85.                 total *= numbers[i]
  86.             return total
  87.  
  88.  
  89. def find_perform_addition(numbers, operands):
  90.     numbers_copy = numbers.copy()
  91.     operands_copy = operands.copy()
  92.  
  93.     for index, op in enumerate(operands):
  94.         if op == "+":
  95.             x = numbers_copy.pop(index)
  96.             y = numbers_copy.pop(index)
  97.             numbers_copy.insert(index, x+y)
  98.             operands_copy.pop(index)
  99.             return (numbers_copy, operands_copy)
  100.     return (numbers_copy, operands_copy)
  101.  
  102.  
  103. def part_a(data):
  104.     """
  105.    >>> line = "((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2".replace(" ", "")
  106.    >>> calculate(line)
  107.    13632
  108.    """
  109.  
  110.     total = 0
  111.     for line in data:
  112.         total += calculate(line.replace(" ", ""))
  113.     print("Part A total: {:d}".format(total))
  114.  
  115.  
  116. def part_b(data):
  117.     """
  118.    >>> line = "1 + (2 * 3) + (4 * (5 + 6))".replace(" ", "")
  119.    >>> calculate(line, True)
  120.    51
  121.    >>> line = "5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))".replace(" ", "")
  122.    >>> calculate(line, True)
  123.    669060
  124.    >>> line = "((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2".replace(" ", "")
  125.    >>> calculate(line, True)
  126.    23340
  127.    """
  128.  
  129.     total = 0
  130.     for line in data:
  131.         total += calculate(line.replace(" ", ""), True)
  132.     print("Part B total: {:d}".format(total))
  133.  
  134.  
  135. if __name__ == "__main__":
  136.     doctest.testmod()
  137.  
  138.     data = get_data(day=day, year=year).splitlines()
  139.     part_a(data)
  140.     part_b(data)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement