Community Blog
Get the latest updates on the Splunk Community, including member experiences, product education, events, and more!

[Puzzles] Solve, Learn, Repeat: Advent of Code - Day 6

ITWhisperer
SplunkTrust
SplunkTrust

Advent of Code

In order to participate in these challenges, you will need to register with the Advent of Code site, so that you can retrieve your own datasets for the puzzles. When you get an answer, you will need to submit it to the Advent of Code site to determine whether the answer is correct. I have already completed the 2025 set using python, so I will know when my SPL generates the correct result.

Day 6

Each day's puzzle is split into two parts; part one is usually easier than part two, and you cannot normally reach part two until you have successfully completed part one. Day 6 is about rearranging the input to be able to complete some simple arithmetic equations. Please visit the website for full details of the puzzle.

This article contains spoilers!

In fact, the whole article is a spoiler as it contains solutions to the puzzle. If you are trying to solve the puzzle yourself and just want some pointers to get you started, stop reading when you have enough, and return if you get stuck again, or just want to compare your solution to mine!

Part One

As with all the Advent of Code puzzles, the description of the problem is aided by some example input; in this case, the input is grid showing columns of numbers followed by the operation that needs to be applied to them. The aim of the puzzle is to determine, for your own dataset, the total sum of all the equations.

Initialising your data

The first thing to do is initialise your data. One way to do this is to save it to a csv file and use inputlookup to load it. Alternative, you could just use makeresults (as I have done here), and set a field to the data:

| makeresults
| eval _raw="123 328  51 64 
 45 64  387 23 
  6 98  215 314
*   +   *   +  "

The next step is to break the data up into separate events,

| rex max_match=0 "(?<line>[^\r\n]+)"
| mvexpand line

Interpreting the data

Each line holds a row in a column of numbers followed by a line with the mathematical operation that needs to be applied to all the numbers in the column; this is either plus (+) or multiply (*). Each column is separated by one or more white spaces. So, extract the groups of non-white spaces using the rex command.

| rex max_match=0 field=line "(?<value>\S+)"
| fields - _time _raw line _mkv_child

Solving Part One

Part One is calculating the value of the columns based on the operator found at the bottom of the column. To make it easier, reverse the events to get the operator at the top of the column.

| reverse

Give each event a name

| streamstats count as operand
| eval operand="operand_".operand

Transpose to get a field for the operator and each operand of the equations.

| transpose 0 header_field=operand

Rename the first field as operator since this contains the operators required for each equation.

| rename operand_1 as operator

Rename the second field as total since this contains the running total for each equation.

| rename operand_2 as total

Create a list of indexes for the multi-value fields.

| eval list=mvrange(0, mvcount(operator))

For the remaining operands, process each equation

| foreach operand_*
    [
    | foreach mode=multivalue list 
        [

Update the (running) total for each equation based on the corresponding operator.

        | eval result=if(mvindex(operator, <<ITEM>>)="+", tonumber(mvindex(total, <<ITEM>>)) + tonumber(mvindex(<<FIELD>>, <<ITEM>>)), tonumber(mvindex(total, <<ITEM>>)) * tonumber(mvindex(<<FIELD>>, <<ITEM>>))),
        new_total=mvappend(if(<<ITEM>>=0, null(), mvindex(total, 0, <<ITEM>>-1)), result, mvindex(total, <<ITEM>>+1, -1)),
        total=new_total

Now, simply sum the totals from all the equations.

| stats sum(total) as total

Part Two

The second part of the puzzle requires that again we determine, for your own dataset, the total sum of all the equations,, but, this time the numbers should be considered vertically.

Solving Part Two

Starting in the same manner as Part One, extract each line from the input

| rex max_match=0 "(?<line>[^\r\n]+)"
| mvexpand line

Give each row an id and extract each character from the line.

| streamstats count as row
| rex max_match=0 field=line "(?<char>.)"

Expand the characters into columns and give each column an id.

| mvexpand char
| streamstats count as column by row global=f

Since we need to process the columns in the correct order, adjust the id so that the lexicographical sort order is also the numeric order. (Since there are fewer than 10,000 columns in my dataset, I have simply added 10,000 to the count.)

| eval column=10000+column

Use the chart command to list the characters by column over a constant value (I have used _time from the original makeresults - if you have lost this, re-instate it or set another field to a constant value).

| chart list(char) as char over _time by column limit=0 useother=f

Now, we can convert back to a table with the untable command. Note the use of underscore (_) for the first field name - this effectively hides the constant field that the chart command used.

| untable _ field value

Use our old friend rex to extract the operator and digits from the list of characters in the value field.

| rex field=value "(?<operator>\*|\+)"
| rex max_match=0 field=value "(?<digit>\d)"

Combine the digits into a number for the column.

| eval number=tonumber(mvjoin(digit,""))

Using streamstats to count the (non-null) operators, we can get an id for each equation.

| streamstats count(operator) as equation

Use stats to gather numbers (operands) and operator for each equation.

| stats list(number) as operands values(operator) as operator by equation
| fields - equation

Initialise a running total for each equation depending on the operator being used (multiplying by one or adding zero does not change the result).

| eval total=if(operator="*", 1, 0)

For each of the operands, combine it with the running total using the appropriate operator.

| foreach mode=multivalue operands
    [
    | eval total=if(operator="*", total*<<ITEM>>, total+<<ITEM>>)
    ]

Now, simply sum the totals from all the equations.

| stats sum(total) as total

Summary

There are many ways to solve this puzzle, and, in the end, I settled on slightly different approaches for Part One and Part Two. Part One used nested foreach loops to iterate over the operands updating the multi-value field of totals for each equation, all within a single event. Part Two separated each equation to its own event before iterating over the operands to determine the result for each equation.

Have questions or thoughts? Comment on this article or in Slack #puzzles channel. Whichever you prefer.

Contributors
Get Updates on the Splunk Community!

Build the Future of Agentic AI: Join the Splunk Agentic Ops Hackathon

AI is changing how teams investigate incidents, detect threats, automate workflows, and build intelligent ...

[Puzzles] Solve, Learn, Repeat: Character substitutions with Regular Expressions

This challenge was first posted on Slack #puzzles channelFor BORE at .conf23, we had a puzzle question which ...

Splunk Community Badges!

  Hey everyone! Ready to earn some serious bragging rights in the community? Along with our existing badges ...