Oh dear, I didn't look at the input and assumed there were just the nine colours in the example. Modelled them in an enum and wrote parsers for them all, which failed on the first line of input text. Lesson: look at the real data!
The problem is a directed acyclic graph one. I modelled the graph as a Vec of edges with the start and end nodes, which meant quite expensive searching. It only took 10 or 20 seconds to run but I knew it was a mistake. Reworked to a HashMap where the key is the start of each edge and the value is the Vec of possible end nodes.
The actual graph traversals are the bread and butter of my day job. Those were easy. I wasted a lot of time today on parsing and bad modelling.
usestd::collections::HashMap;usestd::fs::File;usestd::io::prelude::*;modparser;useparser::*;// --- file readfnread_file(filename:&str)->std::io::Result<String>{letmutfile=File::open(filename)?;letmutcontents=String::new();file.read_to_string(&mutcontents)?;Ok(contents)}// --- model#[derive(Debug,Clone,Eq,Hash,PartialEq)]structBagColor(String,String);implBagColor{fnof(adj:&str,col:&str)->Self{BagColor(String::from(adj),String::from(col))}}#[derive(Debug,Eq,PartialEq)]structContent{color:BagColor,count:usize}#[derive(Debug,Eq,PartialEq)]structContainsRule{container:BagColor,contents:Vec<Content>}#[derive(Debug)]structRuleSet{rules:HashMap<BagColor,Vec<Content>>}fnparse_rule<'a>()->implParser<'a,ContainsRule>{fnbag_color<'b>()->implParser<'b,BagColor>{letadjective=one_or_more(letter).map(|ls|ls.into_iter().collect());letcolor=one_or_more(letter).map(|ls|ls.into_iter().collect());pair(first(adjective,whitespace),color).map(|(a,c)|BagColor(a,c))}fncontainer<'b>()->implParser<'b,BagColor>{first(bag_color(),string(" bags contain "))}letbag_or_bags=string(" bags, ").or(string(" bag, ")).or(string(" bags.")).or(string(" bag."));letcontained=pair(first(integer,whitespace),first(bag_color(),bag_or_bags));letcontents_rule=pair(container(),one_or_more(contained)).map(|(color,contents)|ContainsRule{container:color.clone(),contents:contents.iter().map(|(n,c)|Content{color:c.clone(),count:*nasusize}).collect()});letno_contents_rule=first(container(),string("no other bags.")).map(|color|ContainsRule{container:color,contents:vec![]});contents_rule.or(no_contents_rule)}fnparse_input(input:&str)->ParseResult<RuleSet>{letrule_set=one_or_more(first(parse_rule(),whitespace));rule_set.parse(input).map(|(rest,rules)|{letrule_set=RuleSet{rules:rules.into_iter().map(|r|(r.container,r.contents)).collect()};(rest,rule_set)})}implRuleSet{fncan_contain(&self,from:&BagColor,to:&BagColor)->bool{self.rules.get(from).map(|contents|contents.iter().any(|c|&c.color==to)).unwrap_or(false)}fncan_contain_indirectly(&self,from:&BagColor,to:&BagColor)->bool{self.can_contain(from,to)||self.rules.get(from).map(|contents|contents.iter().any(|c|self.can_contain_indirectly(&c.color,to))).unwrap_or(false)}fnnumber_of_contained_bags(&self,from:&BagColor)->usize{self.rules.get(from).map(|contents|contents.iter().map(|c|c.count*(1+self.number_of_contained_bags(&c.color))).sum()).unwrap_or(0)}// --- problems fnpart1(&self)->usize{self.rules.keys().filter(|color|self.can_contain_indirectly(&color,&BagColor::of("shiny","gold"))).count()}fnpart2(&self)->usize{self.number_of_contained_bags(&BagColor::of("shiny","gold"))}}fnmain(){letinput=read_file("./input.txt").unwrap();letrules:RuleSet=parse_input(&input).unwrap().1;println!("part1 {}",rules.part1());println!("part2 {}",rules.part2());}#[cfg(test)]modtests{usesuper::*;#[test]fntest_parse_with_single_clause(){assert_eq!(parse_rule().parse("light red bags contain 1 bright white bag."),Ok(("",ContainsRule{container:BagColor::of("light","red"),contents:vec![Content{color:BagColor::of("bright","white"),count:1}]})));}#[test]fntest_parse_with_two_clauses(){assert_eq!(parse_rule().parse("light red bags contain 1 bright white bag, 2 muted yellow bags."),Ok(("",ContainsRule{container:BagColor::of("light","red"),contents:vec![Content{color:BagColor::of("bright","white"),count:1},Content{color:BagColor::of("muted","yellow"),count:2}]})));}#[test]fntest_parse_with_many_clauses(){assert_eq!(parse_rule().parse("dotted silver bags contain 2 dotted orange bags, 3 bright fuchsia bags, 5 bright tomato bags, 3 faded turquoise bags."),Ok(("",ContainsRule{container:BagColor::of("dotted","silver"),contents:vec![Content{color:BagColor::of("dotted","orange"),count:2},Content{color:BagColor::of("bright","fuchsia"),count:3},Content{color:BagColor::of("bright","tomato"),count:5},Content{color:BagColor::of("faded","turquoise"),count:3}]})));}#[test]fntest_parse_with_no_contents(){assert_eq!(parse_rule().parse("faded blue bags contain no other bags."),Ok(("",ContainsRule{container:BagColor::of("faded","blue"),contents:vec![]})));}#[test]fntest_parse_records_separated_by_lines(){letp=one_or_more(first(letter,whitespace));assert_eq!(p.parse("a\nb\nc\n"),Ok(("",vec!['a','b','c'])));}}
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Oh dear, I didn't look at the input and assumed there were just the nine colours in the example. Modelled them in an enum and wrote parsers for them all, which failed on the first line of input text. Lesson: look at the real data!
The problem is a directed acyclic graph one. I modelled the graph as a
Vec
of edges with the start and end nodes, which meant quite expensive searching. It only took 10 or 20 seconds to run but I knew it was a mistake. Reworked to aHashMap
where the key is the start of each edge and the value is theVec
of possible end nodes.The actual graph traversals are the bread and butter of my day job. Those were easy. I wasted a lot of time today on parsing and bad modelling.