using System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Text.RegularExpressions; namespace AOC2017 { class BScottDay7 : BScottSolution { public override string Name => "Day 7: Recursive Circus"; public override void Run() { // Sample Problem string[] input = File.ReadAllLines("BScottDay7Sample.txt"); Node rootNode = ProcessList(input); Console.WriteLine($"Part 1 Example Answer: {rootNode.Name}"); int weightDifference = FindWeightDifference(rootNode); Node badProgram = FindBadProgram(rootNode); Console.WriteLine("Part 2 Example Answer: " + (badProgram.Weight + weightDifference)); // Main problem input = File.ReadAllLines("BScottDay7.txt"); rootNode = ProcessList(input); Console.WriteLine($"Part 1 Answer: {rootNode.Name}"); // I have no idea if this convoluted solution will work for other inputs, // but it's printing out the right answer and I am too tired and fed up with this // problem to care if it's not perfect. weightDifference = FindWeightDifference(rootNode); badProgram = FindBadProgram(rootNode); Console.WriteLine("Part 2 Answer: " + (badProgram.Weight + weightDifference)); } static Node ProcessList(string[] input) { List removalList = new List(); Dictionary nodeList = new Dictionary(); Regex regex = new Regex(@"([a-z]+) \((\d+)\)(?: -> )?(?:([a-z]+)*(?:, )?)*"); for (int i = 0; i < input.Length; i++) { Match match = regex.Match(input[i]); if (match.Success) { // create a new node Node node = new Node(match.Groups[1].Value, int.Parse(match.Groups[2].Value)); // add any sub items if (match.Groups[3].Captures.Count > 0) { foreach (Capture capture in match.Groups[3].Captures) { node.ChildNames.Add(capture.Value); } } nodeList.Add(match.Groups[1].Value, node); } } foreach (KeyValuePair kvpNode in nodeList) { if (kvpNode.Value.ChildNames.Count > 0) { foreach (string s in kvpNode.Value.ChildNames) { if (nodeList.ContainsKey(s)) { kvpNode.Value.ChildNodes.Add(nodeList[s]); removalList.Add(s); // a list of nodes to remove from dictionary } } } } // Remove everything from root colllection that was a child node of something else. // This should leave just 1 node remaining which is the root node. foreach (string key in removalList) nodeList.Remove(key); return nodeList.First().Value; // hopefully this is the fully populated root node... } static int GetWeight(Node parentNode) { if (parentNode.ChildNodes.Count == 0) return parentNode.Weight; int weight = parentNode.Weight; foreach (Node t in parentNode.ChildNodes) weight += GetWeight(t); return weight; } /// /// Finds the weight difference of the inbalance /// /// The root node of the entire stack /// The weight difference of the offending program. static int FindWeightDifference(Node rootNode) { int[] weights = new int[rootNode.ChildNodes.Count]; for (int i = 0; i < rootNode.ChildNodes.Count; i++) weights[i] = GetWeight(rootNode.ChildNodes[i]); List result = weights.GroupBy(i => i).OrderBy(g => g.Count()).Select(g => g.Key).ToList(); return result.Last() - result.First(); } static Node FindBadProgram(Node parentNode) { // only find nodes that have at least 3 children to compare if(parentNode.ChildNodes.Count >= 3) { int[] weights = new int[parentNode.ChildNodes.Count]; // calulate the weights for (int i = 0; i < parentNode.ChildNodes.Count; i++) weights[i] = GetWeight(parentNode.ChildNodes[i]); // find the least common weight int result = weights.GroupBy(i => i).OrderBy(g => g.Count()).Select(g => g.Key).ToList().First(); Node nextNode = parentNode.ChildNodes[Array.IndexOf(weights, result)]; if (nextNode.ChildNodes.Count > 0) { // dig into the node with the uncommon weight return FindBadProgram(parentNode.ChildNodes[Array.IndexOf(weights, result)]); } } return parentNode; } class Node { public string Name { get; set; } public int Weight { get; set; } public List ChildNodes { get; set; } public List ChildNames { get; set; } public Node(string name, int weight) { Name = name; Weight = weight; ChildNodes = new List(); ChildNames = new List(); } } } }