Advent of Code: Day 16

Source

Part 1: Given a list of target values of the form:

children: 3
cats: 7
samoyeds: 2

And a list of ‘Aunt Sues’, each with known values:

Sue 1: children: 1, cars: 8, vizslas: 7
Sue 2: akitas: 10, perfumes: 10, children: 5
Sue 3: cars: 5, pomeranians: 4, vizslas: 1

Determine which Sue has no unset but matching values.

For example, Sue 1 is invalid because children is 1 versus 3 and Sue 2 because children is 5 versus 3. Given only the values above, Sue 3 would be valid since there are no contradictions.

I think understanding this problem took longer than solving it. Originally, I thought that missing values should be treated as float('inf'), but they should just be ignored entirely:

targets = {}
sues = collections.defaultdict(dict)

loading_targets = True
for line in sys.stdin:
    line = line.strip()

    if not line:
        loading_targets = False

    elif loading_targets:
        key, val = line.split(': ')
        targets[key] = int(val)

    else:
        sue, things = line.strip().split(': ', 1)
        for thing in things.split(', '):
            key, val = thing.split(': ')
            sues[sue][key] = int(val)

for sue in sues:
    valid = True

    for key in targets:
        if key in sues[sue] and sues[sue][key] != targets[key]:
            valid = False
            break

    if valid:
        print(sue)

The longer half is correctly parsing and loading the data. For my case, I put both inputs in the same stream, separated by an empty line (similar to HTTP and other protocols actually).

After that, it was just a matter of checking each Sue for the first existent but non-matching value.

Part 2: Repeat the same algorithm, however assume that a Sue must have strictly more cats and trees than listed and strictly less pomeranians and goldfish.

For this, I introduced another defaultdict which contains the comparators to use for any given value:

comparators = collections.defaultdict(lambda : operator.eq)
comparators['cats'] = comparators['trees'] = operator.gt
comparators['pomeranians'] = comparators['goldfish'] = operator.lt

After that, we can change the for sue in sues loop to take the comparator into account:

for sue in sues:
    valid = True

    for key in targets:
        if key in sues[sue] and not comparators[key](sues[sue][key], targets[key]):
                valid = False
                break

    if valid:
        print(sue)

Neat.