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(); 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(); var iBoundsAtJ = new Dictionary(); 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 ParseTilePositions(string input) { var tilePositions = new List(); 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); }