AdventOfCode2025/AdventOfCode2025/Solutions/Day9.cs

119 lines
No EOL
3.6 KiB
C#

using Position = (long X, long Y);
namespace AdventOfCode2025.Solutions;
public class Day9() : Solution(9)
{
private const char SplitChar = ',';
public override string SolvePart1() =>
GetPositionCombinations(GetPositions().ToList())
.Select(combination => CalculateRectangleArea(combination.Pos1, combination.Pos2))
.Max()
.ToString();
public override string SolvePart2()
{
List<Position> positions = GetPositions().ToList();
// var areas =
// GetPositionCombinations(positions)
// .Select(GetRectangleCorners)
// .Where(corners => corners.All(pos => IsPointInsidePolygon(pos, positions)))
// .Select(corners => CalculateRectangleArea(corners[0], corners[2]))
// .Max();
var areas =
GetPositionCombinations(positions)
.AsParallel()
.Where(combination => AreAllRectanglePointsInsidePolygon(combination.Pos1, combination.Pos2, positions))
.Select(combination => CalculateRectangleArea(combination.Pos1, combination.Pos2))
.Max();
return areas.ToString();
}
private static bool AreAllRectanglePointsInsidePolygon(Position pos1, Position pos2, List<Position> polygon)
{
var minX = Math.Min(pos1.X, pos2.X);
var maxX = Math.Max(pos1.X, pos2.X);
var minY = Math.Min(pos1.Y, pos2.Y);
var maxY = Math.Max(pos1.Y, pos2.Y);
if (GetRectangleCorners(pos1, pos2).All(corner => IsPointInsidePolygon(corner, polygon)))
{
return false;
}
for (var x = minX; x <= maxX; x++)
{
for (var y = minY; y <= maxY; y++)
{
if (!IsPointInsidePolygon((x, y), polygon))
{
return false;
}
}
}
return true;
}
// ray-casting algorithm or even-odd rule
private static bool IsPointInsidePolygon(Position point, List<Position> polygon)
{
var intersections = 0;
var n = polygon.Count;
for (var i = 0; i < n; i++)
{
var p1 = polygon[i];
var p2 = polygon[(i + 1) % n];
if (p1.Y > point.Y != p2.Y > point.Y && point.X < (p2.X - p1.X) * (point.Y - p1.Y) / (p2.Y - p1.Y) + p1.X)
{
intersections++;
}
}
return intersections % 2 == 1;
}
private static IEnumerable<(Position Pos1, Position Pos2)> GetPositionCombinations(List<Position> positions)
{
for (var i = 0; i < positions.Count; i++)
{
for (var j = i + 1; j < positions.Count; j++)
{
yield return (positions.ElementAt(i), positions.ElementAt(j));
}
}
}
private static long CalculateRectangleArea(Position pos1, Position pos2)
{
var length = Math.Abs(pos1.X - pos2.X) + 1;
var width = Math.Abs(pos1.Y - pos2.Y) + 1;
return length * width;
}
private static Position[] GetRectangleCorners(Position pos1, Position pos2)
{
var minX = Math.Min(pos1.X, pos2.X);
var maxX = Math.Max(pos1.X, pos2.X);
var minY = Math.Min(pos1.Y, pos2.Y);
var maxY = Math.Max(pos1.Y, pos2.Y);
return
[
(minX, minY),
(maxX, minY),
(maxX, maxY),
(minX, maxY)
];
}
private IEnumerable<Position> GetPositions() => Input.Lines.Select(line =>
{
var values = line.Split(SplitChar);
return (X: long.Parse(values[0]), Y: long.Parse(values[1]));
});
}