Files
2021/src/09.cs
Parnic 41f0d167e4 Report more accurate timings
The string colorizer is slow (but apparently only the first time it runs, for some reason?), printing can be slow, who knows what else the framework is doing, etc., so manually inserting Stop()s exactly where we want the timings to stop is the most reliable way to handle this. I like the elegance of the disposable solution, but my goal here is to measure the actual algorithm, not anything else.

A slightly restructuring would make it so I don't have to do this; if main.cs was receiving a string from Part1 and Part2 instead of having each day individually print its own results, we could scope timings to exclude any console writing. But whatever.

I also went ahead and made 17 only run once since I was using the same result to answer part 1 and 2. I've since discovered that part 1 is far simpler and can be found without solving the entire problem space, but whatever.
2021-12-17 10:57:33 -06:00

127 lines
3.6 KiB
C#

namespace aoc2021;
internal class Day09 : Day
{
internal override void Go()
{
var lines = Util.ReadAllLines("inputs/09.txt").ToArray();
byte[,] grid = new byte[lines.Length, lines[0].Length];
for (int i = 0; i < lines.Length; i++)
{
for (int j = 0; j < lines[i].Length; j++)
{
grid[i, j] = (byte)char.GetNumericValue(lines[i][j]);
}
}
Part1(grid);
Part2(grid);
}
private static void Part1(byte[,] grid)
{
using var t = new Timer();
var lowPoints = GetLowPoints(grid);
var totalRisk = lowPoints.Sum(x => grid[x.Item1, x.Item2] + 1);
t.Stop();
Logger.Log($"<+black>> part1: <+white>{totalRisk}<r>");
}
private static List<(int, int)> GetLowPoints(byte[,] grid)
{
List<(int, int)> lowPoints = new();
for (int i = 0; i < grid.GetLength(0); i++)
{
for (int j = 0; j < grid.GetLength(1); j++)
{
byte val = grid[i, j];
if (i > 0 && grid[i - 1, j] <= val)
{
continue;
}
if (i < grid.GetLength(0) - 1 && grid[i + 1, j] <= val)
{
continue;
}
if (j > 0 && grid[i, j - 1] <= val)
{
continue;
}
if (j < grid.GetLength(1) - 1 && grid[i, j + 1] <= val)
{
continue;
}
lowPoints.Add((i, j));
}
}
return lowPoints;
}
private static void Part2(byte[,] grid)
{
using var t = new Timer();
var lowPoints = GetLowPoints(grid);
List<int> basins = new();
foreach (var point in lowPoints)
{
var basinPoints = GetBasinSize(grid, point.Item1, point.Item2);
basins.Add(basinPoints.Distinct().Count() + 1);
}
var top3Mult = basins.OrderByDescending(x => x).Take(3).Aggregate(1, (x, y) => x * y);
t.Stop();
Logger.Log($"<+black>> part2: <+white>{top3Mult}<r>");
}
private static List<(int, int)> GetBasinSize(byte[,] grid, int i, int j)
{
List<(int, int)> basinPoints = new();
if (i >= grid.GetLength(0) || j >= grid.GetLength(1) || i < 0 || j < 0)
{
return new();
}
if (!basinPoints.Contains((i - 1, j)) && IsBasinPoint(grid, grid[i, j], i - 1, j))
{
basinPoints.Add((i - 1, j));
basinPoints.AddRange(GetBasinSize(grid, i - 1, j));
}
if (!basinPoints.Contains((i + 1, j)) && IsBasinPoint(grid, grid[i, j], i + 1, j))
{
basinPoints.Add((i + 1, j));
basinPoints.AddRange(GetBasinSize(grid, i + 1, j));
}
if (!basinPoints.Contains((i, j - 1)) && IsBasinPoint(grid, grid[i, j], i, j - 1))
{
basinPoints.Add((i, j - 1));
basinPoints.AddRange(GetBasinSize(grid, i, j - 1));
}
if (!basinPoints.Contains((i, j + 1)) && IsBasinPoint(grid, grid[i, j], i, j + 1))
{
basinPoints.Add((i, j + 1));
basinPoints.AddRange(GetBasinSize(grid, i, j + 1));
}
return basinPoints;
}
private static bool IsBasinPoint(byte[,] grid, byte val, int i, int j)
{
if (i >= grid.GetLength(0) || j >= grid.GetLength(1) || i < 0 || j < 0)
{
return false;
}
if (grid[i, j] == 9 || val == 9)
{
return false;
}
return grid[i, j] > val;
}
}