using System.Collections.Generic; using System.IO; using System.Linq; using AdvenOfCode.Contracts; using Coordinate = (int x, int y); namespace AoC_2025; public class Day04 : IPuzzleSolver { private string[] ParsePuzzleInput(string path) { var puzzleInput = File.ReadAllLines(path) .Where(line => !string.IsNullOrWhiteSpace(line)) .Select(line => line.Trim()) .ToArray(); return puzzleInput; } public long SolvePart1(string pathToPuzzleInput) { var paperrollGrid = ParsePuzzleInput(pathToPuzzleInput); var accessiblePaperrolls = GetAccessiblePaperrolls(paperrollGrid); var countAccessibleRolls = accessiblePaperrolls.Count(); return countAccessibleRolls; } public long SolvePart2(string pathToPuzzleInput) { var paperrollGrid = ParsePuzzleInput(pathToPuzzleInput); var rollCoordinates = GetRollCoordinates(paperrollGrid); var countAccessibleRolls = RecursiveCountAllRemovableRolls(rollCoordinates); return countAccessibleRolls; } private IEnumerable GetAccessiblePaperrolls(IEnumerable paperrollGrid) { var rollCoordinates = GetRollCoordinates(paperrollGrid); var rollsWithNeighbours = GetNeighbours(rollCoordinates); var accessiblePaperrolls = FilterForLessThan(rollsWithNeighbours, 4); return accessiblePaperrolls; } private int RecursiveCountAllRemovableRolls(HashSet coordinates) { var rollsWithNeighbours = GetNeighbours(coordinates); var accessiblePaperrolls = FilterForLessThan(rollsWithNeighbours, 4).ToArray(); var countAccessibleRolls = accessiblePaperrolls.Length; coordinates.ExceptWith(accessiblePaperrolls); return countAccessibleRolls == 0 ? 0 : countAccessibleRolls + RecursiveCountAllRemovableRolls(coordinates); } private HashSet GetRollCoordinates(IEnumerable paperrollGrid) { var coordinates = paperrollGrid .SelectMany((gridLine, xIndex) => GetCoordinates(gridLine, xIndex)) .ToHashSet(); return coordinates; } private IEnumerable GetCoordinates(string gridLine, int xIndex) { var indexedCharacters = gridLine.Select((character, yIndex) => (character, yIndex)); var validRolls = indexedCharacters.Where(tuple => tuple.character == '@'); var rollCoordinates = validRolls.Select(tuple => (xIndex, tuple.yIndex)); return rollCoordinates; } private IEnumerable<(Coordinate coordinate, Coordinate[] neighbours)> GetNeighbours(HashSet rollCoordinates) { var coordsWithNeighbourCount = rollCoordinates .Select(coord => (coord, GetNeighbours(coord, rollCoordinates))); return coordsWithNeighbourCount; } private Coordinate[] GetNeighbours(Coordinate coordinate, HashSet coordinates) { Coordinate[] neighbourDirections = [ (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1), ]; var possibleNeighbours = neighbourDirections .Select(direction => (coordinate.x + direction.x, coordinate.y + direction.y)); var actualNeighbours = possibleNeighbours .Where(coord => coordinates.Contains(coord)) .ToArray(); return actualNeighbours; } private IEnumerable FilterForLessThan(IEnumerable<(Coordinate coordinate, Coordinate[] neighbours)> coordinatesWithNeighbours, int lessThan) { var coordinatesWithLess = coordinatesWithNeighbours .Where(entry => entry.neighbours.Count() < lessThan) .Select(entry => entry.coordinate); return coordinatesWithLess; } }