using System.Buffers; using System.Numerics; using Spectre.Console; namespace AdventOfCode.Days; public class Day7 : Day { public override int Number => 7; public override string Name => "Laboratories"; public const int Width = 141; public const int Height = 142; public override void RunPart1(bool display = true) { var splitCount = 0; var splittersMap = ParseSplittersMap(Input, out var startPosition); var beams = new HashSet(); var nextLineBeams = new List(); var usedSplitters = new HashSet(); beams.Add(startPosition with { I = startPosition.I + 1 }); // Simulate beams going down var row = 1; while (row < Height - 1) { foreach (var beamPosition in beams) { var targetPosition = beamPosition with { I = beamPosition.I + 1 }; // There is nothing below if (!splittersMap[targetPosition.I, targetPosition.J]) { nextLineBeams.Add(targetPosition); } // There is a splitter, only counts it if not already used else if (usedSplitters.Add(targetPosition)) { splitCount++; // Left and right beam if (targetPosition.J - 1 >= 0) { nextLineBeams.Add(targetPosition with { J = targetPosition.J - 1 }); } if (targetPosition.J + 1 < Width) { nextLineBeams.Add(targetPosition with { J = targetPosition.J + 1 }); } } } // Always delete current line beams since we iterate from top to bottom beams.Clear(); foreach (var nextLineBeam in nextLineBeams) { beams.Add(nextLineBeam); } nextLineBeams.Clear(); // Also clear the used splitters list for next line usedSplitters.Clear(); row++; } if (display) { AnsiConsole.MarkupLine($"[green]Beam split count: [yellow]{splitCount}[/][/]"); } } public override void RunPart2(bool display = true) { var splittersMap = ParseSplittersMap(Input, out var startPosition); var quantumPositions = new Dictionary(); var nextQuantumPositions = new Dictionary(); quantumPositions.Add(startPosition with { I = startPosition.I + 1 }, 1L); // Simulate beams going down for each timeline var row = 1; while (row < Height - 1) { // Simulation step foreach (var (beamPosition, timelinesCount) in quantumPositions) { var targetPosition = beamPosition with { I = beamPosition.I + 1 }; // There is nothing below, keep going down if (!splittersMap[targetPosition.I, targetPosition.J]) { UpdateTimelines(nextQuantumPositions, targetPosition, timelinesCount); } else { // Left and right beams var leftBeamPosition = targetPosition with { J = targetPosition.J - 1 }; var rightBeamPosition = targetPosition with { J = targetPosition.J + 1 }; UpdateTimelines(nextQuantumPositions, leftBeamPosition, timelinesCount); UpdateTimelines(nextQuantumPositions, rightBeamPosition, timelinesCount); } } // Always delete current line beams since we iterate from top to bottom quantumPositions.Clear(); foreach (var nextQuantumPosition in nextQuantumPositions) { quantumPositions.Add(nextQuantumPosition.Key, nextQuantumPosition.Value); } nextQuantumPositions.Clear(); row++; } if (display) { AnsiConsole.MarkupLine($"[green]Timelines count: [yellow]{quantumPositions.Sum(p => p.Value)}[/][/]"); } return; // Update and merge timelines static void UpdateTimelines(Dictionary nextQuantumPositions, Position targetPosition, long timelinesCount) { // Merge timelines if there is already one for this target position if (nextQuantumPositions.TryGetValue(targetPosition, out var otherTimelinesCount)) { nextQuantumPositions[targetPosition] = otherTimelinesCount + timelinesCount; } // No timelines to merge, no new timeline else { nextQuantumPositions[targetPosition] = timelinesCount; } } } private static bool[,] ParseSplittersMap(string input, out Position startPosition) { var map = new bool[Height, Width]; startPosition = new Position(0, 0); var i = 0; foreach (var line in input.EnumerateLines()) { var j = 0; foreach (var symbol in line) { // Find start if (symbol is 'S') { startPosition = new Position(i, j); continue; } map[i, j] = symbol is '^'; j++; } i++; } return map; } private record Position(int I, int J); }