From b596be7353a15cbf3cedbc861c2d786e1b001210 Mon Sep 17 00:00:00 2001
From: Josh Creek <8179928+jcreek@users.noreply.github.com>
Date: Sat, 7 Dec 2024 22:19:25 +0000
Subject: [PATCH] feat(*): Add array searching helper methods
---
.../AOC.Tests/Helpers/ArraySearching.cs | 200 ++++++++++++++++++
1 file changed, 200 insertions(+)
create mode 100644 AdventOfCode C#/AOC.Tests/Helpers/ArraySearching.cs
diff --git a/AdventOfCode C#/AOC.Tests/Helpers/ArraySearching.cs b/AdventOfCode C#/AOC.Tests/Helpers/ArraySearching.cs
new file mode 100644
index 0000000..41cc3fc
--- /dev/null
+++ b/AdventOfCode C#/AOC.Tests/Helpers/ArraySearching.cs
@@ -0,0 +1,200 @@
+namespace AOC.Tests.Helpers;
+
+///
+/// Provides utility methods for searching within 2D character arrays.
+///
+public static class ArraySearching
+{
+ // Directions: Right, Left, Down, Up, Diagonal Down-Right, Diagonal Down-Left, Diagonal Up-Right, Diagonal Up-Left
+ private static readonly (int RowOffset, int ColOffset)[] Directions = new[]
+ {
+ (0, 1), // Right
+ (0, -1), // Left
+ (1, 0), // Down
+ (-1, 0), // Up
+ (1, 1), // Diagonal down-right
+ (1, -1), // Diagonal down-left
+ (-1, 1), // Diagonal up-right
+ (-1, -1) // Diagonal up-left
+ };
+
+ public static char[,] ConvertListTo2DArray(List list)
+ {
+ int rows = list.Count;
+ int cols = list[0].Length;
+ char[,] grid = new char[rows, cols];
+ for (int r = 0; r < rows; r++)
+ {
+ string row = list[r];
+ for (int c = 0; c < cols; c++)
+ {
+ grid[r, c] = row[c];
+ }
+ }
+
+ return grid;
+ }
+
+ public static void Print2DCharArray(char[,] array)
+ {
+ for (int i = 0; i < array.GetLength(0); i++) // Iterate rows
+ {
+ for (int j = 0; j < array.GetLength(1); j++) // Iterate columns
+ {
+ Console.Write(array[i, j] + " ");
+ }
+
+ Console.WriteLine(); // New line after each row
+ }
+ }
+
+ ///
+ /// Finds all occurrences of the specified target string within the given 2D character array.
+ /// Occurrences are searched in all eight directions (horizontal, vertical, and diagonals).
+ /// Returns a collection of matches, where each match is represented as a collection of coordinates.
+ ///
+ /// The 2D character array to search.
+ /// The string to find.
+ ///
+ /// A list of matches, where each match is a list of (row, column) tuples indicating the positions of the found
+ /// string.
+ ///
+ /// Thrown if grid or target is null.
+ /// Thrown if target is empty.
+ public static List> FindStringOccurrences(char[,] grid, string target)
+ {
+ if (grid == null)
+ {
+ throw new ArgumentNullException(nameof(grid));
+ }
+
+ if (target == null)
+ {
+ throw new ArgumentNullException(nameof(target));
+ }
+
+ if (target.Length == 0)
+ {
+ throw new ArgumentException("Target string cannot be empty.", nameof(target));
+ }
+
+ List>? matches = new();
+ int rows = grid.GetLength(0);
+ int cols = grid.GetLength(1);
+ char firstChar = target[0];
+
+ for (int r = 0; r < rows; r++)
+ {
+ for (int c = 0; c < cols; c++)
+ {
+ // Optimization: Only start search if first char matches
+ if (grid[r, c] == firstChar)
+ {
+ foreach ((int rowOffset, int colOffset) in Directions)
+ {
+ if (CanFit(grid, r, c, rowOffset, colOffset, target.Length))
+ {
+ List<(int, int)>? coords = TryMatch(grid, r, c, rowOffset, colOffset, target);
+ if (coords != null)
+ {
+ matches.Add(coords);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return matches;
+ }
+
+ ///
+ /// Extracts all integer values from a 2D character array.
+ /// Consecutive digits are combined to form a number. Non-digit characters separate numbers.
+ /// Returns a list of all parsed integers.
+ ///
+ /// The 2D character array to search.
+ /// A list of integers found in the grid.
+ /// Thrown if grid is null.
+ public static List ExtractNumbers(char[,] grid)
+ {
+ if (grid == null)
+ {
+ throw new ArgumentNullException(nameof(grid));
+ }
+
+ List? numbers = new();
+ int rows = grid.GetLength(0);
+ int cols = grid.GetLength(1);
+
+ // We'll parse row-by-row for simplicity
+ for (int r = 0; r < rows; r++)
+ {
+ int currentNumber = 0;
+ bool buildingNumber = false;
+
+ for (int c = 0; c < cols; c++)
+ {
+ char ch = grid[r, c];
+ if (char.IsDigit(ch))
+ {
+ buildingNumber = true;
+ currentNumber = currentNumber * 10 + (ch - '0');
+ }
+ else
+ {
+ if (buildingNumber)
+ {
+ numbers.Add(currentNumber);
+ currentNumber = 0;
+ buildingNumber = false;
+ }
+ }
+ }
+
+ // If the row ended but we were still building a number
+ if (buildingNumber)
+ {
+ numbers.Add(currentNumber);
+ }
+ }
+
+ return numbers;
+ }
+
+ ///
+ /// Checks if the target string can fit starting at position (r, c) in the given direction.
+ ///
+ private static bool CanFit(char[,] grid, int r, int c, int rowOffset, int colOffset, int length)
+ {
+ int rows = grid.GetLength(0);
+ int cols = grid.GetLength(1);
+
+ int endRow = r + (length - 1) * rowOffset;
+ int endCol = c + (length - 1) * colOffset;
+
+ return endRow >= 0 && endRow < rows && endCol >= 0 && endCol < cols;
+ }
+
+ ///
+ /// Attempts to match the target string starting at (r, c) in a given direction.
+ /// If matched, returns the list of coordinates. Otherwise, returns null.
+ ///
+ private static List<(int, int)> TryMatch(char[,] grid, int r, int c, int rowOffset, int colOffset, string target)
+ {
+ List<(int, int)>? coords = new(target.Length);
+ for (int i = 0; i < target.Length; i++)
+ {
+ int rr = r + i * rowOffset;
+ int cc = c + i * colOffset;
+ if (grid[rr, cc] != target[i])
+ {
+ return null;
+ }
+
+ coords.Add((rr, cc));
+ }
+
+ return coords;
+ }
+}