adventofcode/2020/Day07.cs

118 lines
3.6 KiB
C#
Raw Normal View History

2020-12-07 19:34:10 +00:00
using System;
using static System.Console;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Xunit;
namespace AdventOfCode {
struct Contains {
public Contains(string outer, string inner, int count) {
Outer = outer;
Inner = inner;
Count = count;
}
public string Outer;
public string Inner;
public int Count;
public override string ToString() {
return $"{Outer} contains {Count} {Inner}";
}
}
public class Day07 {
static void AddRules(string rule, List<Contains> contains) {
string[] tokens = rule.Split(" bags contain ");
string color = tokens[0];
string[] otherBags = tokens[1].Split(", ");
foreach (var s in otherBags) {
Match m = Regex.Match(s, @"^(\d+) (\w+ \w+) bag");
if (!m.Success) {
continue;
}
int count = int.Parse(m.Groups[1].Value);
Contains c = new Contains(color, m.Groups[2].Value, count);
contains.Add(c);
}
}
static List<Contains> ParseRules(string[] rules) {
2020-12-07 19:34:10 +00:00
var contains = new List<Contains>();
rules.ToList().ForEach(rule => AddRules(rule, contains));
return contains;
}
2020-12-07 19:34:10 +00:00
2020-12-08 20:02:56 +00:00
static int WhatCanContain(List<Contains> contains, string target) {
2020-12-07 19:34:10 +00:00
var result = new HashSet<string>();
2020-12-08 20:02:56 +00:00
int setSize = 0;
result.Add(target);
2020-12-07 19:34:10 +00:00
2020-12-08 20:02:56 +00:00
// transitive closure: keep adding things, until a round of doing so doesn't add anything new
2020-12-07 19:34:10 +00:00
while (true) {
foreach (Contains c in contains) {
if (result.Contains(c.Inner)) {
result.Add(c.Outer);
}
}
if (result.Count() == setSize) { // nothing new to add, we're done
2020-12-08 20:02:56 +00:00
return result.Count() - 1; // our target doesn't actually contain itself
2020-12-07 19:34:10 +00:00
}
setSize = result.Count();
}
}
2020-12-07 19:47:24 +00:00
static int CountBags(List<Contains> rules, string target) {
int total = 1;
foreach (Contains rule in rules) {
if (rule.Outer == target) {
total += rule.Count * CountBags(rules, rule.Inner);
}
}
return total;
}
2020-12-07 19:34:10 +00:00
static int Part1() {
List<Contains> contains = ParseRules(File.ReadAllLines(Util.RootDir + "day07.txt"));
2020-12-08 20:02:56 +00:00
return WhatCanContain(contains, "shiny gold");
2020-12-07 19:34:10 +00:00
}
static int Part2() {
List<Contains> contains = ParseRules(File.ReadAllLines(Util.RootDir + "day07.txt"));
2020-12-07 19:47:24 +00:00
return CountBags(contains, "shiny gold") - 1;
2020-12-07 19:34:10 +00:00
}
[Fact]
public static void Test() {
string[] example =
@"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.".Split('\n');
2020-12-08 20:02:56 +00:00
Assert.Equal(4, WhatCanContain(ParseRules(example), "shiny gold"));
2020-12-07 19:34:10 +00:00
Assert.Equal(213, Part1());
2020-12-07 19:47:24 +00:00
string[] example2 =
@"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.".Split('\n');
Assert.Equal(127, CountBags(ParseRules(example2), "shiny gold"));
Assert.Equal(38426, Part2());
2020-12-07 19:34:10 +00:00
}
}
}