Advent of Code: Day 3 - Gear Ratios

Grant Riordan - Dec 4 '23 - - Dev Community

Day 3 - Gear Ratios

AoC Problem Page

Todays challenge was more time consuming than neccessarily difficult.

The basic concept was to build a grid of letters, you could do this in a multitude of ways:

  • My solutions - using a combo of Dictionary, with a record struct of x, y coordinates as the key, and the character at that co-ordinate as the dictionary value.

  • could use a 2D array

  • List of custom object.

I chose to use the Dictionary as it seemed the most easy to read, you give it an x, y coordinate, and then the character found at that co-ordinate.

The downfall of my approach is that you're looping over the values twice, (less performant) but it got to the point where i took the hit on performance for readability and my own sanity.

I gather all the numbers and then do the same to get all the symbols. I can then loop over all the numbers, and check whether they are adjacent to any special symbols.

There is a lot of looping in my solution, which yes could probably be reduced if i'd spent more time (i may go back and refactor the code to make it more performant), but the majority of the time when doing AoC I tend to get it to a sweet spot where i can solve the challenge, and then if i have time in the day i go back and refactor, or I return in a few months and re-asses and refactor with the clean brain.

Here's the solution, feel free to adapt and make it better for yourself.

using System.Text;



string filePath = "./test.txt"; // Replace this with the path to your file
string[] inputLines = File.ReadAllLines(filePath).Select(x => x.Trim()).ToArray();

Dictionary<Coordinates, int> numbers = BuildNumbers();
Dictionary<Coordinates, char> specialCharacters = BuildSpecialCharacters();

Part1();

Dictionary<Coordinates, int> BuildNumbers()
{
  Dictionary<Coordinates, int> numbers = new();
  for (int row = 0; row < inputLines.Length; row++)
  {
    var line = inputLines[row];
    for (int col = 0; col < line.Length; col++)
    {
      // find the numbers and store against their starting coordinates
      if (char.IsDigit(line[col]) && line[col] is not '.')
      {
        Coordinates coordinates = new(row, col);
        StringBuilder b = new();
        while (col < line.Length && char.IsDigit(line[col]))
        {
          var value = line[col];
          b.Append(value);
          col++;
        }

        numbers[coordinates] = int.Parse(b.ToString());
      }
    }
  }
  return numbers;
}

Dictionary<Coordinates, char> BuildSpecialCharacters()
{
  Dictionary<Coordinates, char> spCharacters = new();
  for (int row = 0; row < inputLines.Length; row++)
  {
    var line = inputLines[row];
    for (int col = 0; col < line.Length; col++)
    {
      char ch = line[col];
      if (char.IsDigit(ch) || ch == '.')
      {
        continue;
      }

      Coordinates coordinates = new(row, col);
      spCharacters[coordinates] = ch;
    }
  }
  return spCharacters;
}

IEnumerable<Coordinates> GetNumberCoordinates(Coordinates coordinate)
{
  if (!numbers.ContainsKey(coordinate))
  { return new Coordinates[0]; }

  int numberOfDigits = numbers[coordinate].ToString().Length;
  return Enumerable.Range(coordinate.Col, numberOfDigits).Select(col => new Coordinates(coordinate.Row, col));
}

bool NeigbourIsSpecial(Coordinates coordinate)
{
  Coordinates[] directions = {
    new Coordinates(coordinate.Row, coordinate.Col - 1), //left
    new Coordinates(coordinate.Row, coordinate.Col +1), // right
    new Coordinates(coordinate.Row - 1, coordinate.Col), // up
    new Coordinates(coordinate.Row + 1, coordinate.Col), // down

    new Coordinates(coordinate.Row -1,coordinate.Col - 1), // diagonal-up-left
    new Coordinates(coordinate.Row -1, coordinate.Col +1), // diagonal-up-right
    new Coordinates(coordinate.Row +1,coordinate.Col -1), // diagonal-down-left
    new Coordinates(coordinate.Row +1,coordinate.Col + 1), // diagonal-right-down
  };

  foreach (Coordinates c in directions)
  {
    if (specialCharacters.ContainsKey(c)) { return true; }
  }
  return false;
}

void Part1()
{
  int accumulativeValue = 0;
  foreach ((Coordinates c, int number) in numbers)
  {
    IEnumerable<Coordinates> numberCoordinates = GetNumberCoordinates(c);
    if (numberCoordinates.Any(NeigbourIsSpecial))
    {
      accumulativeValue += number;
    }
  }
  Console.WriteLine("Accumulative value: " + accumulativeValue);
}

record struct Coordinates(int Row, int Col);
Enter fullscreen mode Exit fullscreen mode
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .