Advent of Code 2023: Day 13 Point of Incidence
Tags: adventofcode, csharp
Published: 2023-12-21 21:31:10

# Day 13: Point of Incidence

[link to challenge](https://adventofcode.com/2023/day/13)
[link to github repo](https://github.com/gweaths/advent_code_2023)

## Problem

The problem was to find the mirror line within the grid of symbols. The mirrored line could be either vertical between two columns, or horizontal between two rows.

## Solution

```csharp
using System;
using System.IO;
using System.Linq;


static int FindMirror_Part1(string[] grid)
{
  for (int r = 1; r < grid.Length; r++)
  {
    string[] above = grid.Take(r).Reverse().ToArray();
    string[] below = grid.Skip(r).ToArray();

    above = above.Take(below.Length).ToArray();
    below = below.Take(above.Length).ToArray();

    if (above.SequenceEqual(below))
    {
      return r;
    }
  }
  return 0;
}

static int FindMirror_Part2(string[] grid)
{
  for (int currentRow = 1; currentRow < grid.Length; currentRow++)
  {
    // Split the grid into above and below the current row
    var aboveRows = grid.Take(currentRow).Reverse().ToArray();
    var belowRows = grid.Skip(currentRow).ToArray();

    // Check for symmetry by comparing corresponding elements in above and below
    int differencesCount = aboveRows
        .Zip(belowRows, CountDifferences)
        .Sum();

    if (differencesCount == 1)
    {
      return currentRow;
    }
  }

  return 0;
}

// Count the number of differences between two strings
static int CountDifferences(string str1, string str2)
{
  return str1.Zip(str2, (a, b) => a == b ? 0 : 1).Sum();
}

static void Part1()
{
  int total = 0;
  string[] lines = File.ReadAllText("./input.txt").Split("\n\n");
  foreach (string block in lines)
  {
    string[] grid = block.Split("\n");
    int row = FindMirror_Part1(grid);

    total += row * 100;


    /** create  rows from the columns so can be passed through FindMirror method
    - first select all the character and their indexes for each row
    - group by character index (i.e all index 0, 1, 2 to form columns)
    - create new strings (lines) of the characters */

    var columns = grid.SelectMany(s => s.Select((character, index) => new { character, index }))
              .GroupBy(sequence => sequence.index)
              .Select(group => new string(group.Select(x => x.character).ToArray()))
              .ToArray();

    int col = FindMirror_Part1(columns);

    total += col;
  }
  Console.WriteLine("Part1: " + total);
}


static void Part2()
{
  int total = 0;
  string[] blocks = File.ReadAllText("./input.txt").Split("\n\n");
  foreach (string block in blocks)
  {
    string[] grid = block.Split("\n");
    int row = FindMirror_Part2(grid);
    total += row * 100;

    var columns = grid.SelectMany(s => s.Select((char character, int index) => new { character, index }))
      .GroupBy(sequence => sequence.index)
      .Select(g => new string(g.Select(x => x.character).ToArray()))
      .ToArray();

    int col = FindMirror_Part2(columns);
    total += col;
  }
  Console.WriteLine("Part2: " + total);
}

Part1();
Part2();
```
The easiest solution here would be to loop through the lines, and then get the above and below items of the current row. If these values matched like for like, then you knew you have found the mirror line. 

We can then do the same for the columns, by tilting the grid so columns become rows and running the same method on them. 

## Breakdown

`FindMirror` Method:

This method takes an array of strings (grid) representing a grid of characters.

It iterates over the rows of the grid starting from the second row (r = 1) to the last row.

For each row, it separates the grid into two parts: 
**above**: (rows above the current row)
**below**: (rows including and below the current row).

It ensures that above and below have the same length and compares them. If they are equal, it means that the grid is symmetric up to the current row (r), and it returns the current row index (r).

If no symmetry is found up to the last row, it returns 0.

Main Method: (The Entry point to all Console Applications - note some later versions of .Net do not require you to specify this method in your Program.cs file). 

Reads the contents of the file passed to `File.ReadAllText()` and splits it into blocks using the double newline ("\n\n") as a separator. Each block (pattern) is processed separately.

For each block, it further splits it into lines, representing a grid.

Calls FindMirror to find the symmetry in rows (row) and columns (col).

Updates the total based on the calculated symmetry values.

Prints the final total to the console.

Now you may be thinking woah, but what is all this `SelectMany`, and `GroupBy` doing ?

The code uses LINQ to manipulate the grid data. It flattens the grid using SelectMany, groups characters by their index using GroupBy, and creates new strings from grouped characters to represent columns.


### More Detail of LINQ:
**SelectMany and Select (Flattening the Grid):**

```csharp
grid.SelectMany(s => s.Select((character, index) => new { character, index }))
``` 

This part flattens the 2D grid into a sequence of anonymous objects ({ character, index }), where the index is the index within the row. 

It uses `SelectMany` to flatten the grid into a sequence of characters and their corresponding indices.

**GroupBy (Grouping by Index):**

.GroupBy(sequence => sequence.index): This groups the flattened sequence by the index. It creates groups where each group represents a column, and the elements within the group are characters from the same column.

**Select (Creating Strings from Groups):**
```csharp
.Select(group => new string(group.Select(x => x.character).ToArray())): 
```

For each group (representing a column), it creates a new string by selecting the characters (x.character) from each element in the group and converting them to an array.

The resulting array represents the columns of the original grid, now in the form of lines (rows).


These columns can then be passed to the same FindMirror function to find the mirror line of the columns. We don't care if it's a row or a mirror as the logic/concept is the same (lines to the left or in our case always above). 

## Part2:
```csharp
 aboveRows.Zip(belowRows, CountDifferences)
```

 This uses the Zip method to combine corresponding elements from the aboveRows and belowRows arrays into pairs. For each pair of strings (above, below), it calls the CountDifferences method to count the number of differences between the two strings.

The result is a sequence of counts of differences for each corresponding pair of strings.

`.Sum()` Finally, it sums up all the differences counts, obtaining the total number of differences between the above and below rows.

**CountDifferences**

This uses the Zip method again, comparing corresponding characters in str1 and str2.

For each pair of characters (a, b), it returns 0 if the characters are equal and 1 if they are different. This results in a sequence of 0 and 1 values, indicating whether characters at the same position in the two strings are equal or not.

.Sum(): Finally, it sums up the values in the sequence, obtaining the total count of differences between the two strings.

If the total difference is 1, we know that the current row is the one with the smudge, cos the current row is exactly the same, other than 1 character (co-incidence i think not).