Advent of Code Day 7: Splitting Tachyon Beams

On Day 7 of Advent of Code, we’re studying tachyon beams while we’re stuck in a teleporter.

I imagine this puzzle as a game where each iteration moves the beam until it hits the end of the manifold.

Moving a beam

A Beam is a list of positions in the manifold at a given iteration.

record Position(int X, int Y);
record Beam(IEnumerable<Position> Locations);

Here’s the method to move a beam,

static Beam Move(string[][] manifold, Beam beam)
{
    var newLocations = new HashSet<Position>();

    foreach (var current in beam.Locations)
    {
        var downward = manifold[current.X + 1][current.Y];
        if (downward == ".")
        {
            newLocations.Add(new Position(current.X + 1, current.Y));
        }
        else if (downward == "^")
        {
            newLocations.Add(new Position(current.X + 1, current.Y - 1));
            newLocations.Add(new Position(current.X + 1, current.Y + 1));
        }
    }

    return new Beam(newLocations);
}

After moving a beam, I need the entry position of a beam,

static Beam Start(string[][] manifold)
{
    for (int i = 0; i < manifold[0].Length; i++)
    {
        if (manifold[0][i] == "S")
        {
            return new Beam([new Position(0, i)]);
        }
    }

    return new Beam([]);
}

With those two methods, a beam enters and moves downward,

var start = Start(manifold);
var newPosition = Move(manifold, start);
newPosition = Move(manifold, newPosition);

All that’s left is to count splits and move the beam to the end.

Counting splits

Next, I add the number of splits to Beam and create HasReacheTheEnd().

Here’s my full solution,

var manifold = new string[][]
{
    [ ".", ".", ".", ".", ".", ".", ".", "S", ".", ".", ".", ".", ".", ".", "." ],
    [ ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", "." ],
    [ ".", ".", ".", ".", ".", ".", ".", "^", ".", ".", ".", ".", ".", ".", "." ],
    [ ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", "." ],
    [ ".", ".", ".", ".", ".", ".", "^", ".", "^", ".", ".", ".", ".", ".", "." ],
    [ ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", "." ],
    [ ".", ".", ".", ".", ".", "^", ".", "^", ".", "^", ".", ".", ".", ".", "." ],
    [ ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", "." ],
    [ ".", ".", ".", ".", "^", ".", "^", ".", ".", ".", "^", ".", ".", ".", "." ],
    [ ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", "." ],
    [ ".", ".", ".", "^", ".", "^", ".", ".", ".", "^", ".", "^", ".", ".", "." ],
    [ ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", "." ],
    [ ".", ".", "^", ".", ".", ".", "^", ".", ".", ".", ".", ".", "^", ".", "." ],
    [ ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", "." ],
    [ ".", "^", ".", "^", ".", "^", ".", "^", ".", "^", ".", ".", ".", "^", "." ],
    [ ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", "." ]
};

var beam = Start(manifold);
while (!HasReachedTheEnd(manifold, beam))
{
    beam = Move(manifold, beam);
}

Console.WriteLine(beam.SplitCount);
Console.ReadLine();

static Beam Start(string[][] manifold)
{
    for (int i = 0; i < manifold[0].Length; i++)
    {
        if (manifold[0][i] == "S")
        {
            return new Beam(0, [new Position(0, i)]);
        }
    }

    return new Beam(0, []);
}

static bool HasReachedTheEnd(string[][] manifold, Beam beam)
{
    var anyBeam = beam.Locations.First();
    return anyBeam.X >= manifold.Length - 1;
}

static Beam Move(string[][] manifold, Beam beam)
{
    var splits = 0;
    var newLocations = new HashSet<Position>();

    foreach (var current in beam.Locations)
    {
        var downward = manifold[current.X + 1][current.Y];
        if (downward == ".")
        {
            newLocations.Add(new Position(current.X + 1, current.Y));
        }
        else if (downward == "^")
        {
            splits++;

            newLocations.Add(new Position(current.X + 1, current.Y - 1));
            newLocations.Add(new Position(current.X + 1, current.Y + 1));
        }
    }

    return new Beam(beam.SplitCount + splits, newLocations);
}

record Position(int X, int Y);
record Beam(int SplitCount, IEnumerable<Position> Locations);

I thought a “tachyon” was a made-up word until I Googled it. TIL what a tachyon is. Win-win.

Et voilà!

Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, Street-Smart Coding: 30 Ways to Get Better at Coding. That’s the roadmap I wish I’d known from day one.

Get your copy of Street-Smart Coding here

Friday Links: Becoming a senior dev, coding, and phones

Hey, it’s been a busy week. I’ve been preparing my last book for publish on Amazon, so I didn’t read that much.

But I still want to share some interesting reads. Here are my top 3 most read posts from the quarter plus a funny weekend project.

#1. I always joke that I don’t know when “Senior” made it into my job title. But I know it was thanks to good mentors. Here are 5 lessons from one of my mentors (4min). I’ve been following lesson #1 since I met him…Well, not in the past week.

#2. What’s the first thing that comes to mind when you hear “get good at coding”? Younger me only thought about learning programming languages. But here are 7 unconventional ways to improve your coding skills (3min).

#3. I’d do a lot differently if I were a junior again. Here’s one lesson I wish I had known 10 years ago (3min). That would have made me a well-rounded coder.

#4. What if we could slam a phone after a boring Zoom call? That was the side project of someone who turned a rotary phone into a meeting handset (8min).


Speaking of coding, are you following the Advent of Code? I have. I missed a couple of days, but I’m catching up this weekend. Here’s my last solution: Advent of Code Day 6: Doing Cephalopods’ Math (3min).


(Bzzz…Radio voice) This email was brought to you by… my new book, Street-Smart Coding: 30 lessons to help you code like a pro. From Googling to clear communication, it covers the lessons you don’t learn in tutorials. It’s now out on Kindle and paperback on Amazon.

See you next time,

Cesar

Advent of Code Day 6: Doing Cephalopods' Math

On Day 6 of Advent of Code, we’re helping some cephalopods with their kids’ math homework…while they open a trash compactor.

First, I define a problem and function to solve it. A problem is an operation and some numbers.

static int Perform(Problem p)
    => p.Operation switch
    {
        Operation.Add => p.Numbers.Sum(),
        Operation.Multiply => p.Numbers.Aggregate((a, b) => a*b),
        _ => throw new NotImplementedException()
    };

abstract record Operation
{
    public record Add : Operation;
    public record Multiply : Operation;
}
record Problem(Operation Operation, IEnumerable<int> Numbers);

Cephalopods’ math is weird with operations and operands organized vertically, not horizontally. Parsing is the trick part: the input is a 2D array where each column is a problem. Numbers above and operator at the bottom.

I loop through the array of arrays column-wise. The last element becomes the operation, and the rest become the numbers,

static IEnumerable<Problem> Parse(string[][] problems)
{
    var parsedProblems = new List<Problem>();

    for (int j = 0; j < problems[0].Length; j++)
    {
        var numbers = Enumerable.Range(0, problems.Length - 1)
                                .Select(i => int.Parse(problems[i][j]))
                                .ToList();
        Operation op = problems[^1][j] == "+"
                            ? new Operation.Add()
                            : new Operation.Multiply();
        parsedProblems.Add(new Problem(op, numbers));
    }
    return parsedProblems;
}

With the parsing in place, Day 6 is almost done. Here’s my final solution,

var problems = new[]
{
    new[]{ "123", "328", "51", "64" },
    new[]{ "45", "64", "387", "23" },
    new[]{ "6", "98", "215", "314" },
    new[]{ "*", "+", "*", "+" }
};

var total = Parse(problems).Sum(Perform);
Console.WriteLine(total);
Console.ReadKey();

static IEnumerable<Problem> Parse(string[][] problems)
{
    var parsedProblems = new List<Problem>();

    for (int j = 0; j < problems[0].Length; j++)
    {
        var numbers = Enumerable.Range(0, problems.Length - 1)
                                .Select(i => int.Parse(problems[i][j]))
                                .ToList();
        Operation op = problems[^1][j] == "+"
                            ? new Operation.Add()
                            : new Operation.Multiply();
        parsedProblems.Add(new Problem(op, numbers));
    }
    return parsedProblems;
}

static int Perform(Problem p)
    => p.Operation switch
    {
        Operation.Add => p.Numbers.Sum(),
        Operation.Multiply => p.Numbers.Aggregate((a, b) => a*b),
        _ => throw new NotImplementedException()
    };

abstract record Operation
{
    public record Add : Operation;
    public record Multiply : Operation;
}
record Problem(Operation Operation, IEnumerable<int> Numbers);

Two one-off bugs got me this time, but I finally cracked it. The parsing part isn’t that functional in style, but “it works on my machine.” Et voilà!

Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, Street-Smart Coding: 30 Ways to Get Better at Coding. That’s the roadmap I wish I’d known from day one.

Get your copy of Street-Smart Coding here

Advent of Code Day 5: Counting Fresh Ingredients

On Day 5 of Advent of Code, we’re helping the elves to find which ingredients are fresh or spoiled.

To find if an ingredient is fresh, I create a FreshIngredientRange with a method IsFresh(),

record FreshIngredientRange(int IngredientId1, int IngredientId2)
{
    public bool IsFresh(int productId)
        => productId >= IngredientId1 && productId <= IngredientId2;
}

Then, to count all the fresh ingredients, I write a method that receives all product ranges and all candidate products. A single LINQ query will do the counting,

static int CountFreshIngredients(
    IEnumerable<FreshIngredientRange> ranges,
	IEnumerable<int> productIds)
        => productIds.Count(productId => ranges.Any(r => r.IsFresh(productId)));

Here’s my final solution,

var ranges = new FreshIngredientRange[]
{
    new FreshIngredientRange(3, 5),
    new FreshIngredientRange(10, 14),
    new FreshIngredientRange(16, 20),
    new FreshIngredientRange(12, 18)
};

var productIds = new int[] { 1, 5, 8, 11, 17, 32 };

var count = CountFreshIngredients(ranges, productIds);
Console.WriteLine(count);
Console.ReadKey();

static int CountFreshIngredients(IEnumerable<FreshIngredientRange> ranges, IEnumerable<int> productIds)
    => productIds.Count(productId => ranges.Any(r => r.IsFresh(productId)));

record FreshIngredientRange(int IngredientId1, int IngredientId2)
{
    public bool IsFresh(int productId)
        => productId >= IngredientId1 && productId <= IngredientId2;
}

That’s an easy one with LINQ. Just one single line. Et voilà!

Advent of Code sharpens your coding skills. But coding is more than typing symbols fast. It’s also about teamwork, collaboration, and many skills I share in my book, Street-Smart Coding: 30 Ways to Get Better at Coding. That’s the roadmap I wish I’d known from day one.

Get your copy of Street-Smart Coding here

A Quick Lesson After A Long Debugging Session (And Almost Pulling My Hair Off)

I almost pulled my hair off.

I debugged an issue in a Blazor grid for over two half days. I followed my own advice from Street-Smart Coding:

  • Isolated the problem
  • Removed all irrelevant parts
  • Discussed it with a rubber duck

Just to keep seeing the same error message: TypeError: Cannot read properties of null (reading ‘removeChild’).

StackOverflow says it was Blazor trying to remove an orphan element. So I removed everything except for my grid and wrapped it around a div.

Same mistake.

After questioning my career choices and almost removing “Senior” from my title, I asked for help.

My coworker pulled my branch and reproduced the issue. To my surprise, the issue wasn’t only in my grid. It was in all other grids, all over the app. It was an issue in the Blazor component itself we were using for grids. Arrggg!

Sometimes you just need to ask for help earlier. Like one of my mentors told me and the team, “you have nothing to prove. Ask for help.” And that’s something I cover on Chapter #2 of Street-Smart Coding.