var lines = File.ReadAllLines("input.txt"); var charArray = new char[lines.Length, lines[0].Length]; for (var i = 0; i < charArray.GetLength(0); i++) for (var j = 0; j < charArray.GetLength(1); j++) { charArray[i, j] = lines[i][j]; } var uniqueCharacterPositions = GroupUniqueCharacterPositions(charArray); var antinodeLocations = new List<(int, int)>(); var antinodeHarmonicsLocations = new List<(int, int)>(); foreach (var uniqueCharacter in uniqueCharacterPositions) { for (var i = 0; i < uniqueCharacter.Value.Count - 1; i++) { var currentPosition = uniqueCharacter.Value[i]; for (var j = i + 1; j < uniqueCharacter.Value.Count; j++) { var nextPosition = uniqueCharacter.Value[j]; if (Distance(currentPosition.Item1, currentPosition.Item2, nextPosition.Item1, nextPosition.Item2) >= 1) { antinodeLocations.AddRange(CalculateAntinodes(currentPosition, nextPosition, charArray)); } antinodeHarmonicsLocations.AddRange(CalculateAntinodesWithHarmonics(currentPosition, nextPosition, charArray)); } } } Console.WriteLine( $"Part 1: {antinodeLocations.Distinct().Count()}\nPart 2: {antinodeHarmonicsLocations.Distinct().Count()}"); List<(int, int)> CalculateAntinodesWithHarmonics((int, int) currentPosition, (int, int) nextPosition, char[,] input) { var locations = new List<(int, int)> { currentPosition, nextPosition }; var verticalDistance = Math.Abs(currentPosition.Item1 - nextPosition.Item1); var horizontalDistance = Math.Abs(currentPosition.Item2 - nextPosition.Item2); var currentPositionClone = currentPosition; var nextPositionClone = nextPosition; int antinodePositionRow, antinodePositionColumn; while (true) { if (currentPositionClone.Item1 < nextPositionClone.Item1) { antinodePositionRow = currentPositionClone.Item1 - verticalDistance; } else { antinodePositionRow = currentPositionClone.Item1 + verticalDistance; } if (currentPositionClone.Item2 < nextPositionClone.Item2) { antinodePositionColumn = currentPositionClone.Item2 - horizontalDistance; } else { antinodePositionColumn = currentPositionClone.Item2 + horizontalDistance; } if (IsValid(antinodePositionRow, antinodePositionColumn, input)) { locations.Add((antinodePositionRow, antinodePositionColumn)); nextPositionClone = currentPositionClone; currentPositionClone = (antinodePositionRow, antinodePositionColumn); } else { break; } } currentPositionClone = currentPosition; nextPositionClone = nextPosition; while (true) { if (currentPositionClone.Item1 < nextPositionClone.Item1) { antinodePositionRow = nextPositionClone.Item1 + verticalDistance; } else { antinodePositionRow = nextPositionClone.Item1 - verticalDistance; } if (currentPositionClone.Item2 < nextPositionClone.Item2) { antinodePositionColumn = nextPositionClone.Item2 + horizontalDistance; } else { antinodePositionColumn = nextPositionClone.Item2 - horizontalDistance; } if (IsValid(antinodePositionRow, antinodePositionColumn, input)) { locations.Add((antinodePositionRow, antinodePositionColumn)); currentPositionClone = nextPositionClone; nextPositionClone = (antinodePositionRow, antinodePositionColumn); } else { break; } } return locations; } List<(int, int)> CalculateAntinodes((int, int) currentPosition, (int, int) nextPosition, char[,] input) { var locations = new List<(int, int)>(); int firstAntinodePositionRow, firstAntinodePositionColumn, secondAntinodePositionRow, secondAntinodePositionColumn; var verticalDistance = Math.Abs(currentPosition.Item1 - nextPosition.Item1); var horizontalDistance = Math.Abs(currentPosition.Item2 - nextPosition.Item2); if (currentPosition.Item1 < nextPosition.Item1) { firstAntinodePositionRow = currentPosition.Item1 - verticalDistance; secondAntinodePositionRow = nextPosition.Item1 + verticalDistance; } else { firstAntinodePositionRow = currentPosition.Item1 + verticalDistance; secondAntinodePositionRow = nextPosition.Item1 - verticalDistance; } if (currentPosition.Item2 < nextPosition.Item2) { firstAntinodePositionColumn = currentPosition.Item2 - horizontalDistance; secondAntinodePositionColumn = nextPosition.Item2 + horizontalDistance; } else { firstAntinodePositionColumn = currentPosition.Item2 + horizontalDistance; secondAntinodePositionColumn = nextPosition.Item2 - horizontalDistance; } if (IsValid(firstAntinodePositionRow, firstAntinodePositionColumn, input)) { locations.Add((firstAntinodePositionRow, firstAntinodePositionColumn)); } if (IsValid(secondAntinodePositionRow, secondAntinodePositionColumn, input)) { locations.Add((secondAntinodePositionRow, secondAntinodePositionColumn)); } return locations; } Dictionary> GroupUniqueCharacterPositions(char[,] array) { var charLocations = new Dictionary>(); for (var i = 0; i < array.GetLength(0); i++) { for (var j = 0; j < array.GetLength(1); j++) { var currentChar = array[i, j]; if (currentChar == '.') continue; if (!charLocations.ContainsKey(currentChar)) { charLocations[currentChar] = new List<(int, int)>(); } charLocations[currentChar].Add((i, j)); } } return charLocations; } bool IsValid(int row, int column, char[,] grid) => row >= 0 && row < grid.GetLength(0) && column >= 0 && column < grid.GetLength(1); double Distance(int position1Row, int position1Column, int position2Row, int position2Column) => Math.Sqrt(Math.Pow(position1Column - position2Column, 2) + Math.Pow(position2Row - position1Row, 2));