2024-01-14 21:04:10 +00:00
|
|
|
//! Day 07 of 2020.
|
|
|
|
|
2022-10-03 16:02:40 +00:00
|
|
|
use crate::prelude::*;
|
|
|
|
|
2024-01-14 21:04:10 +00:00
|
|
|
/// Get the solution for day 07 of 2020.
|
2022-10-03 16:02:40 +00:00
|
|
|
pub fn solution() -> Solution {
|
|
|
|
Solution::new(Day::new(7, 2020), part_1, part_2).with_expected(169, 82372)
|
|
|
|
}
|
|
|
|
|
2024-01-14 21:04:10 +00:00
|
|
|
/// The target bag to look for.
|
2022-10-03 16:02:40 +00:00
|
|
|
const TARGET: &str = "shiny gold";
|
|
|
|
|
2024-01-14 21:04:10 +00:00
|
|
|
/// Parse the bags from the puzzle input.
|
2022-10-03 16:02:40 +00:00
|
|
|
fn parse_bags(input: &str) -> Vec<(&str, Vec<(usize, &str)>)> {
|
|
|
|
let mut bags = vec![];
|
|
|
|
|
|
|
|
for line in input.lines() {
|
|
|
|
let main_bag = &line[0..line.find(" bags").unwrap()];
|
|
|
|
let other_bags = line[line.find("contain ").unwrap() + "contain ".len()..]
|
|
|
|
.split(',')
|
|
|
|
.map(str::trim)
|
|
|
|
.map(|bag| {
|
|
|
|
let count = bag[0..bag.find(' ').unwrap()]
|
|
|
|
.parse::<usize>()
|
|
|
|
.unwrap_or_default();
|
|
|
|
let bag = &bag[bag.find(' ').unwrap() + 1..bag.find(" bag").unwrap()];
|
|
|
|
(count, bag)
|
|
|
|
})
|
|
|
|
.collect::<Vec<(usize, &str)>>();
|
|
|
|
|
|
|
|
bags.push((main_bag, other_bags));
|
|
|
|
}
|
|
|
|
|
|
|
|
bags
|
|
|
|
}
|
|
|
|
|
2024-01-14 21:04:10 +00:00
|
|
|
/// Find out which bags can hold the [`TARGET`] bag.
|
2022-10-03 16:02:40 +00:00
|
|
|
fn can_hold_target_bag<'a>(
|
|
|
|
target: &'a str,
|
|
|
|
bags: &[(&'a str, Vec<(usize, &'a str)>)],
|
|
|
|
) -> Vec<&'a str> {
|
|
|
|
let mut can_hold = vec![];
|
|
|
|
for (bag, children) in bags {
|
|
|
|
if children.iter().any(|(_, child)| *child == target) {
|
|
|
|
can_hold.push(*bag);
|
|
|
|
can_hold.append(&mut can_hold_target_bag(bag, bags));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
can_hold
|
|
|
|
}
|
|
|
|
|
2024-01-14 21:04:10 +00:00
|
|
|
/// Find out how many bags are required to be inside the [`TARGET`] bag.
|
2022-10-03 16:02:40 +00:00
|
|
|
fn target_bag_holds_amount<'a>(
|
|
|
|
target: &'a str,
|
|
|
|
bags: &[(&'a str, Vec<(usize, &'a str)>)],
|
|
|
|
) -> usize {
|
|
|
|
let mut count = 0;
|
|
|
|
|
|
|
|
for (bag, children) in bags {
|
|
|
|
if bag == &target {
|
|
|
|
for (child_count, child_bag) in children {
|
|
|
|
if child_count != &0 {
|
|
|
|
count += child_count;
|
|
|
|
count += child_count * target_bag_holds_amount(child_bag, bags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
count
|
|
|
|
}
|
|
|
|
|
2024-01-14 21:04:10 +00:00
|
|
|
/// The logic to solve part one.
|
2022-10-03 16:02:40 +00:00
|
|
|
fn part_1(input: &str) -> Result<String> {
|
|
|
|
Ok(
|
|
|
|
can_hold_target_bag(TARGET, &parse_bags(input))
|
|
|
|
.into_iter()
|
|
|
|
.collect::<HashSet<&str>>()
|
|
|
|
.len()
|
|
|
|
.to_string(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-01-14 21:04:10 +00:00
|
|
|
/// The logic to solve part two.
|
2022-10-03 16:02:40 +00:00
|
|
|
fn part_2(input: &str) -> Result<String> {
|
|
|
|
Ok(target_bag_holds_amount("shiny gold", &parse_bags(input)).to_string())
|
|
|
|
}
|