119 lines
No EOL
3.6 KiB
C#
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]));
|
|
});
|
|
} |