I've been having a bad day, so I'm putting on my favorite Future Funk mix to listen to while doing this. Highly reccomend it if you need a mood lifter, or just something with good energy to it.
More 2D grids - fun!
I think I can predict how this problem will go for part 2, but for now I'm just going to take this one step at a time. But for part 1 at least, it's a simple pathfinding algorithm.
public static List<(int, int)> GetShortestPath<T>(List<List<T>> grid, (int, int) start, (int, int) end, Func<T, bool> spotValidator)
{
var seen = new HashSet<(int, int)>();
var queue = new Queue<ImmutableList<(int, int)>>();
queue.Enqueue(ImmutableList.Create(start));
while (queue.Any())
{
var curPath = queue.Dequeue();
var (x, y) = curPath.Last();
if (seen.Contains((x, y))) continue;
seen.Add((x, y));
if (!IsInBounds(grid, x, y)) continue;
if (!spotValidator(grid[x][y])) continue;
if ((x, y) == end) return curPath.ToList();
queue.Enqueue(curPath.Add((x + 1, y)));
queue.Enqueue(curPath.Add((x, y + 1)));
queue.Enqueue(curPath.Add((x - 1, y)));
queue.Enqueue(curPath.Add((x, y - 1)));
}
return new List<(int, int)>();
}
I made a more generic (but still basic) BFS pathfinding function for my utilities; pass in a grid, start, end, and space validator, and it simply does a BFS! Now to call it it's simply:
var shortestPath = Utils.GetShortestPath(grid, (0, 0), (GRID_SIZE, GRID_SIZE), c => c != '#');
Haha, wow, this Part 2 is way easier than I was expecting. Just find the first byte that fully blocks the path. Just drop a byte at a time and find a path; the first one that can't make it is the byte!
foreach (var (x, y) in incomingBytes)
{
grid[x][y] = '#';
if (Utils.GetShortestPath(grid, (0, 0), (GRID_SIZE, GRID_SIZE), c => c != '#').Count == 0)
{
Console.WriteLine($"Byte: {y},{x}");
break;
}
}