Advent of Code 2025

last updated: Dec 03, 2025

This year I've decided to do Advent of Code in Gleam, a programming language I know nothing about and have never written a line of code in.

Previously: Advent of Code 2024, 2023 problem log, and incomplete answers back to 2015 in github

Day 1

Not the easiest day 1! The problem centered around a ring of the numbers 0-99.

Day 2

It seems that the creator of gleam doesn't like tuples, so today I tried not to use any. I started with a custom type to represent a range:

pub type Range {
  Range(start: Int, end: Int)
}

For part 1, I used a generative approach where I iterated from the start of each range through the end, and checked if the number was of the form <x><x>. The heart of it is this function:

pub fn acc_dubs(front: Int, range: Range, acc: List(Int)) {
  let dub =
    string.append(int.to_string(front), int.to_string(front))
    |> int.parse
    |> result.unwrap(0)
  case dub >= range.start, dub <= range.end {
    True, True -> acc_dubs(front + 1, range, [dub, ..acc])
    True, False -> acc
    False, True -> acc_dubs(front + 1, range, acc)
    False, False -> panic as "impossible"
  }
}

For part 2, I got in loops thinking about how to generate all possible combinations of numbers, so I instead took a filtering approach. This function checks if a number is symmetric in the way the problem states (assuming that there are no numbers with more than 10 digits):

pub fn is_sym(n) {
  let ns = n |> int.to_string
  let l = ns |> string.length
  list.any(
    [
      atoi(string.repeat(string.slice(ns, 0, 1), int.max(l, 2))),
      atoi(string.repeat(string.slice(ns, 0, 2), int.max(l / 2, 2))),
      atoi(string.repeat(string.slice(ns, 0, 3), int.max(l / 3, 2))),
      atoi(string.repeat(string.slice(ns, 0, 4), int.max(l / 4, 2))),
      atoi(string.repeat(string.slice(ns, 0, 5), int.max(l / 5, 2))),
    ],
    fn(x) { x == n && n > 9 },
  )
}

I find that pleasing!

Then I iterated over every number in each range and checked it for symmetry, and summed the result:

pub fn part_b(input) {
  let ranges = parse(input)
  list.fold(
    list.flat_map(ranges, fn(r) { acc_nubs(r.start, r.end, []) }),
    0,
    int.add,
  )
}

pub fn acc_nubs(start, end, acc) {
  case start > end, is_sym(start) {
    True, _ -> acc
    _, True -> acc_nubs(start + 1, end, [start, ..acc])
    _, False -> acc_nubs(start + 1, end, acc)
  }
}

I didn't do any better at handling errors today! I think I will make that an emphasis tomorrow.

Custom types were really simple, and I haven't yet hit any big gleam roadblocks. It takes me a minute to think of how to phrase problems recursively, but I like that it kind of forces you to break your code into small, clear functions.

I wish there were better inline testing functionality in gleam, I don't want to put my tests out into a test directory. I'm kind of annoyed generally by how picky it is about the project structure - but I also see how it fits into the zen of the language, which minimizes your options to keep complexity under control.

↑ up