Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- from fractions import Fraction
- from sys import argv
- def parse_number(line, index):
- '''Parse the number starting at line[index] and the first index after
- it.'''
- acc = ''
- while index < len(line) and line[index].isdigit():
- acc += line[index]
- index += 1
- return int(acc), index
- def parse_coords(line):
- '''Parse the X and Y coordinates of a button or prize.'''
- # find X then skip past 1 character
- index = line.find('X') + 2
- # parse the X value then skip 4 characters
- x, index = parse_number(line, index)
- index += 4
- # parse Y and return
- y, _ = parse_number(line, index)
- return x, y
- def parse_machine(src):
- '''Parse the button and prize coordinates of a machine.'''
- return tuple(parse_coords(line) for line in src.split('\n') if line != '')
- def parse(src):
- '''Parse each machine in the input.'''
- return [parse_machine(lines) for lines in src.split('\n\n')]
- def solve(a1, a2, b1, b2, c1, c2):
- '''Solve a two-variable system of linear equations.'''
- # use special case of Cramer's rule:
- # https://en.wikipedia.org/wiki/Cramer's_rule#Explicit_formulas_for_small_systems
- denom = a1 * b2 - a2 * b1
- if denom == 0:
- return None, None
- # No floating-point inaccuracy issues with Fraction!
- x = Fraction(b2 * c1 - b1 * c2, denom)
- y = Fraction(a1 * c2 - c1 * a2, denom)
- return x, y
- def in_range(n):
- '''Check if n is a valid number of presses'''
- return n is not None and n.is_integer() and n >= 0
- def cost(a, b, prize):
- '''Calculate the token cost of winning the prize for this machine.
- A machine with no solution has a 'cost' of 0.'''
- prize = (prize[0] + 10000000000000, prize[1] + 10000000000000)
- a_presses, b_presses = solve(*a, *b, *prize)
- if in_range(a_presses) and in_range(b_presses):
- return 3 * a_presses + b_presses
- else:
- return 0
- def main(machines):
- '''Calculate the total cost of winning each possible machine.'''
- return sum(cost(*machine) for machine in machines)
- if __name__ == '__main__':
- # pass the path to the puzzle input as the first argument
- print(main(parse(open(argv[1]).read())))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement