221 lines
6.1 KiB
C#
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);
|
|
}
|
|
}
|
|
} |