1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
//! # [Day 16: Aunt Sue](https://adventofcode.com/2015/day/16)
//!
//! 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](https://en.wikipedia.org/wiki/Samoyed_%28dog%29)`,
//! `[pomeranians](https://en.wikipedia.org/wiki/Pomeranian_%28dog%29)`,
//! `[akitas](https://en.wikipedia.org/wiki/Akita_%28dog%29)`, and
//! `[vizslas](https://en.wikipedia.org/wiki/Vizsla)`.
//! - `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](https://en.wikipedia.org/wiki/Ticker_tape):
//!
//! ```plain
//! 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 Two
//!
//! 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](https://www.youtube.com/watch?v=RXJKdh1KZ0w), 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?**
use regex::Regex;
use std::collections::HashMap;
#[aoc_generator(day16)]
fn parse_input(input: &str) -> anyhow::Result<Vec<Sue>> {
let mut list: Vec<Sue> = Vec::new();
//Sue 1: children: 1, cars: 8, vizslas: 7
let re = Regex::new(r"^Sue (?P<nr>\d+): (?P<data>.+)$")?;
for line in input.lines() {
if let Some(matches) = re.captures(line) {
let nr = matches.name("nr").unwrap().as_str().parse()?;
let data = matches.name("data").unwrap().as_str().to_string();
let mut map = HashMap::new();
for datum in data.split(", ") {
let parts: Vec<&str> = datum.split(": ").collect();
map.insert(parts[0].to_string(), parts[1].parse()?);
}
list.push(Sue { nr, data: map })
} else {
return Err(anyhow!("failed to parse: {}", line));
}
}
Ok(list)
}
/// Part 1: What is the number of the Sue that got you the gift?
#[aoc(day16, part1)]
fn part1(input: &[Sue]) -> u16 {
find_sue1(input, &get_search()).unwrap()
}
fn find_sue1(sues: &[Sue], search: &HashMap<String, i32>) -> Option<u16> {
for sue in sues {
let mut possible = true;
for key in search.keys() {
if sue.data.contains_key(key) && sue.data.get(key) != search.get(key) {
possible = false;
break;
}
}
if possible {
return Some(sue.nr);
}
}
None
}
/// Part 2: What is the number of the real Aunt Sue?
#[aoc(day16, part2)]
fn part2(input: &[Sue]) -> u16 {
find_sue2(input, &get_search()).unwrap()
}
fn find_sue2(sues: &[Sue], search: &HashMap<String, i32>) -> Option<u16> {
for sue in sues {
let mut possible = true;
for key in search.keys() {
if sue.data.contains_key(key)
&& match key.as_str() {
"cats" | "trees" => sue.data.get(key) <= search.get(key),
"pomeranians" | "goldfish" => sue.data.get(key) >= search.get(key),
_ => sue.data.get(key) != search.get(key),
}
{
possible = false;
break;
}
}
if possible {
return Some(sue.nr);
}
}
None
}
#[derive(Debug)]
struct Sue {
nr: u16,
data: HashMap<String, i32>,
}
fn get_search() -> HashMap<String, i32> {
HashMap::from([
("children".into(), 3),
("cats".into(), 7),
("samoyeds".into(), 2),
("pomeranians".into(), 3),
("akitas".into(), 0),
("vizslas".into(), 0),
("goldfish".into(), 5),
("trees".into(), 3),
("cars".into(), 2),
("perfumes".into(), 1),
])
}