Files
AdventOfCode/Days/Day8.cs
2025-12-13 17:55:37 +01:00

221 lines
6.1 KiB
C#

using System.Buffers;
using System.Numerics;
using Spectre.Console;
namespace AdventOfCode.Days;
public class Day8 : Day
{
public override int Number => 8;
public override string Name => "Playground";
public override void RunPart1(bool display = true)
{
var boxPositions = ParseBoxPositions(Input);
var boxPairDistances = FetchOrderedDistances(boxPositions);
var circuits = new List<HashSet<Box>>();
var connectionsCount = 0;
while (connectionsCount < 1000)
{
// Fetch next minimal distance pair. This is always the last pair in our ordered list
var (pairLeftBox, pairRightBox, _) = boxPairDistances[^1];
boxPairDistances.RemoveAt(boxPairDistances.Count - 1);
// Create a connection if those two boxes are not already connected
if (!ConnectionExists(circuits, pairLeftBox, pairRightBox))
{
// Connect the boxes, eventually merging circuits
ConnectBoxes(circuits, pairLeftBox, pairRightBox);
}
connectionsCount++;
}
var result = circuits
.OrderByDescending(circuit => circuit.Count)
.Take(3)
.Aggregate(1, (product, circuit) => product * circuit.Count);
if (display)
{
AnsiConsole.MarkupLine($"[green]Result is: [yellow]{result}[/][/]");
}
}
public override void RunPart2(bool display = true)
{
var boxPositions = ParseBoxPositions(Input);
var boxPairDistances = FetchOrderedDistances(boxPositions);
var circuits = new List<HashSet<Box>>();
long result;
while (true)
{
// Fetch next minimal distance pair. This is always the last pair in our ordered list
var (pairLeftBox, pairRightBox, _) = boxPairDistances[^1];
boxPairDistances.RemoveAt(boxPairDistances.Count - 1);
// Create a connection if those two boxes are not already connected
if (!ConnectionExists(circuits, pairLeftBox, pairRightBox))
{
// Connect the boxes, eventually merging circuits
ConnectBoxes(circuits, pairLeftBox, pairRightBox);
}
// Check if there is a circuit with all boxes connected
if (circuits[0].Count >= 1000)
{
result = pairLeftBox.X * pairRightBox.X;
break;
}
}
if (display)
{
AnsiConsole.MarkupLine($"[green]Result is: [yellow]{result}[/][/]");
}
}
private static double BoxDistance(Box left, Box right)
{
return Math.Sqrt(
(right.X - left.X) * (right.X - left.X) +
(right.Y - left.Y) * (right.Y - left.Y) +
(right.Z - left.Z) * (right.Z - left.Z)
);
}
static bool ConnectionExists(List<HashSet<Box>> circuits, Box left, Box right)
{
foreach (var circuit in circuits)
{
if (circuit.Contains(left) && circuit.Contains(right))
{
return true;
}
}
return false;
}
static void ConnectBoxes(List<HashSet<Box>> circuits, Box pairLeftBox, Box pairRightBox)
{
// Try to find circuit containing each box
HashSet<Box>? leftCircuit = null;
HashSet<Box>? rightCircuit = null;
foreach (var circuit in circuits)
{
if (circuit.Contains(pairLeftBox))
{
leftCircuit = circuit;
}
if (circuit.Contains(pairRightBox))
{
rightCircuit = circuit;
}
}
// None is in a circuit, create a new one
if (leftCircuit is null && rightCircuit is null)
{
var circuit = new HashSet<Box>
{
pairLeftBox,
pairRightBox
};
circuits.Add(circuit);
}
// One of the two is null
else if (leftCircuit is not null && rightCircuit is null)
{
leftCircuit.Add(pairRightBox);
}
else if (leftCircuit is null && rightCircuit is not null)
{
rightCircuit.Add(pairLeftBox);
}
// Both are already in a circuit, merge them
else
{
foreach (var box in rightCircuit!)
{
leftCircuit!.Add(box);
}
circuits.Remove(rightCircuit);
}
}
private static List<Box> ParseBoxPositions(string input)
{
var positions = new List<Box>();
foreach (var line in input.EnumerateLines())
{
var split = line.Split(',');
split.MoveNext();
var x = long.Parse(line[split.Current]);
split.MoveNext();
var y = long.Parse(line[split.Current]);
split.MoveNext();
var z = long.Parse(line[split.Current]);
positions.Add(new Box(x, y, z));
}
return positions;
}
private static List<BoxPairDistance> FetchOrderedDistances(List<Box> boxes)
{
var boxPairDistances = new List<BoxPairDistance>();
for (var i = 0; i < boxes.Count - 1; i++)
{
for (var j = i + 1; j < boxes.Count; j++)
{
var leftBox = boxes[i];
var rightBox = boxes[j];
var distance = BoxDistance(leftBox, rightBox);
boxPairDistances.Add(new BoxPairDistance(leftBox, rightBox, distance));
}
}
// Sort in reverse order
boxPairDistances.Sort((left, right) => right.CompareTo(left));
return boxPairDistances;
}
private record Box(long X, long Y, long Z);
private record BoxPairDistance(Box Left, Box Right, double Distance) : IComparable<BoxPairDistance>
{
public int CompareTo(BoxPairDistance? other)
{
if (ReferenceEquals(this, other))
{
return 0;
}
if (other is null)
{
return 1;
}
return Distance.CompareTo(other.Distance);
}
}
}