2025 Advent of Code

It’s just over a week until Advent of Code 2025 starts, here is my usual post about my approach for this year.

This time, I’m going back to a past tactic of trying to learn a new language at the same time as solving the problems, I don’t really have the time to do this but I didn’t want to go another year where I use a language I already know. This years choice is ๐Ÿฅ๐Ÿฅ๐Ÿฅ Ruby.

I was surprised to learn that Ruby has been around since 1995, the first time I really heard about it was when I learnt about Ruby on Rails and that was around 2006 so even then it was a well established language. Given the language is ~30 years old, it should have an established set of tools available that would typically be used in an engineering context. In 2025, Ruby appears at number 18 in the list of programming languages in Stack Overflow’s Developer Survey, I’m surprised it isn’t a little higher but it seems to be a favourite for web development and hasn’t been picked up in the AI trend. Even though it’s not a top 10 language, some popular open source software is written in Ruby including:

So, lets take a dive into what I’ve set up for this years challenges.

Installing and bootstrapping Ruby

Ruby, like python, comes preinstalled on Macs and so to prevent issues that can be caused by updating or using this it’s best to install a venv type version manager. For ruby this is rbenv, which manages the global versions of Ruby available on a given machine.

1โ•ฐโ”€โฏ brew install rbenv

Once rbenv is installed, we need to run init which leads to updates in ~/.zprofile

1โ•ฐโ”€โฏ rbenv init

The latest version of Ruby is currently 3.4.7; we need to make this available as a Ruby version in rbenv.

1โ•ฐโ”€โฏ rbenv install 3.4.7

rbenv now knows about v3.4.7 but we need to set this as the version to use for the AoC project else the system version will be used. In the AoC project folder we need to run:

1โ•ฐโ”€โฏ rbenv local 3.4.7

This creates a .ruby-version file locally which contains the ruby version the projet uses, I guess the clue is in the name. The final step of Ruby setup is to bootstrap the project so that we can add the dependencies.

1โ•ฐโ”€โฏ bundle init

This creates a Gemfile for me to add Ruby Gems to, I’ll cover the Gems I’m going to use in the next section.

Setting up the project

Now that I have ruby installed and ready to go, I want to add some source files and things that can be used

first thing is the folder structure. GitHub gives a couple of files but I need folders and ruby files to test the structure with.

The structure I’ll be using is:

 12025-advent-of-code/
 2โ”‚
 3โ”œโ”€โ”€ .git/
 4โ”œโ”€โ”€ .github/                      # Contains GitHub build workflows
 5โ”œโ”€โ”€ .vscode/                      # Contains debug and run configs
 6โ”œโ”€โ”€ config/
 7โ”‚   โ””โ”€โ”€ pre_commit.yml.           # Configuration of pre-commit checks to perform
 8โ”œโ”€โ”€ lib/                          # Daily solution code
 9โ”‚   โ””โ”€โ”€ day_01/
10โ”‚       โ”œโ”€โ”€ task_01.rb
11โ”‚       โ””โ”€โ”€ task_02.rb
12โ”‚
13โ”œโ”€โ”€ test/                         # Unit tests
14โ”‚   โ””โ”€โ”€ day_01/
15โ”‚       โ”œโ”€โ”€ example_input_01.txt  # Example input from the AOC challenge
16โ”‚       โ”œโ”€โ”€ input.txt             # Real input
17โ”‚       โ”œโ”€โ”€ test_task_01.rb
18โ”‚       โ””โ”€โ”€ test_task_02.rb
19โ”œโ”€โ”€ .gitignore                    # Files I don't want to check into Git
20โ”œโ”€โ”€ .rubocop.yml                  # RuboCop linter/formatter rules
21โ”œโ”€โ”€ .ruby-version                 # The version of Ruby that the project uses
22โ”œโ”€โ”€ Gemfile                       # Lists of gems
23โ”œโ”€โ”€ Gemfile.lock                  # Auto-generated by Bundler
24โ”œโ”€โ”€ Rakefile                      # The build tasks that are available to the project
25โ””โ”€โ”€ setup_day.sh                  # Shell script for rapidly setting up the repo for the next day's task

And as with all previous years I will not be checking the real input (input.txt each day) into GitHub, this respects the wishes of the event organisers.

Gemfile

I need to define the libraries I’ll use in my Gemfile and I can add these simply but one thing I’ve used with other AoC solutions is a separation between the development, test, build and runtime libraries so I will try to follow a similar convention here. As I understand it, with Ruby I can define groups of gems, all builds will include all groups unless I explicitly exclude them. For example, I could have a group called :runtime and a group called :development; on local build I wouldn’t exclude anything, but on the deployable build I would exclude :development. As I want to work “prod-like” I’ll try to group libraries and then exclude some when on the build server.

 1source "https://rubygems.org"
 2
 3group :development do
 4  ...
 5end
 6
 7group :test do
 8  ...
 9end
10
11group :runtime do
12  ...
13end

After adding each dependency I run

1โ•ฐโ”€โฏ bundle install

I think that this installs the library executables to a centrl Ruby repo on my machine and then links the version in the lock file to the project. I can’t see any evidence of the dependencies existing within my project which is why I think it works this way.

Make file

In this case not make but rake, which is very similar but for Ruby. I’ll add this as a :runtime dependency as it should always be in place for all the tasks that follow in this post.

Make files contain all the useful commands for the project, in the case of mine I will include a test target to trigger the unit test framework and also a lint target so that I can manually trigger a run of the linter. I may never use the lint target but adding it gives me a bit more practice.

Unit test framework

It looks like the standard framework to use for unit testing is minitest so this is the framework I’ll adopt.

I’ll add the test framwork as a :test library, it will be needed locally and on the build server but not deployed to production as tests wouldn’t be deployed to production.

Linter

The linter I’ve chosen is rubocop, this does almost everything I need and seems to be a Ruby standard. I’ll add this to the :development group. I also want rubocop to lint my test files to make sure they match the expected test format, so for this I will use a rubocop extension rubocop-minitest. The rubocop-minitest extension is also added to the :development group.

Once rubocop is installed it needs to be configured, this is done in the .rubocop.yml file in the root directory. I will try to leave as many rules on as possible, the ones that I want to configure are:

  • SuggestExtensions, I’ll disbale this otherwise every time I lint I will get a list of “helpful” suggested extra extensions
  • Naming/VariableNumber, I’ll set the EnforcedStyle to snake_case, this means that variables with numbers in them can have an underscore between the name and the number, for example variable_01 instead of variable01
  • Style/Documentation, I’ll disbale this as I don’t want to always add docs.
  • Metrics/MethodLength, I’ll disable this instead of configuring a greater Max value as I might have functions that end up being longer, I might refactor in future but will want to check in a version so that it’s recorded.

A final fun thing I’ll configure with Rubocop is setting the formatter to be the pacman formatter.

Pre-commit package

My goto pre-commit hook utility is usually pre-commit, created by the brilliant Anthony Sottile who has himself completed Advent of Code challenges in the past, 2021 being the most recent year.

For this year I will use a package called pre-commit but this is one written in Ruby. I’ve found it a bit clunky to get going with it, but I’ll stick with it for now and if it causes me issues there will be a blog post in how to move from one package to another.

GitHub actions

As I’m using rake, my actions file only really does two things, firstly intsll ruby and secondly run the test task which is the default task in the rake file. If the tests fail, the build will fail and I’ll be notified, this is a great simulation of a prod environment where tests might “work on my machine” - the build server becomes the source of truth.

Bootstrap script

Each year I’ve found it very beneficial to have a script I can run that will create a folder with files for the next set of challenges. I’ve copied the one I used last year and updated it to match the 2025 project I’ve created.

Library files

When I do AoC there are a few library functions that I like to have ready as they are useful for problems that have come up in the past. This year will be no exception, Ruby is completely new to me so working on these library functions will help to familiarise myself with the language before the first challenge is released. The utils library will contain:

  • Point - a class to wrap x,y con-ordinates. I create this for all those 2D space problems. Key functions are:
    • get_manhattan_distance - this returns the distance from a Point to another point
    • in_bounds? - tests whether a point sits within a given bounding box

Debugger

The final thing I get setup is a debugger for when I inevitably have a solution that works for the example but fails for the real input. Whilst print statements work, the output can become hard to follow so I like to have a debugger handy. To debug I will install the debase library in my project as a :development dependency and install the Ruby LSP plugin into Visual Code.

That’s it, my 2025 AoC repo all set up and ready to go.