mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Implementation day 21.
This commit is contained in:
@@ -1,12 +1,133 @@
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Read;
|
||||
|
||||
use crate::common::GroupingCount;
|
||||
use crate::common::Lines;
|
||||
use crate::Solution;
|
||||
|
||||
fn read_input(input: &mut dyn Read) -> Vec<(Vec<String>, Vec<String>)> {
|
||||
let mut foods = Vec::new();
|
||||
|
||||
for line in Lines::new(input) {
|
||||
let mut main_split = line.split(" (contains ");
|
||||
|
||||
let ingredients = main_split.next().unwrap();
|
||||
let allergens = main_split.next().unwrap().trim_end_matches(')');
|
||||
|
||||
let ingredients = ingredients.split(' ').map(str::to_owned).collect();
|
||||
let allergens = allergens.split(", ").map(str::to_owned).collect();
|
||||
|
||||
foods.push((ingredients, allergens));
|
||||
}
|
||||
|
||||
foods
|
||||
}
|
||||
|
||||
fn compute_allergens_possible(
|
||||
foods: &[(Vec<String>, Vec<String>)],
|
||||
) -> HashMap<String, Vec<String>> {
|
||||
let mut allergen_possible: HashMap<String, Vec<String>> = HashMap::new();
|
||||
|
||||
for (ingredients, allergens) in foods {
|
||||
for allergen in allergens {
|
||||
match allergen_possible.entry(allergen.clone()) {
|
||||
Entry::Occupied(entry) => {
|
||||
entry.into_mut().retain(|a| ingredients.contains(a));
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(ingredients.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
allergen_possible
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Day21;
|
||||
|
||||
impl Solution for Day21 {
|
||||
fn part1(&mut self, _input: &mut dyn Read) -> String {
|
||||
todo!()
|
||||
fn part1(&mut self, input: &mut dyn Read) -> String {
|
||||
let foods = read_input(input);
|
||||
|
||||
let allergens_possible = compute_allergens_possible(&foods);
|
||||
|
||||
let mut ingredient_count = foods
|
||||
.iter()
|
||||
.map(|(ingredients, _)| ingredients)
|
||||
.flatten()
|
||||
.grouping_count();
|
||||
|
||||
for possible in allergens_possible.values().flatten() {
|
||||
ingredient_count.remove(possible);
|
||||
}
|
||||
|
||||
ingredient_count.values().sum::<usize>().to_string()
|
||||
}
|
||||
|
||||
fn part2(&mut self, input: &mut dyn Read) -> String {
|
||||
let foods = read_input(input);
|
||||
|
||||
let mut allergen_possible: HashMap<String, Vec<String>> =
|
||||
compute_allergens_possible(&foods);
|
||||
|
||||
let mut dangerous = Vec::new();
|
||||
|
||||
while !allergen_possible.is_empty() {
|
||||
let mut found = None;
|
||||
|
||||
for (allergen, possible) in &allergen_possible {
|
||||
if possible.len() == 1 {
|
||||
found = Some(allergen.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let allergen = found.expect("Impossible puzzle");
|
||||
let entry = allergen_possible
|
||||
.remove(&allergen)
|
||||
.expect("Invariant violated")
|
||||
.pop()
|
||||
.unwrap();
|
||||
|
||||
for possible in allergen_possible.values_mut() {
|
||||
possible.retain(|p| p != &entry);
|
||||
}
|
||||
|
||||
dangerous.push((allergen, entry));
|
||||
}
|
||||
|
||||
dangerous.sort();
|
||||
|
||||
dangerous
|
||||
.iter()
|
||||
.map(|(_, b)| b)
|
||||
.fold(String::from(","), |mut a, b| {
|
||||
a.push_str(b);
|
||||
a.push(',');
|
||||
a
|
||||
})
|
||||
.trim_matches(',')
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::test_implementation;
|
||||
|
||||
use super::*;
|
||||
|
||||
const SAMPLE: &[u8] = include_bytes!("../samples/21.txt");
|
||||
|
||||
#[test]
|
||||
fn sample_part1() {
|
||||
test_implementation!(Day21, 1, SAMPLE, 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_part2() {
|
||||
test_implementation!(Day21, 2, SAMPLE, "mxmxvkd,sqjhc,fvjkl");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user