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 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 CalculateLoad(CreateStringMap(bolders, cubes, grid.Rows, grid.Columns).Split(':'), grid.Rows); } public async Task GetSolutionPart2() { int maxCycles = 1_000_000_000; Dictionary mapStates = []; 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 = 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 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 static HashSet MoveDown(HashSet bolders, HashSet cubes, long ySize) { HashSet 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 MoveLeft(HashSet bolders, HashSet cubes) { HashSet 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 MoveRight(HashSet bolders, HashSet cubes, long xSize) { HashSet 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 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(); } } }