Add day 15
This commit is contained in:
@@ -118,7 +118,7 @@ public class Day14 : Day
|
||||
public override int Number => 14;
|
||||
public override string Name => "Regolith Reservoir";
|
||||
|
||||
private Point _sandSource = new(500, 0);
|
||||
private readonly Point _sandSource = new(500, 0);
|
||||
private Point _offsetSandSource;
|
||||
|
||||
public override void RunPart1()
|
||||
@@ -161,7 +161,7 @@ public class Day14 : Day
|
||||
AnsiConsole.MarkupLine($"[green]Total sand blocks: [yellow]{sandBlocksCount}[/][/]");
|
||||
}
|
||||
|
||||
private bool GenerateSand(CellType[,] map, Canvas canvas, in Point sandSource)
|
||||
private static bool GenerateSand(CellType[,] map, Canvas canvas, in Point sandSource)
|
||||
{
|
||||
// Stop if source is blocked
|
||||
if (map[sandSource.X, sandSource.Y] == CellType.Sand)
|
||||
@@ -230,7 +230,7 @@ public class Day14 : Day
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool GenerateSand(IDictionary<Point, CellType> map, in Point sandSource, int groundY)
|
||||
private static bool GenerateSand(IDictionary<Point, CellType> map, in Point sandSource, int groundY)
|
||||
{
|
||||
// Stop if source is blocked
|
||||
if (map.GetValueOrDefault(sandSource, CellType.Air) == CellType.Sand)
|
||||
@@ -374,7 +374,7 @@ public class Day14 : Day
|
||||
};
|
||||
}
|
||||
|
||||
private IDictionary<Point, CellType> GenerateDictionaryMap()
|
||||
private static IDictionary<Point, CellType> GenerateDictionaryMap()
|
||||
{
|
||||
var paths = Input.ReadAllLines()
|
||||
.Select(line => new MapPath(line.Split(" -> ")
|
||||
|
||||
286
Days/Day15.cs
Normal file
286
Days/Day15.cs
Normal file
@@ -0,0 +1,286 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Drawing;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace AdventOfCode.Days;
|
||||
|
||||
public class SensorBeaconPair
|
||||
{
|
||||
public Point SensorPosition { get; }
|
||||
public Point BeaconPosition { get; }
|
||||
|
||||
public int SensorRange =>
|
||||
Math.Abs(SensorPosition.X - BeaconPosition.X) + Math.Abs(SensorPosition.Y - BeaconPosition.Y);
|
||||
|
||||
public SensorBeaconPair(in Point sensorPosition, in Point beaconPosition)
|
||||
{
|
||||
SensorPosition = sensorPosition;
|
||||
BeaconPosition = beaconPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public class CoverRange
|
||||
{
|
||||
public int Start { get; }
|
||||
public int End { get; }
|
||||
public int Covered => End - Start + 1;
|
||||
|
||||
public CoverRange(int start, int end)
|
||||
{
|
||||
Start = start;
|
||||
End = end;
|
||||
}
|
||||
|
||||
public CoverRange(CoverRange coverRange)
|
||||
{
|
||||
Start = coverRange.Start;
|
||||
End = coverRange.End;
|
||||
}
|
||||
|
||||
public CoverRange Subtract(CoverRange other)
|
||||
{
|
||||
// Overlap on left
|
||||
int newStart;
|
||||
if (other.Start <= Start && other.End >= Start)
|
||||
{
|
||||
newStart = other.End + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
newStart = Start;
|
||||
}
|
||||
|
||||
// Overlap on right
|
||||
int newEnd;
|
||||
if (other.Start <= End && other.End >= End)
|
||||
{
|
||||
newEnd = other.Start - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
newEnd = End;
|
||||
}
|
||||
|
||||
return new CoverRange(newStart, newEnd);
|
||||
}
|
||||
|
||||
public static CoverRange operator -(CoverRange left, CoverRange right)
|
||||
{
|
||||
return left.Subtract(right);
|
||||
}
|
||||
}
|
||||
|
||||
public class Day15 : Day
|
||||
{
|
||||
public override int Number => 15;
|
||||
public override string Name => "Beacon Exclusion Zone";
|
||||
public override void RunPart1()
|
||||
{
|
||||
const int targetY = 2_000_000;
|
||||
|
||||
// Parse pairs
|
||||
var pairs = ParsePairs();
|
||||
|
||||
var ranges = new List<CoverRange>(pairs.Count);
|
||||
|
||||
// Get which columns are included on line targetY, remove the ones which already contains a beacon
|
||||
var beaconsOnTargetY = new HashSet<int>();
|
||||
foreach (var sensorBeaconPair in pairs)
|
||||
{
|
||||
// Get covered columns on y line
|
||||
var xCenter = sensorBeaconPair.SensorPosition.X;
|
||||
var coveredColumns = sensorBeaconPair.SensorRange - Math.Abs(sensorBeaconPair.SensorPosition.Y - targetY);
|
||||
|
||||
if (coveredColumns > 0)
|
||||
{
|
||||
ranges.Add(new CoverRange(xCenter - coveredColumns, xCenter + coveredColumns));
|
||||
|
||||
// Check if the beacon is on targetY
|
||||
if (sensorBeaconPair.BeaconPosition.Y == targetY)
|
||||
{
|
||||
beaconsOnTargetY.Add(sensorBeaconPair.BeaconPosition.X);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove overlapped ranges => inner.Start >= outer.Start && inner.End <= outer.End));
|
||||
ranges.RemoveAll(inner =>
|
||||
ranges.Where(outer => outer != inner).Any(outer => inner.Start >= outer.Start && inner.End <= outer.End));
|
||||
|
||||
// Compute unique number of x columns
|
||||
long totalCovered = 0;
|
||||
for (int i = 0; i < ranges.Count; i++)
|
||||
{
|
||||
var range = ranges[i];
|
||||
var coverRange = range;
|
||||
|
||||
// Add number of covered columns and then subtract common ones
|
||||
for (int j = i + 1; j < ranges.Count; j++)
|
||||
{
|
||||
var exclude = ranges[j];
|
||||
|
||||
coverRange -= exclude;
|
||||
}
|
||||
|
||||
totalCovered += coverRange.Covered;
|
||||
}
|
||||
|
||||
// Remove lines which contains a beacon
|
||||
totalCovered -= beaconsOnTargetY.Count;
|
||||
|
||||
// Print total covered columns on line Y
|
||||
AnsiConsole.MarkupLine($"[green]Number of positions that cannot contain a beacon: [yellow]{totalCovered}[/][/]");
|
||||
}
|
||||
|
||||
public override void RunPart2()
|
||||
{
|
||||
const int xMin = 0;
|
||||
const int xMax = 4_000_000;
|
||||
const int yMin = 0;
|
||||
const int yMax = 4_000_000;
|
||||
|
||||
bool IsOutbound(Point point) => point.X is < xMin or > xMax || point.Y is < yMin or > yMax;
|
||||
|
||||
// Parse pairs
|
||||
var pairs = ParsePairs();
|
||||
|
||||
// Find all points that are just outside a sensor range
|
||||
var possiblePoints = new ConcurrentBag<Point>();
|
||||
|
||||
Parallel.For(0, pairs.Count, i =>
|
||||
{
|
||||
var pair = pairs[i];
|
||||
|
||||
var sensor = pair.SensorPosition;
|
||||
var distance = pair.SensorRange + 1;
|
||||
|
||||
for (int x = -distance; x <= distance; x++)
|
||||
{
|
||||
var y = distance - Math.Abs(x);
|
||||
var positivePoint = new Point(sensor.X + x, sensor.Y + y);
|
||||
var negativePoint = new Point(sensor.X + x, sensor.Y - y);
|
||||
|
||||
// If both points are outbound, just skip
|
||||
var positiveIsOutbound = IsOutbound(positivePoint);
|
||||
var negativeIsOutbound = IsOutbound(negativePoint);
|
||||
|
||||
if (positiveIsOutbound && negativeIsOutbound)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if this point is just outside at least one other sensor range
|
||||
for (int otherIndex = 0; otherIndex < pairs.Count; otherIndex++)
|
||||
{
|
||||
if (otherIndex == i)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var otherPair = pairs[otherIndex];
|
||||
|
||||
var otherSensor = otherPair.SensorPosition;
|
||||
var otherDistance = otherPair.SensorRange + 1;
|
||||
|
||||
if (!positiveIsOutbound)
|
||||
{
|
||||
var distancePositive = Math.Abs(positivePoint.X - otherSensor.X) +
|
||||
Math.Abs(positivePoint.Y - otherSensor.Y);
|
||||
|
||||
if (distancePositive == otherDistance)
|
||||
{
|
||||
possiblePoints.Add(positivePoint);
|
||||
}
|
||||
}
|
||||
|
||||
if (!negativeIsOutbound)
|
||||
{
|
||||
var distanceNegative = Math.Abs(negativePoint.X - negativePoint.X) +
|
||||
Math.Abs(negativePoint.Y - negativePoint.Y);
|
||||
|
||||
if (distanceNegative == otherDistance)
|
||||
{
|
||||
possiblePoints.Add(negativePoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Keep the only outside detection point
|
||||
var finalPoint = possiblePoints.First(f =>
|
||||
pairs.All(p => Math.Abs(f.X - p.SensorPosition.X) + Math.Abs(f.Y - p.SensorPosition.Y) > p.SensorRange));
|
||||
|
||||
var tuningFrequency = finalPoint.X * (long) 4_000_000 + finalPoint.Y;
|
||||
|
||||
AnsiConsole.MarkupLine($"[green]Tuning frequency: [yellow]{tuningFrequency}[/][/]");
|
||||
}
|
||||
|
||||
private static IList<SensorBeaconPair> ParsePairs()
|
||||
{
|
||||
var pairs = new List<SensorBeaconPair>();
|
||||
|
||||
foreach (var line in Input.ReadAllLines())
|
||||
{
|
||||
var span = line.AsSpan();
|
||||
|
||||
// Parse sensor position
|
||||
int sensorXStart = span.IndexOf('=') + 1;
|
||||
int sensorXEnd = span.IndexOf(',');
|
||||
|
||||
var sensorX = int.Parse(span[sensorXStart..sensorXEnd]);
|
||||
span = span[sensorXEnd..];
|
||||
|
||||
int sensorYStart = span.IndexOf('=') + 1;
|
||||
int sensorYEnd = span.IndexOf(':');
|
||||
|
||||
var sensorY = int.Parse(span[sensorYStart..sensorYEnd]);
|
||||
span = span[sensorYEnd..];
|
||||
|
||||
// Parse beacon
|
||||
int beaconXStart = span.IndexOf('=') + 1;
|
||||
int beaconXEnd = span.IndexOf(',');
|
||||
|
||||
var beaconX = int.Parse(span[beaconXStart..beaconXEnd]);
|
||||
span = span[beaconXEnd..];
|
||||
|
||||
int beaconYStart = span.IndexOf('=') + 1;
|
||||
|
||||
var beaconY = int.Parse(span[beaconYStart..]);
|
||||
|
||||
pairs.Add(new SensorBeaconPair(new Point(sensorX, sensorY), new Point(beaconX, beaconY)));
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
|
||||
#region Input
|
||||
|
||||
public const string Input =
|
||||
"""
|
||||
Sensor at x=1384790, y=3850432: closest beacon is at x=2674241, y=4192888
|
||||
Sensor at x=2825953, y=288046: closest beacon is at x=2154954, y=-342775
|
||||
Sensor at x=3553843, y=2822363: closest beacon is at x=3444765, y=2347460
|
||||
Sensor at x=2495377, y=3130491: closest beacon is at x=2761496, y=2831113
|
||||
Sensor at x=1329263, y=1778185: closest beacon is at x=2729595, y=2000000
|
||||
Sensor at x=2882039, y=2206085: closest beacon is at x=2729595, y=2000000
|
||||
Sensor at x=3903141, y=2510440: closest beacon is at x=4006219, y=3011198
|
||||
Sensor at x=3403454, y=3996578: closest beacon is at x=3754119, y=4475047
|
||||
Sensor at x=3630476, y=1048796: closest beacon is at x=3444765, y=2347460
|
||||
Sensor at x=16252, y=2089672: closest beacon is at x=-276514, y=2995794
|
||||
Sensor at x=428672, y=1150723: closest beacon is at x=-281319, y=668868
|
||||
Sensor at x=2939101, y=3624676: closest beacon is at x=2674241, y=4192888
|
||||
Sensor at x=3166958, y=2890076: closest beacon is at x=2761496, y=2831113
|
||||
Sensor at x=3758241, y=3546895: closest beacon is at x=4006219, y=3011198
|
||||
Sensor at x=218942, y=3011070: closest beacon is at x=-276514, y=2995794
|
||||
Sensor at x=52656, y=3484635: closest beacon is at x=-276514, y=2995794
|
||||
Sensor at x=2057106, y=405314: closest beacon is at x=2154954, y=-342775
|
||||
Sensor at x=1966905, y=2495701: closest beacon is at x=2761496, y=2831113
|
||||
Sensor at x=511976, y=2696731: closest beacon is at x=-276514, y=2995794
|
||||
Sensor at x=3094465, y=2478570: closest beacon is at x=3444765, y=2347460
|
||||
Sensor at x=806671, y=228252: closest beacon is at x=-281319, y=668868
|
||||
Sensor at x=3011731, y=1976307: closest beacon is at x=2729595, y=2000000
|
||||
""";
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user