1
0
Fork 0
mirror of https://github.com/HenkKalkwater/aoc-2020 synced 2024-11-22 11:05:18 +00:00
aoc-2020/source/day7.d
2020-12-07 08:03:29 +01:00

130 lines
3.5 KiB
D

import std.algorithm;
import std.array;
import std.format;
import std.range;
import std.stdio;
import std.string;
import std.traits;
import std.variant;
import dayutil;
alias Colour = size_t;
Variant run(int part, File input, bool bigboy, string[] args) {
BagNode[][Colour] rules;
input.byLineCopy.each!(x => x.parseAndAddRule(rules));
Variant result = parts!size_t(part,
() => part1(rules),
() => part2(rules));
return result;
}
size_t part1(BagNode[][Colour] rules) {
Colour result = 0;
foreach(Colour colour; rules.byKey()) {
if (eventuallyContains(rules, rules[colour], colourCode("shiny gold"))) {
result++;
}
}
return result;
}
size_t part2(BagNode[][Colour] rules) {
return countContaining(rules, rules[colourCode("shiny gold")]);
}
size_t[string] colourCodeMap;
size_t index = 0;
size_t colourCode(string colour) {
return colourCodeMap.require(colour, index++);
}
struct BagNode {
this(string colour, size_t count) {
this.colour = colourCode(colour);
this.count = count;
}
Colour colour;
size_t count = 0;
}
void parseAndAddRule(string rule, ref BagNode[][size_t] ruleMap) {
auto splitRes = rule.splitter(" bags contain ").array;
size_t key = colourCode(splitRes[0]);
BagNode[] value = [];
if (splitRes[1] != "no other bags.") {
value = splitRes[1].splitter(", ").map!((x) {
string colour;
size_t count;
x.splitter(" bag").array[0].formattedRead!"%d %s"(count, colour);
return BagNode(colour, count);
}).array;
}
ruleMap[key] = value;
}
bool eventuallyContains(BagNode[][Colour] colourMap, BagNode[] nodes, Colour colour) {
foreach(BagNode node; nodes) {
if (node.colour == colour) {
return true;
} else {
if (eventuallyContains(colourMap, colourMap[node.colour], colour)) {
return true;
}
}
}
return false;
}
size_t countContaining(BagNode[][Colour] colourMap, BagNode[] children) {
size_t total = 0;
//writeln("Recursing into node with ", children.length, " children");
foreach(BagNode node; children) {
total += node.count * (1 + countContaining(colourMap, colourMap[node.colour]));
}
return total;
}
unittest {
string input = q"EOS
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.
EOS";
BagNode[][size_t] testRules;
input.lineSplitter.each!(x => x.parseAndAddRule(testRules));
assert(testRules[colourCode("light red")] == [BagNode("bright white", 1), BagNode("muted yellow", 2)]);
assert(part1(testRules) == 4);
assert(countContaining(testRules, testRules[colourCode("faded blue")]) == 0);
assert(countContaining(testRules, testRules[colourCode("vibrant plum")]) == 11);
}
unittest {
string input = q"EOS
shiny gold bags contain 2 dark red bags.
dark red bags contain 2 dark orange bags.
dark orange bags contain 2 dark yellow bags.
dark yellow bags contain 2 dark green bags.
dark green bags contain 2 dark blue bags.
dark blue bags contain 2 dark violet bags.
dark violet bags contain no other bags.
EOS";
BagNode[][size_t] testRules;
input.lineSplitter.each!(x => x.parseAndAddRule(testRules));
size_t count = part2(testRules);
assert(count == 126);
}