-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
3 changed files
with
3,620 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
namespace AdventOfCode.Solutions._2024; | ||
|
||
/// <summary> | ||
/// Day 18: RAM Run | ||
/// https://adventofcode.com/2024/day/18 | ||
/// </summary> | ||
[Description("RAM Run")] | ||
public static partial class Day18 { | ||
|
||
private static List<Point> _bytes = default!; | ||
private static Action<string[], bool>? _visualise = null; | ||
|
||
[Init] | ||
public static void Init(string[] input, Action<string[], bool>? visualise = null) | ||
{ | ||
_bytes = [.. input.Select(i => i.As<Point>())]; | ||
_visualise = visualise; | ||
} | ||
|
||
public static int Part1(string[] _, params object[]? args) | ||
{ | ||
int gridSize = args.GridSize(); | ||
int noOfBytes = args.Bytes(); | ||
|
||
Point start = Point.Zero; | ||
Point end = new(gridSize - 1, gridSize - 1); | ||
|
||
_bytes.Take(noOfBytes).VisualiseRam("Initial state:", gridSize, []); | ||
|
||
List<Point> shortestPath = FindShortestPath(start, end, _bytes.Take(noOfBytes), gridSize); | ||
|
||
_bytes.Take(noOfBytes).VisualiseRam("Final:", gridSize, shortestPath); | ||
|
||
return shortestPath.Count - 1; // shortestPath includes start | ||
} | ||
|
||
public static List<Point> FindShortestPath(Point start, Point goal, IEnumerable<Point> obstacles, int gridSize) | ||
{ | ||
PriorityQueue<Point, int> priorityQueue = new(); | ||
Dictionary<Point, int> cost = new() { [start] = 0 }; | ||
Dictionary<Point, int> distances = new() { [start] = start.ManhattanDistance(goal) }; | ||
Dictionary<Point, Point> previous = []; | ||
|
||
HashSet<Point> obstaclesSet = [.. obstacles]; | ||
|
||
priorityQueue.Enqueue(start, distances[start]); | ||
|
||
while (priorityQueue.Count > 0) { | ||
Point current = priorityQueue.Dequeue(); | ||
|
||
if (current == goal) { | ||
return ReconstructPath(previous, current); | ||
} | ||
|
||
foreach (Direction direction in Directions.NSEW) { | ||
Point adjacent = current + direction.Delta(); | ||
|
||
// Ignore invalid neighbours | ||
if (adjacent.X < 0 || adjacent.Y < 0 || adjacent.X >= gridSize || adjacent.Y >= gridSize | ||
|| obstaclesSet.Contains(adjacent)) { | ||
continue; | ||
} | ||
|
||
int tentativeCost = cost[current] + 1; | ||
|
||
if (tentativeCost < cost.GetValueOrDefault(adjacent, int.MaxValue)) { | ||
previous[adjacent] = current; | ||
cost[adjacent] = tentativeCost; | ||
distances[adjacent] = tentativeCost + adjacent.ManhattanDistance(goal); | ||
|
||
// Add to priority queue with updated distance score | ||
priorityQueue.Enqueue(adjacent, distances[adjacent]); | ||
} | ||
} | ||
} | ||
|
||
return []; // No path found | ||
} | ||
|
||
private static List<Point> ReconstructPath(Dictionary<Point, Point> cameFrom, Point current) | ||
{ | ||
List<Point> totalPath = [current]; | ||
|
||
while (cameFrom.ContainsKey(current)) { | ||
current = cameFrom[current]; | ||
totalPath.Insert(0, current); | ||
} | ||
|
||
return totalPath; | ||
} | ||
|
||
|
||
public static string Part2(string[] input, params object[]? args) => NO_SOLUTION_WRITTEN_MESSAGE; | ||
|
||
private static void VisualiseRam(this IEnumerable<Point> bytes, string title, int gridSize, IEnumerable<Point> route, bool clearScreen = false) | ||
{ | ||
if (_visualise is null) { | ||
return; | ||
} | ||
|
||
char[,] outputRamMap = new char[gridSize, gridSize]; | ||
outputRamMap.FillInPlace('.'); | ||
|
||
foreach (Point position in bytes ?? []) { | ||
outputRamMap[position.X, position.Y] = '#'; | ||
} | ||
|
||
foreach (Point position in route ?? []) { | ||
outputRamMap[position.X, position.Y] = 'O'; | ||
} | ||
|
||
string[] output = ["", title, .. outputRamMap.AsStrings()/*.Select(s => s.Replace('.', ' '))*/]; | ||
_visualise?.Invoke(output, clearScreen); | ||
} | ||
|
||
private static int GridSize(this object[]? args) => GetArgument(args, 1, 71); | ||
private static int Bytes(this object[]? args) => GetArgument(args, 2, 1024); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
namespace AdventOfCode.Tests.Year2024; | ||
|
||
public class Tests_18_RAM_Run(ITestOutputHelper testOutputHelper) | ||
{ | ||
const int DAY = 18; | ||
|
||
[Theory] | ||
[InlineData(""" | ||
5,4 | ||
4,2 | ||
4,5 | ||
3,0 | ||
2,1 | ||
6,3 | ||
2,4 | ||
1,5 | ||
0,6 | ||
3,3 | ||
2,6 | ||
5,1 | ||
1,2 | ||
5,5 | ||
2,5 | ||
6,5 | ||
1,4 | ||
0,4 | ||
6,4 | ||
1,1 | ||
6,1 | ||
1,0 | ||
0,5 | ||
1,6 | ||
2,0 | ||
""", 7, 12, 22)] | ||
public void Part1(string input, int gridSize, int bytes, int expected) | ||
{ | ||
_ = int.TryParse(SolutionRouter.SolveProblem(YEAR, DAY, PART1, input, new Action<string[], bool>(Callback), gridSize, bytes), out int actual); | ||
actual.ShouldBe(expected); | ||
} | ||
|
||
|
||
private void Callback(string[] lines, bool _) | ||
{ | ||
if (lines is null or []) { | ||
return; | ||
} | ||
|
||
testOutputHelper.WriteLine(string.Join(Environment.NewLine, lines)); | ||
} | ||
|
||
} |