[2k25] Add day 9

This commit is contained in:
2026-01-18 11:56:23 +01:00
parent ded4631c3b
commit f1ad810991
2 changed files with 688 additions and 0 deletions

192
Days/Day9.cs Normal file
View File

@@ -0,0 +1,192 @@
using Spectre.Console;
namespace AdventOfCode.Days;
public class Day9 : Day
{
public override int Number => 9;
public override string Name => "Movie Theater";
public override void RunPart1(bool display = true)
{
var tilePositions = ParseTilePositions(Input);
var maxArea = 0L;
for (var firstIndex = 0; firstIndex < tilePositions.Count - 1; firstIndex++)
{
for (var secondIndex = firstIndex + 1; secondIndex < tilePositions.Count; secondIndex++)
{
var firstTile = tilePositions[firstIndex];
var secondTile = tilePositions[secondIndex];
var area = (Math.Abs(secondTile.I - firstTile.I) + 1) *
(Math.Abs(secondTile.J - firstTile.J) + 1);
if (area > maxArea)
{
maxArea = area;
}
}
}
if (display)
{
AnsiConsole.MarkupLine($"[green]Max tile area is: [yellow]{maxArea}[/][/]");
}
}
public override void RunPart2(bool display = true)
{
var tilePositions = ParseTilePositions(Input);
// Make a list of possible areas in descending order
var tileAreas = new List<TileArea>();
for (var firstIndex = 0; firstIndex < tilePositions.Count - 1; firstIndex++)
{
for (var secondIndex = firstIndex + 1; secondIndex < tilePositions.Count; secondIndex++)
{
var firstTile = tilePositions[firstIndex];
var secondTile = tilePositions[secondIndex];
var area = (Math.Abs(secondTile.I - firstTile.I) + 1) *
(Math.Abs(secondTile.J - firstTile.J) + 1);
tileAreas.Add(new TileArea(firstTile, secondTile, area));
}
}
// Sort list in descending order (biggest area first)
tileAreas.Sort((left, right) => right.Area.CompareTo(left.Area));
// Compute polygon bounds with green tiles
var jBoundsAtI = new Dictionary<long, PositionBounds>();
var iBoundsAtJ = new Dictionary<long, PositionBounds>();
foreach (var (firstTile, secondTile) in tilePositions.Zip(tilePositions.Skip(1).Append(tilePositions[0])))
{
// Tiles are on same line, set bounds for columns (j)
if (firstTile.I == secondTile.I)
{
var start = Math.Min(firstTile.J, secondTile.J);
var end = Math.Max(firstTile.J, secondTile.J);
for (var j = start; j <= end; j++)
{
// Update bounds
if (iBoundsAtJ.TryGetValue(j, out var bounds))
{
iBoundsAtJ[j] = new PositionBounds(
Math.Min(firstTile.I, bounds.Min),
Math.Max(firstTile.I, bounds.Max)
);
}
else
{
iBoundsAtJ[j] = new PositionBounds(
firstTile.I,
firstTile.I
);
}
}
}
// Tiles are on same column, set bounds for rows (i)
else if (firstTile.J == secondTile.J)
{
var start = Math.Min(firstTile.I, secondTile.I);
var end = Math.Max(firstTile.I, secondTile.I);
for (var i = start; i<= end; i++)
{
// Update bounds
if (jBoundsAtI.TryGetValue(i, out var bounds))
{
jBoundsAtI[i] = new PositionBounds(
Math.Min(firstTile.J, bounds.Min),
Math.Max(firstTile.J, bounds.Max)
);
}
else
{
jBoundsAtI[i] = new PositionBounds(
firstTile.J,
firstTile.J
);
}
}
}
}
// Find the first area that is valid (composed of green or red tiles)
var maxValidArea = -1L;
foreach (var tileArea in tileAreas)
{
// For each point in this area, check if it is within bounds
var iStart = Math.Min(tileArea.First.I, tileArea.Second.I);
var iEnd = Math.Max(tileArea.First.I, tileArea.Second.I);
var jStart = Math.Min(tileArea.First.J, tileArea.Second.J);
var jEnd = Math.Max(tileArea.First.J, tileArea.Second.J);
// Check if any point is out of bounds
var outOfBounds = false;
for (var i = iStart; i <= iEnd; i++)
{
for (var j = jStart; j <= jEnd; j++)
{
var iBound = iBoundsAtJ[j];
var jBound = jBoundsAtI[i];
if (i < iBound.Min || i > iBound.Max ||
j < jBound.Min || j > jBound.Max)
{
outOfBounds = true;
break;
}
}
if (outOfBounds)
{
break;
}
}
// If no points are out of bounds, we have our biggest valid area
if (!outOfBounds)
{
maxValidArea = tileArea.Area;
break;
}
}
if (display)
{
AnsiConsole.MarkupLine($"[green]Max valid tile area is: [yellow]{maxValidArea}[/][/]");
}
}
private static List<TilePosition> ParseTilePositions(string input)
{
var tilePositions = new List<TilePosition>();
foreach (var line in input.EnumerateLines())
{
var splitIndex = line.IndexOf(',');
var j = long.Parse(line[..splitIndex]);
var i = long.Parse(line[(splitIndex + 1)..]);
tilePositions.Add(new TilePosition(i, j));
}
return tilePositions;
}
private record TilePosition(long I, long J);
private record TileArea(TilePosition First, TilePosition Second, long Area);
private record PositionBounds(long Min, long Max);
}