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() { int 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(); int totalBolders = bolders.Count; for (int currentCycle = 0; currentCycle < maxCycles; currentCycle++) { // ensure the state is saved mapStates.TryAdd(CreateStringMap(bolders, cubes, grid.Rows, grid.Columns), currentCycle); // add a test to see when we hit the right cycle for the test data // move the boulders bolders = MoveUp(bolders, cubes); if (bolders.Count != totalBolders) { Console.WriteLine($"lost boulders MoveUp at {currentCycle}, from {totalBolders} to {bolders.Count}"); return string.Empty; } bolders = MoveLeft(bolders, cubes); if (bolders.Count != totalBolders) { Console.WriteLine($"lost boulders MoveLeft at {currentCycle}, from {totalBolders} to {bolders.Count}"); return string.Empty; } bolders = MoveDown(bolders, cubes, grid.Rows); if (bolders.Count != totalBolders) { Console.WriteLine($"lost boulders MoveDown at {currentCycle}, from {totalBolders} to {bolders.Count}"); return string.Empty; } bolders = MoveRight(bolders, cubes, grid.Columns); if (bolders.Count != totalBolders) { Console.WriteLine($"lost boulders MoveRight at {currentCycle}, from {totalBolders} to {bolders.Count}"); return string.Empty; } // see if we found the loop, if so we continue untill the end if (foundLoop) continue; // see if the current layout of the platform already exists // if so we are repeating our selfs so wel can calculate the remaining steps to get to the end. // we might even yoink the map from the dic and use that if (mapStates.TryGetValue(CreateStringMap(bolders, cubes, grid.Rows, grid.Columns), out int prevCycle)) { // calculate remaining cycles to the first exit point int remainingCycles = maxCycles % (currentCycle + 1); // set the total the first exit point int exitMap = remainingCycles + currentCycle; string[] lines = mapStates.First(kvp => kvp.Value == prevCycle - 1).Key.Split(':', StringSplitOptions.RemoveEmptyEntries); int totalWeight = 0; for (int lineIndex = 0; lineIndex < lines.Length; lineIndex++) { int weight = lines[lineIndex].Length, lineWeight = 0; for (int charIndex = 0; charIndex < lines[lineIndex].Length; charIndex++) { if (lines[lineIndex][charIndex] is '.') continue; if (lines[lineIndex][charIndex] is '#') { weight = lines[lineIndex].Length - charIndex - 1; continue; } if (lines[lineIndex][charIndex] is 'O') { lineWeight += weight; weight--; } } totalWeight += lineWeight; } return totalWeight.ToString(); } } 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.OrderByDescending(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 - 1, bolder.Y)) // check that the next tile does not have a bolder && !cubes.Contains(new(xSearch - 1, 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.OrderByDescending(bolder => bolder.X)) { // current location long xSearch = bolder.X; while (xSearch + 1 < xSize // ensure in map && !boldersMoved.Contains(new(xSearch + 1, bolder.Y)) // check that the next tile does not have a bolder && !cubes.Contains(new(xSearch + 1, 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.Append(':'); } return stringBuilder.ToString(); } } }