--- Day 10: Factory ---

December 10

Listening to: PlayStation downtempo mix


This one seems particularly complex. But I guess it's day 10/12, so having some sort of complex configuration is necessary. It's also the first day I felt to really make a more object-oriented approach.

The example given for part 1 shows pressing a button multiple times, but that makes no sense - the two presses would exactly cancel each other out, so any combination of buttons is going to require pressing a button either zero or one times. Given that, getting the power set of all of the buttons to find all of the different combinations to press makes the most sense. Admittedly, it took a while for me to figure out a good way to generate that, but hey, now I have a good utility function, using a bitmask!

public static IEnumerable<List<T>> PowerSet<T>(List<T> list)
{
    int numSets = 1 << list.Count;

    for (int mask = 0; mask < numSets; mask++)
    {
        var nextSet = new List<T>();
        for (int i = 0; i < list.Count; i++)
        {
            if ((mask & (1 << i)) > 0)
            {
                nextSet.Add(list[i]);
            }
        }

        yield return nextSet;
    }
}

From there, it's trivially easy to just feed it the list of the buttons, sort it by size, and just pick the first one in the list that provides the right answer.


Now this time, it looks like the multiset needs to be used, instead of the powerset, due to the possibility of repeated items.

public static IEnumerable<List<T>> Multiset<T>(List<T> list, int size)
{
    if (size == 0)
    {
        yield return new();
        yield break;
    }

    foreach (var subset in Multiset(list, size - 1))
    {
        foreach (var item in list)
        {
            var nextList = new List<T>(subset)
            {
                item
            };
            yield return nextList;
        }
    }
}

By baking the size of it into the multiset, I can easily

  1. Start or look at only a specific length of combination
  2. Use recursion to generate the lists of smaller lengths

Point 1 is really important here as each button can only increase one of the joltages by one, meaning there needs to be a minimum of presses equal to the highest joltage. So if it's, for example, 12, then I can skip checking all presses of 1-11.

... except this bruteforce method isn't really going to work. Just with the example input it takes 18 seconds with a max length of 12. A few in the real input have joltages approaching 200!


Taking a small break, and looking at some of the discussion around the problem, I'm thinking that just blindly checking every combination is probably a recipie for failure; a lot of the combinations are likely going too high on one of the buttons.

It looks like this is basically just a series of linear equations... and one of the main ways people are solving it are with something called Z3. Honeslty I would have prefered just solving it myself... but doing these problems are about improving my abilities as a programmer, and one of those abilities is learning and using new tools. So I suppose this year I get to learn Z3 and use it to solve this problem.

I'm not going to share my code here - mostly because I don't think it's worth sharing. I feel like I don't understand it well enough. I just fed Z3 come constraints and... it just popped out an answer. It's cool, but I admit I feel a bit uneasy about black-box type things like this.


Time taken: 2 hours

My solutions for today's puzzles