import std; void main() { readText("input").calculateNumberOfContainingBagsTranisitively("shiny gold").writeln; } auto calculateNumberOfContainingBagsTranisitively(string rules, string color) { return rules.calculateContainingBagsTranisitively(color).byKey.count; } auto calculateContainingBagsTranisitively(string rules, string color) { return rules.parseRules.calculateContainingBagsTranisitively(color); } void[0][string] calculateContainingBagsTranisitively(string[][string] containedInMap, string color) { string[]* containingBags = (color in containedInMap); if (!containingBags || (*containingBags).length == 0) { return null; } void[0][string] result; foreach (bag; *containingBags) { result[bag] = []; containedInMap.calculateContainingBagsTranisitively(bag) .byKey.each!((containingBag) => result[containingBag] = []); } return result; } auto parseRules(string rules) { string[][string] containedInMap; rules.splitter("\n").filter!(not!empty) .each!((rule) { auto spec = rule.split(" bags contain "); auto containingBag = spec[0]; spec[1].splitter(", ").filter!(it => it != "no other bags.") .map!(bagSpec => bagSpec.matchFirst(r"(\d+) (.+) (bag|bags)\.*")[2]) .each!(bag => containedInMap.update(bag, { return [containingBag]; }, (ref string[] old) { return old ~ [containingBag]; })); }); return containedInMap; } unittest { auto input = `light red bags contain 1 bright white bag, 2 muted yellow bags. dark orange bags contain 3 bright white bags, 4 muted yellow bags. bright white bags contain 1 shiny gold bag. muted yellow bags contain 2 shiny gold bags, 9 faded blue bags. shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags. dark olive bags contain 3 faded blue bags, 4 dotted black bags. vibrant plum bags contain 5 faded blue bags, 6 dotted black bags. faded blue bags contain no other bags. dotted black bags contain no other bags.`; assert(input.calculateNumberOfContainingBagsTranisitively("shiny gold") == 4); }