Day 15 - Advent of Code 2015

Working solutions for the day 15 puzzles.

Part One

""" day_15_01.py """

# usage: python3 day_15_01.py < input

import functools
import itertools
from operator import mul
import sys


def combos(count, goal):
    """ calculate count integers that sum to goal """
    array = [range(1, 101) for _ in range(count)]
    for combo in itertools.product(*array):
        if sum(combo) == goal:
            yield combo


def product(elements):
    """ calculate product of elements """
    return functools.reduce(mul, elements, 1)


ingredients = {}
for item in sys.stdin.read().splitlines():
    colon = item.find(':')
    name = item[:colon]
    properties = [(label, int(value)) for label, value in [attribute.split()
                  for attribute in item[colon + 2:].split(', ')]]
    ingredients[name] = properties

maximum = 0
for option in combos(len(ingredients), 100):
    values = []
    for i, name in enumerate(ingredients):
        attributes = [0] * (len(ingredients[name]) - 1)
        for j, (label, value) in enumerate(ingredients[name]):
            if label != 'calories':
                attributes[j] = option[i] * value
        values.append(attributes)
    total = [sum(x) for x in zip(*values)]
    total = [0 if x < 0 else x for x in total]
    maximum = max(maximum, product(total))
print(maximum)

Part Two

""" day_15_02.py """

# usage: python3 day_15_02.py < input

import functools
import itertools
from operator import mul
import sys


def combos(count, goal):
    """ calculate count integers that sum to goal """
    array = [range(1, 101) for _ in range(count)]
    for combo in itertools.product(*array):
        if sum(combo) == goal:
            yield combo


def product(elements):
    """ calculate product of elements """
    return functools.reduce(mul, elements, 1)


ingredients = {}
for item in sys.stdin.read().splitlines():
    colon = item.find(':')
    name = item[:colon]
    properties = [(label, int(value)) for label, value in [attribute.split()
                  for attribute in item[colon + 2:].split(', ')]]
    ingredients[name] = properties

maximum = 0
for option in combos(len(ingredients), 100):
    values = []
    for i, name in enumerate(ingredients):
        attributes = [0] * len(ingredients[name])
        for j, (label, value) in enumerate(ingredients[name]):
            attributes[j] = option[i] * value
        values.append(attributes)
    total = [sum(x) for x in zip(*values)]
    total = [0 if x < 0 else x for x in total]
    if total[-1] == 500:
        maximum = max(maximum, product(total[:-1]))
print(maximum)