<jupyter_start><jupyter_text>Day 16Your Aunt Sue has given you a wonderful gift, and you'd like to send her a thank you card. However, there's a small problem: she signed it "From, Aunt Sue".You have 500 Aunts named "Sue".So, to avoid sending the card to the wrong person, you need to figure out which Aunt Sue (which you conveniently number 1 to 500, for sanity) gave you the gift. You open the present and, as luck would have it, good ol' Aunt Sue got you a My First Crime Scene Analysis Machine! Just what you wanted. Or needed, as the case may be.The My First Crime Scene Analysis Machine (MFCSAM for short) can detect a few specific compounds in a given sample, as well as how many distinct kinds of those compounds there are. According to the instructions, these are what the MFCSAM can detect:    children, by human DNA age analysis.    cats. It doesn't differentiate individual breeds.    Several seemingly random breeds of dog: samoyeds, pomeranians, akitas, and vizslas.    goldfish. No other kinds of fish.    trees, all in one group.    cars, presumably by exhaust or gasoline or something.    perfumes, which is handy, since many of your Aunts Sue wear a few kinds.In fact, many of your Aunts Sue have many of these. You put the wrapping from the gift into the MFCSAM. It beeps inquisitively at you a few times and then prints out a message on ticker tape:```textchildren: 3cats: 7samoyeds: 2pomeranians: 3akitas: 0vizslas: 0goldfish: 5trees: 3cars: 2perfumes: 1```You make a list of the things you can remember about each Aunt Sue. Things missing from your list aren't zero - you simply don't remember the value.What is the number of the Sue that got you the gift?  Puzzle 1<jupyter_code>from dataclasses import dataclass
from typing import List


@dataclass
class Sue():
    number: int
    children: int=None
    cats: int=None
    samoyeds: int=None
    pomeranians: int=None
    akitas: int=None
    vizslas: int=None
    goldfish: int=None
    trees: int=None
    cars: int=None
    perfumes: int=None
    

def parse_sue(instr:str):
    """Return Sue object from string
    
    :param instr: description of Sue
    """
    data = instr.strip().split()

    pairs = ((2, 3), (4, 5), (6, 7))
    kwargs = {}
    for word, val in pairs:
        kwargs[data[word][:-1]] = int(data[val].replace(",", ""))
    
    return Sue(number=int(data[1][:-1]), **kwargs)


def id_sue(sues:List):
    """Return an identified Sue from the list
    
    :param sues: list of Sues objects
    """
    ticker = {"children": 3,
              "cats": 7,
              "samoyeds": 2,
              "pomeranians": 3,
              "akitas": 0,
              "vizslas": 0,
              "goldfish": 5,
              "trees": 3,
              "cars": 2,
              "perfumes": 1}
    for key, val in ticker.items():
        sues = [_ for _ in sues if getattr(_, key) in (None, val)]
        
    return sues
data = ("Sue 1: goldfish: 9, cars: 0, samoyeds: 9",
        "Sue 2: perfumes: 5, trees: 8, goldfish: 8",
        "Sue 3: pomeranians: 2, akitas: 1, trees: 5",
        "Sue 4: goldfish: 10, akitas: 2, perfumes: 9",
        "Sue 5: cars: 5, perfumes: 6, akitas: 9",
        "Sue 6: goldfish: 10, cats: 9, cars: 8",
        "Sue 7: trees: 2, samoyeds: 7, goldfish: 10",
        "Sue 8: cars: 8, perfumes: 6, goldfish: 1",
        "Sue 9: cats: 4, pomeranians: 0, trees: 0",
        "Sue 10: trees: 2, children: 10, samoyeds: 10")

sues = [parse_sue(sue) for sue in data]
print(sues)
print(id_sue(sues))<jupyter_output>[Sue(number=1, children=None, cats=None, samoyeds=9, pomeranians=None, akitas=None, vizslas=None, goldfish=9, trees=None, cars=0, perfumes=None), Sue(number=2, children=None, cats=None, samoyeds=None, pomeranians=None, akitas=None, vizslas=None, goldfish=8, trees=8, cars=None, perfumes=5), Sue(number=3, children=None, cats=None, samoyeds=None, pomeranians=2, akitas=1, vizslas=None, goldfish=None, trees=5, cars=None, perfumes=None), Sue(number=4, children=None, cats=None, samoyeds=None, pomeranians=None, akitas=2, vizslas=None, goldfish=10, trees=None, cars=None, perfumes=9), Sue(number=5, children=None, cats=None, samoyeds=None, pomeranians=None, akitas=9, vizslas=None, goldfish=None, trees=None, cars=5, perfumes=6), Sue(number=6, children=None, cats=9, samoyeds=None, pomeranians=None, akitas=None, vizslas=None, goldfish=10, trees=None, cars=8, perfumes=None), Sue(number=7, children=None, cats=None, samoyeds=7, pomeranians=None, akitas=None, vizslas=None, goldfish=10, trees=2, cars=Non[...]<jupyter_text>Solution<jupyter_code>with open("day16.txt", "r") as ifh:
    print(id_sue([parse_sue(_) for _ in ifh.readlines()]))<jupyter_output>[Sue(number=40, children=None, cats=7, samoyeds=None, pomeranians=None, akitas=0, vizslas=0, goldfish=None, trees=None, cars=None, perfumes=None)]<jupyter_text>Puzzle 2As you're about to send the thank you note, something in the MFCSAM's instructions catches your eye. Apparently, it has an outdated retroencabulator, and so the output from the machine isn't exact values - some of them indicate ranges.In particular, the cats and trees readings indicates that there are greater than that many (due to the unpredictable nuclear decay of cat dander and tree pollen), while the pomeranians and goldfish readings indicate that there are fewer than that many (due to the modial interaction of magnetoreluctance).What is the number of the real Aunt Sue?<jupyter_code>def id_sue_fixed(sues:List):
    """Return an identified Sue from the list
    
    :param sues: list of Sues objects
    """
    ticker = {"children": 3,
              "cats": 7,
              "samoyeds": 2,
              "pomeranians": 3,
              "akitas": 0,
              "vizslas": 0,
              "goldfish": 5,
              "trees": 3,
              "cars": 2,
              "perfumes": 1}
    for key, val in ticker.items():
        if key in ("cats", "trees"):
            sues = [_ for _ in sues if getattr(_, key) is None or getattr(_, key) > val]
        elif key in ("pomeranians", "goldfish"):
            sues = [_ for _ in sues if getattr(_, key) is None or getattr(_, key) < val]
        else:
            sues = [_ for _ in sues if getattr(_, key) in (None, val)]
        
    return sues
with open("day16.txt", "r") as ifh:
    print(id_sue_fixed([parse_sue(_) for _ in ifh.readlines()]))<jupyter_output>[Sue(number=241, children=None, cats=None, samoyeds=2, pomeranians=1, akitas=None, vizslas=None, goldfish=None, trees=None, cars=2, perfumes=None)]
{'fraction_non_alphanumeric': 0.11501842751842752, 'fraction_numerical': 0.021805896805896806, 'mean_word_length': 3.4670781893004117, 'pattern_counts': {'":': 20, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '525831', 'license': 'MIT', 'n_tokens_mistral': 2300, 'n_tokens_neox': 2136, 'n_words': 784, 'path': 'day16.ipynb', 'repo_name': 'widdowquinn/aoc2015', 'size': '6512'}