using System.Runtime.InteropServices; var exParsed = new Day05Parsed("example-input.txt"); Console.WriteLine("Example Part01: {0}", exParsed.GetPart01()); Console.WriteLine("Example Part02: {0}", exParsed.GetPart02()); var pzParsed = new Day05Parsed("puzzle-input.txt"); Console.WriteLine("Puzzle Part01: {0}", pzParsed.GetPart01()); Console.WriteLine("Puzzle Part02: {0}", pzParsed.GetPart02()); class Day05Parsed : AdventCommon.ParsedInput { public class Page : IComparable { public static Dictionary> RulesByPage = new Dictionary>(); public Page(int value) => Value = value; public int Value { get; set; } public int CompareTo(Page? other) { if (other == null) throw new Exception("Other is null"); if (this.Value == other.Value) return 0; if (!RulesByPage.ContainsKey(this.Value)) { // If there's no key, then this MUST be at the end so its the "biggest" return 1; } if (RulesByPage[this.Value].Contains(other.Value)) { return -1; } else if (RulesByPage[other.Value].Contains(this.Value)) { return 1; } throw new Exception("Unable to compare!"); } } public class OrderingRule { public OrderingRule(string line) { LeftPage = Int32.Parse(line.Split('|')[0]); RightPage = Int32.Parse(line.Split('|')[1]); } public int LeftPage { get; set; } public int RightPage { get; set; } } public class Update { public Update(string[] pages) { foreach (var page in pages) { Pages.Add(Int32.Parse(page)); } } public List Pages { get; set; } = new List(); } public Dictionary> RulesByPage { get; set; } = new Dictionary>(); public List Rules { get; set; } = new List(); public List Updates { get; set; } = new List(); public Day05Parsed(string fileName) : base(fileName) { foreach (var rule in Rules) { if (RulesByPage.ContainsKey(rule.LeftPage)) { RulesByPage[rule.LeftPage].Add(rule.RightPage); } else { RulesByPage.Add(rule.LeftPage, new HashSet() { rule.RightPage }); } } Page.RulesByPage = RulesByPage; } public override int GetPart01() { List validUpdates = new List(); Updates.ForEach(update => { if (IsUpdateValid(update)) validUpdates.Add(update); }); // We have all the valid updates, now get the "middle" item int middlePageNumberSum = 0; foreach (var update in validUpdates) { middlePageNumberSum += update.Pages[update.Pages.Count / 2]; } return middlePageNumberSum; } public override int GetPart02() { List invalidUpdates = new List(); Updates.ForEach(update => { if (!IsUpdateValid(update)) invalidUpdates.Add(update); }); int middleSum = 0; foreach (var update in invalidUpdates) { List pages = new List(); foreach (var page in update.Pages) { pages.Add(new Page(page)); } pages.Sort(); middleSum += pages[pages.Count / 2].Value; } return middleSum; } private bool IsUpdateValid(Update update) { bool isUpdateValid = true; for (int pageIndex = 0; pageIndex < update.Pages.Count; pageIndex++) { if (!RulesByPage.ContainsKey(update.Pages[pageIndex])) { continue; } var rules = RulesByPage[update.Pages[pageIndex]]; // Make sure current page has rules that say its before all the subsequent pages for (int subIndex = pageIndex + 1; subIndex < update.Pages.Count; subIndex++) { if (!rules.Contains(update.Pages[subIndex])) { isUpdateValid = false; break; } } // Go backwards and make sure none of the previous pages have an existing rule for (int i = 0; i < pageIndex; i++) { if (rules.Contains(update.Pages[i])) { isUpdateValid = false; break; } } } return isUpdateValid; } public override bool ParseLine(string line, object? context = null) { if (line.Contains('|')) { // It's a rule Rules.Add(new OrderingRule(line)); } else if (!String.IsNullOrWhiteSpace(line)) { // Must be an update string[] pages = line.Split(','); Updates.Add(new Update(pages)); } return true; } }