Files
AdventOfCode/2024/Day05CSharp/Program.cs
2025-11-30 20:28:10 -05:00

197 lines
5.4 KiB
C#

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<Page>
{
public static Dictionary<int, HashSet<int>> RulesByPage = new Dictionary<int, HashSet<int>>();
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<int> Pages { get; set; } = new List<int>();
}
public Dictionary<int, HashSet<int>> RulesByPage { get; set; } = new Dictionary<int, HashSet<int>>();
public List<OrderingRule> Rules { get; set; } = new List<OrderingRule>();
public List<Update> Updates { get; set; } = new List<Update>();
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<int>() { rule.RightPage });
}
}
Page.RulesByPage = RulesByPage;
}
public override int GetPart01()
{
List<Update> validUpdates = new List<Update>();
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<Update> invalidUpdates = new List<Update>();
Updates.ForEach(update => {
if (!IsUpdateValid(update)) invalidUpdates.Add(update);
});
int middleSum = 0;
foreach (var update in invalidUpdates)
{
List<Page> pages = new List<Page>();
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;
}
}