Add day 15
This commit is contained in:
@@ -118,7 +118,7 @@ public class Day14 : Day
|
|||||||
public override int Number => 14;
|
public override int Number => 14;
|
||||||
public override string Name => "Regolith Reservoir";
|
public override string Name => "Regolith Reservoir";
|
||||||
|
|
||||||
private Point _sandSource = new(500, 0);
|
private readonly Point _sandSource = new(500, 0);
|
||||||
private Point _offsetSandSource;
|
private Point _offsetSandSource;
|
||||||
|
|
||||||
public override void RunPart1()
|
public override void RunPart1()
|
||||||
@@ -161,7 +161,7 @@ public class Day14 : Day
|
|||||||
AnsiConsole.MarkupLine($"[green]Total sand blocks: [yellow]{sandBlocksCount}[/][/]");
|
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
|
// Stop if source is blocked
|
||||||
if (map[sandSource.X, sandSource.Y] == CellType.Sand)
|
if (map[sandSource.X, sandSource.Y] == CellType.Sand)
|
||||||
@@ -230,7 +230,7 @@ public class Day14 : Day
|
|||||||
return true;
|
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
|
// Stop if source is blocked
|
||||||
if (map.GetValueOrDefault(sandSource, CellType.Air) == CellType.Sand)
|
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()
|
var paths = Input.ReadAllLines()
|
||||||
.Select(line => new MapPath(line.Split(" -> ")
|
.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