Advent of Code 2025 in Charly

Day 6

This article is part of my series on implementing each Advent of Code 2025 challenge in my own programming language Charly.

Part 1

The first part of today’s puzzle asked us to solve the math homework for the friendly cephalopods I encountered in the garbage disposal unit. Their homework came in a file that looked like this:

123 328  51 64
 45 64  387 23
  6 98  215 314
*   +   *   +

Each column contained a list of input numbers and an operator to apply to those numbers. The above worksheet contains four such problems:

To verify your work, you had to provide the sum of all the individual problems’ solutions.

My solution to part 1 of the puzzle:

const input_path = ARGV[1]
const lines = readfile(input_path)
    .lines()
    .map(->(line, i, lines) {
        let line = line
            .split(" ")
            .filterEmpty()

        if i < lines.length - 1 {
            line = line.map(->(l) l.to_number())
        }

        line
    })
const (...data, operators) = lines

const problems = operators
    .map(->(op, index) {
        const operands = data.map(->(row) row[index])
        (op, operands)
    })

const results = problems.map(->(problem) {
    const (operand, inputs) = problem
    const result = operand == "+" ? inputs.sum() : inputs.product()
    result
})

const totalSum = results.sum()

print("totalSum = {totalSum}")

Part 2

The second part of today’s puzzle was a bit more complicated. As a quick reminder, here’s the example worksheet again:

123 328  51 64
 45 64  387 23
  6 98  215 314
*   +   *   +

The cephalopods have a different way of writing down their math problems than we humans do. I’m not going to attempt to explain this myself, so instead I’ll just reference the problem description:

Cephalopod Math

Cephalopod math is written right-to-left in columns. Each number is given in its own column, with the most significant digit at the top and the least significant digit at the bottom. (Problems are still separated with a column consisting only of spaces, and the symbol at the bottom of the problem is still the operator to use.)

This changed the problem significantly. The worksheet now consisted of these problems:

I now had to consume all input lines right-to-left and construct the input numbers myself. Additionally, in the example worksheet provided, all the data columns had a width of exactly 3. This wasn’t the case in the actual worksheet I had to process. Each column could be anywhere from 2 to 4 characters wide.

I solved this by repeatedly popping off characters from the end of all input lines. If all characters were whitespace, that meant I had reached the end of a problem and could move on to the next one.

The rest of the puzzle remained unchanged: solve each problem by performing either a + or * operation, and then sum up the results to get the final solution.

My solution to part 2 of the puzzle:

const input_path = ARGV[1]

let lines = readfile(input_path).lines()
const maxLineLength = lines.map(->(l) l.length).findMax()
lines = lines.map(->(line) {
    while line.length < maxLineLength {
        line = "{line} "
    }
    line
})

const (...inputLines, operandLine) = lines
const (inputs, operands) = (
    inputLines.map(->(line) line.chars()),
    operandLine.split(" ").filterEmpty().reverse()
)

let readerOffset = 0
const problems = operands
    .map(->(operand) {
        let inputData = []

        loop {
            if inputs.first().empty() {
                break
            }

            const charsRead = inputs.map(->(chars) {
                chars.pop()
            })

            if charsRead.all(->(c) c == " ") {
                break
            }

            const number = charsRead
                .filter(->(c) c != " ")
                .join("")
                .to_number()

            inputData.push(number)
        }

        (operand, inputData)
    })

const solutions = problems.map(->(problem) {
    const (operand, inputs) = problem
    const result = operand == "+" ? inputs.sum() : inputs.product()
    result
})

const totalSum = solutions.sum()

print("totalSum = {totalSum}")

Changes to the stdlib / VM

Standard library methods I added:

Methods I had to modify:

You can find the individual commits below:

Late-night addendum

I was really inspired by the solution that Lukas Lebovitz came up with, so I decided to port it to Charly. Since his code relies heavily on Kotlin standard library functions, I first had to implement those in the Charly standard library as well. My final version looks like this:

const input_path = ARGV[1]

// ensure all lines have the same length, as the IDE will remove trailing spaces
const lines = readfile(input_path).lines().apply(->(lines) {
    const maxLength = lines.findMaxBy(->(it) it.length).length
    lines.map(->(line) line.padRight(maxLength, " "))
}).map(->(line) line.chars())

// originally implemented in Kotlin by @lukaslebo
let totalSum = 0
const currentProblemOperands = []
lines.first().indices().reverse().each(->(i) {
    const column = lines.map(->(line) line[i])
    const digits = column.dropLast(1)
    const operand = column.last()

    if (digits.all(->(d) d == " ")) {
        i -= 1
        return
    }

    currentProblemOperands.push(digits.join("").to_number())

    switch operand {
        case "+" totalSum += currentProblemOperands.sum()
        case "*" totalSum += currentProblemOperands.product()
        default return
    }
    currentProblemOperands.clear()
})

print("totalSum = {totalSum}")

I added the following methods to the standard library:

I modified the following method:

You can find the individual commits below:


Copyright © 2024 - 2025 Leonard Schütz | Attributions This post was written by a human and reviewed and proof-read by LLMs