AdventOfCode/AdventOfCode.Solutions/2023/Day 14/Day14.cs

166 lines
6.5 KiB
C#

using AdventOfCode.Core;
using AdventOfCode.Core.Shared.Grid;
using System.Collections.Generic;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading.Tasks.Dataflow;
namespace AdventOfCode.Solutions._2023
{
public class Day14(InputReader reader) : IChallange
{
private InputReader _inputReader = reader;
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 bolders.Sum(bolder => grid.Rows - bolder.Y).ToString();
}
public async Task<string> GetSolutionPart2()
{
long maxCycles = 1_000_000_000;
Dictionary<string, int> mapStates = [];
bool foundLoop = false;
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 = 0; currentCycle < maxCycles; currentCycle++)
{
mapStates.TryAdd(CreateStringMap(bolders, cubes, grid.Rows, grid.Columns), currentCycle);
bolders = MoveUp(bolders, cubes);
bolders = MoveLeft(bolders, cubes);
bolders = MoveDown(bolders, cubes, grid.Rows);
bolders = MoveRight(bolders, cubes, grid.Columns);
if (foundLoop) continue;
int hash = bolders.GetHashCode();
if (mapStates.TryGetValue(CreateStringMap(bolders, cubes, grid.Rows, grid.Columns), out int prevCycle))
{
// calculate remaining cycles to the first exit point
long remainingCycles = (maxCycles - prevCycle) % (currentCycle + 1 - prevCycle) + 1;
// set the total the first exit point
maxCycles = remainingCycles + currentCycle;
foundLoop = true;
}
}
return bolders.Sum(bolder => grid.Rows - bolder.Y).ToString();
}
private 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 HashSet<Point> MoveDown(HashSet<Point> bolders, HashSet<Point> cubes, long ySize)
{
HashSet<Point> boldersMoved = [];
foreach (Point bolder in bolders.OrderBy(bolder => bolder.Y))
{
// current location
long ySearch = bolder.Y;
while (ySearch + 1 < ySize // 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 HashSet<Point> MoveLeft(HashSet<Point> bolders, HashSet<Point> cubes)
{
HashSet<Point> boldersMoved = [];
foreach (Point bolder in bolders.OrderBy(bolder => bolder.X))
{
// current location
long xSearch = bolder.X;
while (xSearch - 1 >= 0 // ensure in map
&& !boldersMoved.Contains(new(xSearch, bolder.Y)) // check that the next tile does not have a bolder
&& !cubes.Contains(new(xSearch, bolder.Y))) // check that the next tile has not cube
{
xSearch--; // move to next
}
// something found on next location so place bolder here
boldersMoved.Add(new(xSearch, bolder.Y));
}
return boldersMoved;
}
private HashSet<Point> MoveRight(HashSet<Point> bolders, HashSet<Point> cubes, long xSize)
{
HashSet<Point> boldersMoved = [];
foreach (Point bolder in bolders.OrderBy(bolder => bolder.Y))
{
// current location
long xSearch = bolder.Y;
while (xSearch + 1 < xSize // ensure in map
&& !boldersMoved.Contains(new(xSearch, bolder.Y)) // check that the next tile does not have a bolder
&& !cubes.Contains(new(xSearch, bolder.Y))) // check that the next tile has not cube
{
xSearch++; // move to next
}
// something found on next location so place bolder here
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.AppendLine();
}
return stringBuilder.ToString();
}
}
}