Advent Of Code 2021 - Day 13

We're passing the half way point today, Day 13. We're gonna celebrate by folding some transparent paper, to obtain a code we need for a nice, thermal camera.

This is our input:

6,10
0,14
9,10
0,3
10,4
4,11
6,0
6,12
4,1
0,13
10,12
3,4
3,0
8,4
1,10
2,14
8,10
9,0

fold along y=7
fold along x=5

The first part of this is a number of points (X,Y) that are marked with dots on the paper. The second part is a set of folding instructions. Folding along Y means fold up, folding along X means fold left.

Since the paper is transparent, any points remain visible even if folded over. If two dots overlap, they still count as one dot. Part 1 tells us to just carry out the first instruction, and then count the visible dots. It's reasonable to assume that we will be going through all the instructions in Part 2, so I made my solution with that in mind.

First, lets create some useful data from our input:

var input = File.ReadAllText("input.txt").Split(Environment.NewLine + Environment.NewLine);

var points = input[0].Split(Environment.NewLine)
    .Select(x => (int.Parse(x.Split(',')[0]), int.Parse(x.Split(',')[1]))).ToList();

var instructions = input[1].Split(Environment.NewLine).Select(x => x.Replace("fold along ", string.Empty))
    .Select(x => (x.Split('=')[0], int.Parse(x.Split('=')[1])));

var maxCol = points.Max(x => x.Item1);
var maxRow = points.Max(x => x.Item2);

var grid = new Dictionary<(int X, int Y), bool>();

for (var i = 0; i <= maxRow; i++)
{
    for (var j = 0; j <= maxCol; j++)
    {
        var point = (j, i);
        grid.Add(point, false);
    }
}

foreach (var point in points)
{
    grid[point] = true;
}

This is not very pretty to me, but after splitting, replacing and looping we have a nice grid with points, some of them have dots so they get a value of true, the rest are false.

Then, lets start going through our instructions and folding the paper accordingly:

var foldNr = 0;
foreach (var (axis, line) in instructions)
{
    foldNr++;

    var pointsToFold = (axis == "y")
        ? grid.Where(x => x.Key.Y > line)
        : grid.Where(x => x.Key.X > line);

    var edge = (axis == "y") ? grid.Max(x => x.Key.Y) : grid.Max(x => x.Key.X);

    var newPoints = pointsToFold.Select(x => GetInversePoint(x, axis, edge))
        .ToDictionary(x => x.Key, x => x.Value);

    foreach (var point in newPoints.Where(point => point.Value))
    {
        grid[point.Key] = point.Value;
    }

    grid = (axis == "y")
        ? grid.Where(x => x.Key.Y < line).ToDictionary(x => x.Key, x => x.Value)
        : grid.Where(x => x.Key.X < line).ToDictionary(x => x.Key, x => x.Value);

    var visible = grid.Count(x => x.Value);
    Console.WriteLine($"Visible after fold {foldNr}: {visible}");
}

We find the points that will be folded, get their new location with GetInversePoint() and set the in our grid to true if the new point has true for value. Then we filter out the points that can be discarded after our fold, and print the number of visible dots after each fold. Here, we could have stopped after one fold, but since I assumed we would be doing all the folds in Part 2, I decided to just print the values here.

The GetInversePoint() method takes a point, the axis we're folding and the relevant edge value. Then we return a new point that is equally far from the edge, but on the opposite side of the paper.

private static KeyValuePair<(int, int), bool> GetInversePoint(KeyValuePair<(int, int), bool> point, string axis, int edge)
{
    if (axis == "y")
    {
        var distanceFromEdge = edge - point.Key.Item2;
        return new KeyValuePair<(int, int), bool>((point.Key.Item1, distanceFromEdge), point.Value);
    }
    else
    {
        var distanceFromEdge = edge - point.Key.Item1;
        return new KeyValuePair<(int, int), bool>((distanceFromEdge, point.Key.Item2), point.Value);
    }
}

For Part 2, we will go through all the instructions, and when we're done folding the dots will form a code made up of eight capital letters. This is a pretty simple addition to the solution above:

foreach (var (point, value) in grid.OrderBy(x => x.Key.Y))
{
    if (point.X == 0)
    {
        Console.WriteLine();
    }

    Console.Write(value ? "#" : ".");
}

I enjoyed todays challenge, it was a little bit easier than the previous two for me. I was in a rush when I did this, so I'm sure there are optimizations to be made. But overall, I'm feeling alright about this.