176 lines
6.5 KiB
C#
176 lines
6.5 KiB
C#
using System.Text;
|
|
|
|
namespace AdventOfCode.Solutions._2023
|
|
{
|
|
public class Day14 : IChallange
|
|
{
|
|
public int Year => 2023;
|
|
public int Day => 14;
|
|
|
|
private readonly InputReader _inputReader;
|
|
|
|
public Day14(InputReader inputReader)
|
|
{
|
|
_inputReader = inputReader;
|
|
_inputReader.SetInput(this);
|
|
}
|
|
|
|
public async Task<string> GetSolutionPart1()
|
|
{
|
|
Grid<Point> grid = await _inputReader.ReadToGrid<Point>();
|
|
HashSet<Point> bolders = grid.FindWithValue('O').Select(p => new Point(p.X, p.Y)).ToHashSet();
|
|
HashSet<Point> cubes = grid.FindWithValue('#').Select(p => new Point(p.X, p.Y)).ToHashSet();
|
|
|
|
bolders = MoveUp(bolders, cubes);
|
|
|
|
return CalculateLoad(CreateStringMap(bolders, cubes, grid.Rows, grid.Columns).Split(':'), grid.Rows);
|
|
}
|
|
|
|
public async Task<string> GetSolutionPart2()
|
|
{
|
|
int maxCycles = 1_000_000_000;
|
|
Dictionary<string, int> mapStates = [];
|
|
|
|
Grid<Point> grid = await _inputReader.ReadToGrid<Point>();
|
|
HashSet<Point> bolders = grid.FindWithValue('O').Select(p => new Point(p.X, p.Y)).ToHashSet();
|
|
HashSet<Point> cubes = grid.FindWithValue('#').Select(p => new Point(p.X, p.Y)).ToHashSet();
|
|
|
|
for (int currentCycle = 1; currentCycle <= maxCycles; currentCycle++)
|
|
{
|
|
// move the boulders
|
|
bolders = MoveUp(bolders, cubes);
|
|
bolders = MoveLeft(bolders, cubes);
|
|
bolders = MoveDown(bolders, cubes, grid.Rows);
|
|
bolders = MoveRight(bolders, cubes, grid.Columns);
|
|
|
|
// see if the current layout of the platform already exists
|
|
if (mapStates.TryGetValue(CreateStringMap(bolders, cubes, grid.Rows, grid.Columns), out int prevCycle))
|
|
{
|
|
long cyclesWithoutStartCycles = maxCycles - prevCycle;
|
|
long cycleLength = currentCycle - prevCycle;
|
|
|
|
long remainingCycles = cyclesWithoutStartCycles % cycleLength;
|
|
long exitMap = remainingCycles + prevCycle;
|
|
string[] lines = mapStates.First(kvp => kvp.Value == exitMap).Key.Split(':');
|
|
return CalculateLoad(lines, grid.Rows);
|
|
}
|
|
|
|
// ensure the state is saved
|
|
mapStates.TryAdd(CreateStringMap(bolders, cubes, grid.Rows, grid.Columns), currentCycle);
|
|
}
|
|
|
|
return bolders.Sum(bolder => grid.Rows - bolder.Y).ToString();
|
|
}
|
|
|
|
private static string CalculateLoad(string[] lines, long totalRows)
|
|
{
|
|
long totalWeight = 0;
|
|
for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++)
|
|
{
|
|
int bolders = lines[lineIndex].Where(c => c == 'O').Count();
|
|
totalWeight += bolders * (totalRows - lineIndex);
|
|
}
|
|
|
|
return totalWeight.ToString();
|
|
}
|
|
|
|
private static HashSet<Point> MoveUp(HashSet<Point> bolders, HashSet<Point> cubes)
|
|
{
|
|
HashSet<Point> boldersMoved = [];
|
|
foreach (Point bolder in bolders.OrderBy(bolder => bolder.Y) )
|
|
{
|
|
// current location
|
|
long ySearch = bolder.Y;
|
|
while (ySearch - 1 >= 0 // ensure in map
|
|
&& !boldersMoved.Contains(new(bolder.X, ySearch - 1)) // check that the next tile does not have a bolder
|
|
&& !cubes.Contains(new(bolder.X, ySearch - 1))) // check that the next tile has not cube
|
|
{
|
|
ySearch--; // move to next
|
|
}
|
|
|
|
// something found on next location so place bolder here
|
|
boldersMoved.Add(new(bolder.X, ySearch));
|
|
}
|
|
|
|
return boldersMoved;
|
|
}
|
|
|
|
private static HashSet<Point> MoveDown(HashSet<Point> bolders, HashSet<Point> cubes, long ySize)
|
|
{
|
|
HashSet<Point> boldersMoved = [];
|
|
foreach (Point bolder in bolders.OrderByDescending(bolder => bolder.Y))
|
|
{
|
|
long ySearch = bolder.Y;
|
|
while (ySearch + 1 < ySize
|
|
&& !boldersMoved.Contains(new(bolder.X, ySearch + 1))
|
|
&& !cubes.Contains(new(bolder.X, ySearch + 1)))
|
|
{
|
|
ySearch++;
|
|
}
|
|
|
|
boldersMoved.Add(new(bolder.X, ySearch));
|
|
}
|
|
|
|
return boldersMoved;
|
|
}
|
|
|
|
private static HashSet<Point> MoveLeft(HashSet<Point> bolders, HashSet<Point> cubes)
|
|
{
|
|
HashSet<Point> boldersMoved = [];
|
|
foreach (Point bolder in bolders.OrderBy(bolder => bolder.X))
|
|
{
|
|
long xSearch = bolder.X;
|
|
while (xSearch - 1 >= 0
|
|
&& !boldersMoved.Contains(new(xSearch - 1, bolder.Y))
|
|
&& !cubes.Contains(new(xSearch - 1, bolder.Y)))
|
|
{
|
|
xSearch--;
|
|
}
|
|
|
|
boldersMoved.Add(new(xSearch, bolder.Y));
|
|
}
|
|
|
|
return boldersMoved;
|
|
}
|
|
|
|
private static HashSet<Point> MoveRight(HashSet<Point> bolders, HashSet<Point> cubes, long xSize)
|
|
{
|
|
HashSet<Point> boldersMoved = [];
|
|
foreach (Point bolder in bolders.OrderByDescending(bolder => bolder.X))
|
|
{
|
|
long xSearch = bolder.X;
|
|
while (xSearch + 1 < xSize
|
|
&& !boldersMoved.Contains(new(xSearch + 1, bolder.Y))
|
|
&& !cubes.Contains(new(xSearch + 1, bolder.Y)))
|
|
{
|
|
xSearch++;
|
|
}
|
|
|
|
boldersMoved.Add(new(xSearch, bolder.Y));
|
|
}
|
|
|
|
return boldersMoved;
|
|
}
|
|
|
|
private static string CreateStringMap(HashSet<Point> bolders, HashSet<Point> cubess, long maxX, long maxY)
|
|
{
|
|
var stringBuilder = new StringBuilder();
|
|
for (var y = 0; y < maxY; y++)
|
|
{
|
|
for (var x = 0; x < maxX; x++)
|
|
{
|
|
if (bolders.Contains(new Point(x, y)))
|
|
stringBuilder.Append('O');
|
|
else if (cubess.Contains(new Point(x, y)))
|
|
stringBuilder.Append('#');
|
|
else
|
|
stringBuilder.Append('.');
|
|
}
|
|
|
|
stringBuilder.Append(':');
|
|
}
|
|
|
|
return stringBuilder.ToString();
|
|
}
|
|
}
|
|
} |