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 GetSolutionPart1() { Grid grid = await _inputReader.ReadToGrid(); HashSet bolders = grid.FindWithValue('O').Select(p => new Point(p.X, p.Y)).ToHashSet(); HashSet 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 GetSolutionPart2() { long maxCycles = 1_000_000_000; Dictionary mapStates = []; bool foundLoop = false; Grid grid = await _inputReader.ReadToGrid(); HashSet bolders = grid.FindWithValue('O').Select(p => new Point(p.X, p.Y)).ToHashSet(); HashSet 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 MoveUp(HashSet bolders, HashSet cubes) { HashSet 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 MoveDown(HashSet bolders, HashSet cubes, long ySize) { HashSet 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 MoveLeft(HashSet bolders, HashSet cubes) { HashSet 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 MoveRight(HashSet bolders, HashSet cubes, long xSize) { HashSet 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 bolders, HashSet 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(); } } }