summaryrefslogtreecommitdiff
path: root/2020/src/bin/day_7.rs
diff options
context:
space:
mode:
Diffstat (limited to '2020/src/bin/day_7.rs')
-rw-r--r--2020/src/bin/day_7.rs137
1 files changed, 137 insertions, 0 deletions
diff --git a/2020/src/bin/day_7.rs b/2020/src/bin/day_7.rs
new file mode 100644
index 0000000..45cb9ee
--- /dev/null
+++ b/2020/src/bin/day_7.rs
@@ -0,0 +1,137 @@
+use bevy::prelude::*;
+use std::collections::{BTreeMap, BTreeSet};
+use std::fs::File;
+use std::io::{BufRead, BufReader};
+
+fn main() {
+ App::build()
+ .add_resource(WindowDescriptor {
+ title: "Advent of Code".to_string(),
+ width: 1920,
+ height: 1080,
+ ..Default::default()
+ })
+ .add_resource(ClearColor(Color::rgb(0., 0., 0.)))
+ .add_startup_system(setup_camera.system())
+ .add_startup_system(read_input_file.system())
+ .add_system(trim_empty_bags.system())
+ .add_system(add_known_sizes.system())
+ .add_system(report_gold_bag_known_size.system())
+ //.add_system(debug_print_bags.system())
+ .add_plugins(DefaultPlugins)
+ .run();
+}
+
+fn setup_camera(mut commands: Commands) {
+ commands.spawn(Camera2dComponents::default());
+}
+
+const TARGET_COLOUR: &str = "shiny gold";
+
+struct Bag {
+ colour: String,
+ required: BTreeMap<String, usize>,
+}
+struct CantContainShinyGold;
+#[derive(Clone)]
+struct KnownSize(usize);
+
+fn read_input_file(mut commands: Commands) {
+ let f = File::open("./inputs/day_7.txt").unwrap();
+
+ for line in BufReader::new(f).lines() {
+ let line = line.unwrap();
+ let line = line.trim();
+
+ let (src_colour, contents) = split_2(line, "bags contain");
+ let contents_parts = contents
+ .trim_end_matches('.')
+ .split(',')
+ .filter(|part| part != &"no other bags")
+ .map(|part| {
+ let (quantity, colour) = split_2(
+ part.trim().trim_end_matches("bags").trim_end_matches("bag"),
+ " ",
+ );
+ (colour, quantity.parse().unwrap())
+ })
+ .collect();
+ commands.spawn((Bag {
+ colour: src_colour,
+ required: contents_parts,
+ },));
+ }
+}
+
+fn split_2(line: &str, split: &str) -> (String, String) {
+ let parts = line.splitn(2, split).collect::<Vec<_>>();
+ assert_eq!(parts.len(), 2);
+ (parts[0].trim().to_string(), parts[1].trim().to_string())
+}
+
+fn debug_print_bags(bags: Query<&Bag>) {
+ for bag in bags.iter() {
+ println!("{} requires {:?}", bag.colour, bag.required);
+ }
+}
+
+fn trim_empty_bags(
+ mut commands: Commands,
+ bags: Query<Without<CantContainShinyGold, (Entity, &Bag)>>,
+) {
+ let mut colours_cleared = BTreeSet::new();
+ for (entity, bag) in bags.iter() {
+ let required_not_relevant = bag.required.keys().all(|required| {
+ !bags
+ .iter()
+ .any(|can_contain_shiny_gold| &can_contain_shiny_gold.1.colour == required)
+ });
+ if required_not_relevant && bag.colour != TARGET_COLOUR {
+ colours_cleared.insert(bag.colour.clone());
+ commands.insert_one(entity, CantContainShinyGold);
+ }
+ }
+
+ if colours_cleared.is_empty() {
+ println!(
+ "{} bags can hold the shiny gold one",
+ bags.iter().count() - 1
+ );
+ }
+}
+
+fn add_known_sizes(mut commands: Commands, bags: Query<(Entity, &Bag, Option<&KnownSize>)>) {
+ for (entity, bag, size) in bags.iter() {
+ if size.is_some() {
+ continue;
+ }
+
+ let mut new_size = Some(0);
+ for (required_colour, required_quantity) in bag.required.iter() {
+ if new_size.is_none() {
+ break;
+ }
+
+ let inner_bag_size = bags
+ .iter()
+ .find(|(_, bag, _)| &bag.colour == required_colour)
+ .and_then(|(_, _, size)| size.clone());
+ new_size = new_size
+ .zip(inner_bag_size)
+ .map(|(a, b)| a + required_quantity * b.0)
+ }
+
+ if let Some(new_size) = new_size {
+ commands.insert_one(entity, KnownSize(new_size + 1));
+ }
+ }
+}
+
+fn report_gold_bag_known_size(bags: Query<(&Bag, &KnownSize)>) {
+ println!("{} have a known size", bags.iter().count());
+ for (bag, size) in bags.iter() {
+ if bag.colour == TARGET_COLOUR {
+ println!("The shiny gold bag must contain {} other bags!", size.0 - 1)
+ }
+ }
+}