AoC 2015 Puzzle 16

Info

I solved this puzzle. You can find the code here, but I have yet to write a good explanation of the solution here. I will do this shortly, thank you for your patience.

Day 16: Aunt Sue

Your 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:

  • children: 3
  • cats: 7
  • samoyeds: 2
  • pomeranians: 3
  • akitas: 0
  • vizslas: 0
  • goldfish: 5
  • trees: 3
  • cars: 2
  • perfumes: 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?

Part 2

As 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?

Complete Solution

@ToString
@RequiredArgsConstructor
class Sue {
    @NonNull
    int nr;
    Map<String, Integer> props = new HashMap<>();

    boolean isEqualToSue(Sue other, int part) {
        if (part == 1) {
            // The current Sue has the same keys for its values
            for (String k : props.keySet()) {
                if (props.get(k) != other.props.get(k)) {
                    return false;
                }
            }
        } else {
            // The current Sue has the same keys for its values
            for (String k : props.keySet()) {
                switch (k) {
                case "cats":
                case "trees":
                    if (props.get(k) <= target.props.get(k)) {
                        return false;
                    }
                    break;
                case "pomeranians":
                case "goldfish":
                    if (props.get(k) > target.props.get(k)) {
                        return false;
                    }
                    break;
                default:
                    if (props.get(k) != target.props.get(k)) {
                        return false;
                    }
                }
            }
        }

        return true;
    }

    void addProp(String key, Integer value) {
        props.put(key, value);
    }
}

Pattern p = Pattern.compile("^Sue (.*?): (.+): (.+), (.+): (.+), (.+): (.+)");

List<Sue> parseSue(List<String> input) {
    List<Sue> output = new ArrayList<>();
    for (String s : input) {
        // System.out.println(s);
        Matcher m = p.matcher(s);

        if (m.find()) {
            Sue possible = new Sue(Integer.parseInt(m.group(1)));
            for (int i = 2; i <= m.groupCount(); i += 2) {
                possible.addProp(m.group(i), Integer.parseInt(m.group(i + 1)));
            }
            output.add(possible);
        }
    }

    return output;
}

Sue target = new Sue(0) {
    {
        addProp("children", 3);
        addProp("cats", 7);
        addProp("samoyeds", 2);
        addProp("pomeranians", 3);
        addProp("akitas", 0);
        addProp("vizslas", 0);
        addProp("goldfish", 5);
        addProp("trees", 3);
        addProp("cars", 2);
        addProp("perfumes", 1);
    }
};

@Override
public String part1(List<String> input) {

    List<Sue> sues = parseSue(input);

    for (Sue s : sues) {
        if (s.isEqualToSue(target, 1)) {
            return String.valueOf(s.nr);
        }
    }
    return null;
}

@Override
public String part2(List<String> input) {
    List<Sue> sues = parseSue(input);

    for (Sue s : sues) {
        if (s.isEqualToSue(target, 2)) {
            return String.valueOf(s.nr);
        }
    }
    return null;
}