192 lines
6.3 KiB
C#
192 lines
6.3 KiB
C#
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);
|
|
} |