top of page

Advent of code in SPL - 2025 day 10 part 1

  • Writer: Gabriel Vasseur
    Gabriel Vasseur
  • 14 hours ago
  • 5 min read

Day 10 is here.


Part 1 challenge


First a quick summary of the challenge in case you're not logged on the advent of code or haven't reached day 10 yet.


We are given a target light pattern we need to achieve, for instance [.##.], and a set of buttons, for instance (3) (1,3) (2) (2,3) (0,2) (0,1). [.##.] means first light off, second and third on, and fourth off.


Buttons are used to toggle lights on and off. So button (3) would toggle the fourth light. Button (1,3) would toggle both the second and fourth light.


The challenge is to find the smallest number of button presses required to go from all lights off to the target light pattern.


In this case it would be 2: one for (0,2) and one for (0,1) (in any order):

....   ---press(0,2)--->   #.#.   ---press(0,1)--->   .##.

Part 1 solution


Day 10 part 1 really lends itself to think in terms of binary operations. Lights are either on or off, so the light pattern [#.....] could be represented as a binary number b100000.


In decimal this would normally be 32, because the standard binary notation writes the Most Significant Bit first (MSB-first). But for this challenge, it makes our lives much easier if we adopt the Least Significant Bit first (LSB-first) notation. So for us today b100000 is 1.


Similarly the button (0,3,4) could be represented as b100110 (note: we know from the light pattern that we need a total of 6 bits, so I added a 0 at the end, but in LSB-first it doesn't matter if we do or not), which in decimal (in LSB-first) is 1 + 8 + 16 = 25.


We're told in the challenge description that pressing (0,3,4) when the light pattern is [#.....] results in [...##.]. This is basically a binary XOR operation:

b100000 XOR b100110 = b000110

A binary XOR takes each bit from one number and applies the "exclusive or" logic with the matching bit from the other number, which is true if one or the other bit is true, but false if none or both are true.

Binary          LSB-first      MSB-first
b 1 0 0 0 0 0        1              32
b 1 0 0 1 1 0       25              38
---------------------------------------
b 0 0 0 1 1 0       24               6

Note how it's easy to follow the calculation in binary but the decimal side is much more opaque (it looks like it's doing a kind subtraction but that's misleading, for instance 3 XOR 4 would yield 7).


The funny thing about binary XOR is that you can think of any one of the two numbers as the starting state and the other one as the switch, and all that happens is the end state is the same as the starting state, except that the bits that are true in the switch are flipped. This logic is reversible, but in our example it makes sense to consider b100000 (the light pattern) as the starting point and b100110 (the button) as the switch.


Of course, SPL can do binary XOR:

 And if you want to can check that 32 XOR 38 does gives 6 too.


Amazing. So this gives us a cool efficient way to calculate the results of button presses. What we have to do now is enumerate all potential combinations of button presses, which I initially thought was going to be a nightmare (and it absolutely is in part 2), but my colleague Lochie pointed out right away that pressing the same button twice basically has no effect, so we don't have to consider cases where the same button is pressed more than once.


It means each button can only be pressed either zero or one time... doesn't that sound binary again?


So let's say we have a problem with 3 buttons, We could enumerate the possibilities like so (0 means no press, 1 means one press):

button1  button2  button3
    0        0       0
    0        0       1
    0        1       0
    0        1       1
    1        0       0
    1        0       1
    1        1       0
    1        1       1

This looks a lot like counting in binary...! Here I reverted to MSB-first (sue me), but in LSB-first all that changes is the order of the combinations. Hopefully you get the idea:

button1  button2  button3      decimal representation of the combination
    0        0       0            0
    0        0       1            1
    0        1       0            2
    0        1       1            3
    1        0       0            4
    1        0       1            5
    1        1       0            6
    1        1       1            7

So if we have 3 buttons, we need "2 to the power of 3" combinations. And we can ignore the 0, since pressing no buttons never helped anyone. So valid combinations are basically:


For each combination we then just need to XOR the relevant buttons together (since the starting point is 0, all lights off) and see which ones land on the desired light pattern. If there's more than one, we'll have to keep track of how many button presses in total, and pick the one with the least presses.


Time to hit the search bar! Let's start by parsing the input. We'll start with the sample data:


Let's first make the target light pattern binary:

Note how our use of LSB-first makes the code much cleaner.


Then do the same for buttons. We're going to have to mvexpand to treat each button individually, which means we need to first number the machines so we can put them back together later.


Right, so before we go further we have to address something: this is the point where I discovered that foreach mode=multivalue DOES NOT WORK WHEN THE MULTIVALUE FIELD HAS ONLY ONE VALUE!!! As far as I'm concerned this is a major shame on Splunk and a major annoyance for us. I'm working hard with the help of my fantastic account team to get Splunk to address this. In the mean time we're going to try to avoid foreach mode=multivalue where we can and handle the failure mode where we can't.


So here is the same code refactored:

It actually looks cleaner, not just because we don't have to deal with the bug.


Let's keep going. At this point we have a list of binary button light patterns and the target light pattern for each machine. Let's enumerate all the possible press combinations:

Great, so now we need to convert a binary representation of the presses combination (combo) into the actual list of buttons involved.


Maybe the easiest way to do this is to iterate over the list of buttons, keeping track of where we are. For each of them we check if the corresponding bit is 1 in combo. I personally find it easier to think in terms of a for loop, so this is my first version:


Now that we got it working, let's refactor the evil foreach out (though in this case we're guaranteed to always have more than one button):

Nice.


Now that we have a list of buttons to press, we can evaluate what the results would look like. Unfortunately we can't use bit_xor like we can use sum() so there's no avoiding our foe and we have to protect against the failings of SPL's multivalue foreach:


From there we can solve the puzzle:

Hurrah!


It's going to be difficult to do our usual "save the full challenge data as a csv, add a 'machine' header line before uploading it to splunk" routine because buttons have commas and that is completely messing with the idea of a csv. We'd need to add quotes everywhere. I don't like it but, in this instance, it's easier to just copy and paste the full data:

Amazing. That was quite fun. (if we ignore the hair-pulling foreach mode=multivalue fail)


Part 2


Part 2 is another story, and it's been holding the release of new episodes for too long. So it'll have to come separately!

Comments


©2021 by Gabriel Vasseur. Proudly created with Wix.com

bottom of page