using System; using System.Collections.Generic; using System.Data.Common; using System.IO; using System.Linq; using AdvenOfCode.Contracts; using Coordinate = (long x, long y); using MinMax = (long minX, long minY, long maxX, long maxY); namespace AoC_2025; public class Day09 : IPuzzleSolver { private Coordinate[] ParsePuzzleInput(string path) { var puzzleInput = File.ReadAllLines(path) .Where(line => !string.IsNullOrWhiteSpace(line)) .Select(line => line.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(str => long.Parse(str)).ToArray()) .Select(numbers => (numbers[0], numbers[1])) .ToArray(); return puzzleInput; } public long SolvePart1(string pathToPuzzleInput) { var redTiles = ParsePuzzleInput(pathToPuzzleInput); var possibleRectangles = GetAllPossibleRectanglesAsMinMax(redTiles); var areas = GetOrderedAreas(possibleRectangles); return areas.First(); } public long SolvePart2(string pathToPuzzleInput) { var redTiles = ParsePuzzleInput(pathToPuzzleInput); var borders = GetBordersAsMinMax(redTiles); var possibleRectangles = GetAllPossibleRectanglesAsMinMax(redTiles); var containedRectangles = GetRectanglesContainedInBorders(possibleRectangles, borders); var areas = GetOrderedAreas(containedRectangles); return areas.First(); } private IOrderedEnumerable GetOrderedAreas(IEnumerable<(long minX, long minY, long maxX, long maxY)> containedRectangles) { return containedRectangles .Select(rectangle => GetArea((rectangle.maxX, rectangle.maxY), (rectangle.minX, rectangle.minY))) .OrderDescending(); } private IEnumerable<(long minX, long minY, long maxX, long maxY)> GetRectanglesContainedInBorders(IEnumerable<(long minX, long minY, long maxX, long maxY)> possibleRectangles, (long minX, long minY, long maxX, long maxY)[] borders) { return possibleRectangles .Where(rectangle => IsRectangleInsideBorders(borders, rectangle)); } private IEnumerable<(long minX, long minY, long maxX, long maxY)> GetAllPossibleRectanglesAsMinMax(Coordinate[] redTiles) { return Combinations(redTiles).Select(x => AsMinMax(x.a, x.b)); } private MinMax[] GetBordersAsMinMax(Coordinate[] redTiles) { return GetBorder(redTiles).Select(x => AsMinMax(x.a, x.b)).ToArray(); } private bool IsRectangleInsideBorders((long minX, long minY, long maxX, long maxY)[] borders, (long minX, long minY, long maxX, long maxY) rectangle) { return !borders.Any(border => RectangleOverlapsBorder(rectangle, border)); } private MinMax AsMinMax(Coordinate a, Coordinate b) { return (Math.Min(a.x, b.x), Math.Min(a.y, b.y), Math.Max(a.x, b.x), Math.Max(a.y, b.y)); } private bool RectangleOverlapsBorder(MinMax rectangle, MinMax borderSegment) { return rectangle.minX < borderSegment.maxX && rectangle.minY < borderSegment.maxY && rectangle.maxX > borderSegment.minX && rectangle.maxY > borderSegment.minY; } private long GetArea(Coordinate tileA, Coordinate tileB) { return (Math.Abs(tileA.x - tileB.x) + 1) * (Math.Abs(tileA.y - tileB.y) + 1); } private IEnumerable<(Coordinate tileA, Coordinate tileB)> GetAllCombinationsSortedByArea(IEnumerable tiles) { var combinations = Combinations(tiles) .OrderByDescending(x => GetArea(x.a, x.b)); return combinations; } private IEnumerable<(TValue a, TValue b)> Combinations(IEnumerable values) { var enumeratedValues = values.Index().ToArray(); var pairs = from a in enumeratedValues from b in enumeratedValues where a.Index < b.Index select (a.Item, b.Item); return pairs; } private IEnumerable<(Coordinate a, Coordinate b)> GetBorder(IEnumerable tiles) { var allTiles = tiles.ToArray(); return allTiles.Zip([..allTiles[1..], allTiles[0]], (a, b) => (a, b)); } }