Advent of Code Day 10: Initializing Factory Machines

On Day 10 of Advent of Code, we’re helping to turn on factory machines by configuring light indicators.

Each machine has buttons that toggle indicators, and the challenge is figuring out the minimal combination that produces the target pattern.

To model this, I define a machine as a list of booleans representing its light indicators. Then, I write Push() to toggle light indicators at a given index.

record Machine(bool[] Indicators)
{
    public Machine Push(int[] buttons)
    {
        var pushed = new bool[Indicators.Length];
        Array.Copy(Indicators, pushed, Indicators.Length);
        foreach (var button in buttons)
        {
            pushed[button] = !Indicators[button];
        }
        return new Machine(pushed);
    }
}

To configure a machine, I use this StackOverflow answer to generate all button combinations and apply them to reach the target indicator pattern.

static int[][] ConfigureIndicators(Machine m, int[][] buttons)
{
    var turnOn = new List<int[][]>();

    var combinations = buttons.Combinations();
    foreach (var combination in combinations)
    {
        var tmp = combination.Aggregate(m.Off(), (machine, comb) => machine.Push(comb));
        if (m.Indicators.SequenceEqual(tmp.Indicators))
        {
            turnOn.Add(combo);
        }
    }

    return turnOn.MinBy(t => t.Length);
}

LINQ Aggregate method to the rescue here!

Unlike the puzzle instructions, with buttons.Combinations() I’m assuming I can only toggle a sequence of buttons once.

So far, I can write,

var m = new Machine([false, true, true, false]);
var buttons = new int[][]
{
    [ 3 ],
    [ 1,3 ],
    [ 2 ],
    [ 2,3 ],
    [ 0,2 ],
    [ 0,1 ]
};
var presses = ConfigureIndicators(m, buttons);
// presses.Length

And this puzzle is almost a voilà moment. I skip parsing the diagrams and just hard‑code them, then sum the button‑press lengths. Yes, I’m a bit lazy.

Here’s my full solution,

var m = new Machine([false, true, true, false]);
var buttons = new int[][]
{
    [ 3 ],
    [ 1,3 ],
    [ 2 ],
    [ 2,3 ],
    [ 0,2 ],
    [ 0,1 ]
};
var presses = ConfigureIndicators(m, buttons);

var m2 = new Machine([false, false, false, true, false]);
var buttons2 = new int[][]
{
    [0,2,3,4],
    [2,3],
    [0,4],
    [0,1,2],
    [1,2,3,4]
};
var presses2 = ConfigureIndicators(m2, buttons2);

var m3 = new Machine([false, true, true, true, false, true]);
var buttons3 = new int[][]
{
    [0,1,2,3,4],
    [0,3,4],
    [0,1,2,4,5],
    [1,2]
};
var presses3 = ConfigureIndicators(m3, buttons3);

var total = presses.Length + presses2.Length + presses3.Length;
Console.WriteLine(total);
Console.ReadKey();

static int[][] ConfigureIndicators(Machine m, int[][] buttons)
{
    var turnOn = new List<int[][]>();

    var combinations = buttons.Combinations();
    foreach (var combo in combinations)
    {
        var tmp = combo.Aggregate(m.Off(), (machine, comb) => machine.Push(comb));
        if (m.Indicators.SequenceEqual(tmp.Indicators))
        {
            turnOn.Add(combo);
        }
    }

    return turnOn.MinBy(t => t.Length);
}

record Machine(bool[] Indicators)
{
    public Machine Off()
        => new Machine(new bool[Indicators.Length]);

    public Machine Push(int[] buttons)
    {
        var pushed = new bool[Indicators.Length];
        Array.Copy(Indicators, pushed, Indicators.Length);
        foreach (var button in buttons)
        {
            pushed[button] = !Indicators[button];
        }
        return new Machine(pushed);
    }

    public override string ToString()
        => $"[{string.Join("", Indicators.Select(i => i ? "#" : "."))}]";
}

internal static class Extensions
{
    public static IEnumerable<T[]> Combinations<T>(this IEnumerable<T> source)
    {
        if (null == source)
            throw new ArgumentNullException(nameof(source));

        T[] data = source.ToArray();

        return Enumerable
          .Range(1, 1 << (data.Length))
          .Select(index => data
             .Where((v, i) => (index & (1 << i)) != 0)
             .ToArray());
    }
}

And finally…et voilà! The factory lights up, and Day 10 is complete.

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 9: Finding the Largest Rectangle of Red Tiles

On Day 9 of Advent of Code, we’re helping elves to redecorate the theater by rearranging the red tiles on the floor.

After yesterday’s struggle, this is an easy one when I consider every pair of points as the opposite corners of a rectangle to calculate its area. Like this,

record Rectangle(Position Left, Position Right)
{
    public int Area
        => (Math.Abs(Left.Y - Right.Y) + 1)
                * (Math.Abs(Left.X - Right.X) + 1);
}

Then, I reuse Pairs() from yesterday to find all rectangles. Next, I use LINQ’s MaxBy to find the largest one. Like this,

var largest = redTiles.Pairs()
    .Select(p => new Rectangle(p.First, p.Second))
    .MaxBy(r => r.Area);

With those two pieces, the puzzle is solved. Here’s my full solution,

var redTiles = new[]
{
    new Position(7,1),
    new Position(11,1),
    new Position(11,7),
    new Position(9,7),
    new Position(9,5),
    new Position(2,5),
    new Position(2,3),
    new Position(7,3),
};

var largest = redTiles.Pairs()
                    .Select(p => new Rectangle(p.First, p.Second))
                    .MaxBy(r => r.Area);

Console.WriteLine(largest);
Console.ReadKey();

record Position(int X, int Y);
record Rectangle(Position Left, Position Right)
{
    public int Area
        => (Math.Abs(Left.Y - Right.Y) + 1)
                * (Math.Abs(Left.X - Right.X) + 1);
}

internal static class Extensions
{
    public static IEnumerable<(T First, T Second)> Pairs<T>(this IEnumerable<T> self)
    {
        return self.SelectMany((fst, i) => self.Skip(i + 1).Select(snd => (fst, snd)));
    }
}

I deserved some rest from yesterday’s puzzle. Today, I can easily say: 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 8: Connecting Junction Boxes

On Day 8 of Advent of Code, we’re helping elves with some decorations, so we’re connecting junction boxes.

Unlike the first 7 days, I cheated. Yes! This puzzle wasn’t fun anymre after spending a couple of hours to make the example work. In a moment of frustration, I went to Reddit for some help and I reworked my original solution.

First, I tried to create circuits as I visited pair of junction boxes. But, after getting some help from Reddit, every junction box is a 1-box circuit to then merge all the pairs.

Finding distances

To find the distance between all possibles pair of junction boxes, I borrowed the Pairs() method from Day 3.

To calculate the distance, I use the squared of the distance as a small optimization.

var distances = allBoxes.Pairs()
    .Select(p => (p.First, p.Second, Distance: Distance(p.First, p.Second)))
    .OrderBy(p => p.Distance);

static int Distance(JunctionBox p1, JunctionBox p2)
{
    var d2 = (p1.X - p2.X) * (p1.X - p2.X)
                + (p1.Y - p2.Y) * (p1.Y - p2.Y)
                + (p1.Z - p2.Z) * (p1.Z - p2.Z);
    return d2;
}

static class Extensions
{
    public static IEnumerable<(T First, T Second)> Pairs<T>(this IEnumerable<T> self)
    {
        return self.SelectMany((fst, i) => self.Skip(i + 1).Select(snd => (fst, snd)));
    }
}

Connecting boxes

A circuit is a wrapper around a HashSet with a method to merge two circuits.

record Circuit(HashSet<JunctionBox> Boxes)
{
    public Circuit(JunctionBox box)
        : this(new HashSet<JunctionBox>([box]))
    {
    }

    public bool Contains(JunctionBox box)
        => Boxes.Contains(box);

    public void Connect(Circuit circuit)
    {
        foreach (var box in circuit.Boxes)
        {
            Boxes.Add(box);
        }
    }
}

With the list of junction boxes by distance and a circuit, here’s the full solution,

var allBoxes = new[]
{
    new JunctionBox(162,817,812),
    new JunctionBox(57,618,57),
    new JunctionBox(906,360,560),
    new JunctionBox(592,479,940),
    new JunctionBox(352,342,300),
    new JunctionBox(466,668,158),
    new JunctionBox(542,29,236),
    new JunctionBox(431,825,988),
    new JunctionBox(739,650,466),
    new JunctionBox(52,470,668),
    new JunctionBox(216,146,977),
    new JunctionBox(819,987,18),
    new JunctionBox(117,168,530),
    new JunctionBox(805,96,715),
    new JunctionBox(346,949,466),
    new JunctionBox(970,615,88),
    new JunctionBox(941,993,340),
    new JunctionBox(862,61,35),
    new JunctionBox(984,92,344),
    new JunctionBox(425,690,689),
};

var circuits = allBoxes.Select(box => new Circuit(box)).ToList();

var distances = allBoxes.Pairs()
    .Select(p => (p.First, p.Second, Distance: Distance(p.First, p.Second)))
    .OrderBy(p => p.Distance);
foreach (var pair in distances.Take(1_000))
{
    var hasFirstBox = circuits.First(c => c.Contains(pair.First));
    var hasSecondBox = circuits.First(c => c.Contains(pair.Second));
    if (hasFirstBox != hasSecondBox)
    {
        hasFirstBox.Connect(hasSecondBox);
        circuits.Remove(hasSecondBox);
    }
}

foreach (var circuit in circuits.OrderBy(c => c.Boxes.Count).Take(3))
{
    Console.WriteLine(circuit.Boxes.Count);
}
Console.ReadKey();

static int Distance(JunctionBox p1, JunctionBox p2)
{
    var d2 = (p1.X - p2.X) * (p1.X - p2.X)
                + (p1.Y - p2.Y) * (p1.Y - p2.Y)
                + (p1.Z - p2.Z) * (p1.Z - p2.Z);
    return d2;
}

record Circuit(HashSet<JunctionBox> Boxes)
{
    public Circuit(JunctionBox box)
        : this(new HashSet<JunctionBox>([box]))
    {
    }

    public bool Contains(JunctionBox box)
        => Boxes.Contains(box);

    public void Connect(Circuit circuit)
    {
        foreach (var box in circuit.Boxes)
        {
            Boxes.Add(box);
        }
    }
}
record JunctionBox(int X, int Y, int Z)
{
    public override string ToString() => $"{X},{Y},{Z}";
}

static class Extensions
{
    public static IEnumerable<(T First, T Second)> Pairs<T>(this IEnumerable<T> self)
    {
        return self.SelectMany((fst, i) => self.Skip(i + 1).Select(snd => (fst, snd)));
    }
}

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 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