Advertisement
alexandrajay2002

Advent of Code 2024 day 13 part 2

Dec 13th, 2024
45
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.23 KB | Source Code | 0 0
  1. from fractions import Fraction
  2. from sys import argv
  3.  
  4.  
  5. def parse_number(line, index):
  6.     '''Parse the number starting at line[index] and the first index after
  7.       it.'''
  8.     acc = ''
  9.     while index < len(line) and line[index].isdigit():
  10.         acc += line[index]
  11.         index += 1
  12.  
  13.     return int(acc), index
  14.  
  15.  
  16. def parse_coords(line):
  17.     '''Parse the X and Y coordinates of a button or prize.'''
  18.  
  19.     # find X then skip past 1 character
  20.     index = line.find('X') + 2
  21.  
  22.     # parse the X value then skip 4 characters
  23.     x, index = parse_number(line, index)
  24.     index += 4
  25.  
  26.     # parse Y and return
  27.     y, _ = parse_number(line, index)
  28.  
  29.     return x, y
  30.  
  31.  
  32. def parse_machine(src):
  33.     '''Parse the button and prize coordinates of a machine.'''
  34.     return tuple(parse_coords(line) for line in src.split('\n') if line != '')
  35.  
  36.  
  37. def parse(src):
  38.     '''Parse each machine in the input.'''
  39.     return [parse_machine(lines) for lines in src.split('\n\n')]
  40.  
  41.  
  42. def solve(a1, a2, b1, b2, c1, c2):
  43.     '''Solve a two-variable system of linear equations.'''
  44.  
  45.     # use special case of Cramer's rule:
  46.     # https://en.wikipedia.org/wiki/Cramer's_rule#Explicit_formulas_for_small_systems
  47.     denom = a1 * b2 - a2 * b1
  48.     if denom == 0:
  49.         return None, None
  50.  
  51.     # No floating-point inaccuracy issues with Fraction!
  52.     x = Fraction(b2 * c1 - b1 * c2, denom)
  53.     y = Fraction(a1 * c2 - c1 * a2, denom)
  54.     return x, y
  55.  
  56.  
  57. def in_range(n):
  58.     '''Check if n is a valid number of presses'''
  59.     return n is not None and n.is_integer() and n >= 0
  60.  
  61.  
  62. def cost(a, b, prize):
  63.     '''Calculate the token cost of winning the prize for this machine.
  64.  
  65.       A machine with no solution has a 'cost' of 0.'''
  66.     prize = (prize[0] + 10000000000000, prize[1] + 10000000000000)
  67.     a_presses, b_presses = solve(*a, *b, *prize)
  68.     if in_range(a_presses) and in_range(b_presses):
  69.         return 3 * a_presses + b_presses
  70.     else:
  71.         return 0
  72.  
  73.  
  74. def main(machines):
  75.     '''Calculate the total cost of winning each possible machine.'''
  76.     return sum(cost(*machine) for machine in machines)
  77.  
  78.  
  79. if __name__ == '__main__':
  80.     # pass the path to the puzzle input as the first argument
  81.     print(main(parse(open(argv[1]).read())))
  82.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement