2020: day07: part 1
This commit is contained in:
parent
62d5e5b6c0
commit
eb8743c5d2
6 changed files with 805 additions and 0 deletions
197
aoc2020/src/day07.rs
Normal file
197
aoc2020/src/day07.rs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
use std::str::FromStr;
|
||||
|
||||
use aoc::err;
|
||||
|
||||
const INPUT: &str = include_str!("../input/day07.txt");
|
||||
|
||||
pub fn run() -> aoc::Result<String> {
|
||||
let mut res = String::with_capacity(128);
|
||||
|
||||
writeln!(res, "part 1: {}", part1(INPUT)?)?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn part1(input: &str) -> aoc::Result<usize> {
|
||||
let bag_rules = input
|
||||
.lines()
|
||||
.map(|line| line.parse())
|
||||
.collect::<aoc::Result<Vec<BagRule>>>()
|
||||
.unwrap();
|
||||
|
||||
// create map with Key = color, Value = BagRule
|
||||
let bag_rules_map = bag_rules
|
||||
.iter()
|
||||
.map(|bag_rule| (bag_rule.color.clone(), bag_rule.clone()))
|
||||
.collect();
|
||||
|
||||
Ok(bag_rules
|
||||
.iter()
|
||||
.filter(|bag| bag.can_contain("shiny gold", &bag_rules_map))
|
||||
.count())
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
struct BagRule {
|
||||
color: String,
|
||||
contains: Vec<(usize, String)>,
|
||||
}
|
||||
|
||||
impl BagRule {
|
||||
fn can_contain(&self, color: &str, all_bags: &HashMap<String, BagRule>) -> bool {
|
||||
if self.contains.iter().any(|(_, c)| c == color) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.contains.iter().any(|(_, c)| {
|
||||
// fetch rules for this bag in map
|
||||
let bag_rule = &all_bags[c];
|
||||
|
||||
bag_rule.can_contain(color, all_bags)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for BagRule {
|
||||
type Err = aoc::Error;
|
||||
|
||||
fn from_str(s: &str) -> aoc::Result<Self> {
|
||||
let words: Vec<&str> = s.split(' ').collect();
|
||||
|
||||
// let's assume our input is always valid for now
|
||||
let adjective = words[0];
|
||||
let color = words[1];
|
||||
|
||||
debug_assert!(words[2] == "bags");
|
||||
debug_assert!(words[3] == "contain");
|
||||
|
||||
let mut words = &words[4..];
|
||||
let mut contains = Vec::new();
|
||||
|
||||
loop {
|
||||
match words[0] {
|
||||
// this bag doesn't contain any other bag, we can stop
|
||||
"no" => {
|
||||
debug_assert!(words[1] == "other");
|
||||
debug_assert!(words[2] == "bags.");
|
||||
break;
|
||||
}
|
||||
// this is a list of bags that should be contained, parse the first one then loop
|
||||
// again
|
||||
number => {
|
||||
let n = number
|
||||
.parse()
|
||||
.map_err(|e| err!("couldn't parse number `{}` in bag rule", e))?;
|
||||
|
||||
let adjective = words[1];
|
||||
let color = words[2];
|
||||
|
||||
contains.push((n, format!("{} {}", adjective, color)));
|
||||
|
||||
match words[3] {
|
||||
// there are other bags in this one
|
||||
"bag," | "bags," => {
|
||||
words = &words[4..];
|
||||
}
|
||||
// this was the last bag
|
||||
"bag." | "bags." => break,
|
||||
_ => todo!("handle this with error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
color: format!("{} {}", adjective, color),
|
||||
contains,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
static PROVIDED: &'static str = include_str!("../input/day07_provided.txt");
|
||||
|
||||
#[test]
|
||||
fn part1_provided_parse() {
|
||||
let bag_rules = PROVIDED
|
||||
.lines()
|
||||
.map(|line| line.parse())
|
||||
.collect::<aoc::Result<Vec<BagRule>>>()
|
||||
.unwrap();
|
||||
|
||||
let expected = vec![
|
||||
BagRule {
|
||||
color: "light red".to_string(),
|
||||
contains: vec![
|
||||
(1, "bright white".to_string()),
|
||||
(2, "muted yellow".to_string()),
|
||||
],
|
||||
},
|
||||
BagRule {
|
||||
color: "dark orange".to_string(),
|
||||
contains: vec![
|
||||
(3, "bright white".to_string()),
|
||||
(4, "muted yellow".to_string()),
|
||||
],
|
||||
},
|
||||
BagRule {
|
||||
color: "bright white".to_string(),
|
||||
contains: vec![(1, "shiny gold".to_string())],
|
||||
},
|
||||
BagRule {
|
||||
color: "muted yellow".to_string(),
|
||||
contains: vec![(2, "shiny gold".to_string()), (9, "faded blue".to_string())],
|
||||
},
|
||||
BagRule {
|
||||
color: "shiny gold".to_string(),
|
||||
contains: vec![
|
||||
(1, "dark olive".to_string()),
|
||||
(2, "vibrant plum".to_string()),
|
||||
],
|
||||
},
|
||||
BagRule {
|
||||
color: "dark olive".to_string(),
|
||||
contains: vec![
|
||||
(3, "faded blue".to_string()),
|
||||
(4, "dotted black".to_string()),
|
||||
],
|
||||
},
|
||||
BagRule {
|
||||
color: "vibrant plum".to_string(),
|
||||
contains: vec![
|
||||
(5, "faded blue".to_string()),
|
||||
(6, "dotted black".to_string()),
|
||||
],
|
||||
},
|
||||
BagRule {
|
||||
color: "faded blue".to_string(),
|
||||
contains: vec![],
|
||||
},
|
||||
BagRule {
|
||||
color: "dotted black".to_string(),
|
||||
contains: vec![],
|
||||
},
|
||||
];
|
||||
|
||||
assert_eq!(bag_rules.len(), expected.len());
|
||||
|
||||
for (parsed, expected) in expected.into_iter().zip(bag_rules) {
|
||||
assert_eq!(parsed, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part1_provided_compute() {
|
||||
assert_eq!(part1(PROVIDED).unwrap(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part1_real() {
|
||||
assert_eq!(part1(INPUT).unwrap(), 272);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,3 +4,4 @@ pub mod day03;
|
|||
pub mod day04;
|
||||
pub mod day05;
|
||||
pub mod day06;
|
||||
pub mod day07;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use aoc2020::day03;
|
|||
use aoc2020::day04;
|
||||
use aoc2020::day05;
|
||||
use aoc2020::day06;
|
||||
use aoc2020::day07;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let days: &[DayFunc] = &[
|
||||
|
|
@ -16,6 +17,7 @@ fn main() -> Result<()> {
|
|||
day04::run,
|
||||
day05::run,
|
||||
day06::run,
|
||||
day07::run,
|
||||
];
|
||||
|
||||
aoc::run(days)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue