Advent of Code 2019: Elixir @callback

It’s Advent of Code season again! I can’t believe it’s been three years since I started participating. It’s such a fun time to remember why I like programming in the first place. I use it to learn new languages, hone skills and patterns, and just have fun.

I’m always obsessed with building tooling, so while the problems are easier I’m having a great time defining a sort of test runner for my advent of code… I think this is my second or third year trying in elixir, so I’m trying some new things.

This year’s (current) model is using @callback to define a sort of API that all Advent problems have to solve:

defmodule Advent do
  @moduledoc """
  This is intended to be an importable module that defines some runners and
  conventions for advent of code problems.

  Each "day" should contain the following methods...

  - `setup/0` should setup a input object.
  - `p1/1` and `p2/2` should take the input and solve the puzzle, returning the
    answer.

  I want to define a test runner such that if you run mix <day number> it runs
  `setup/0` once and runs `p1/1` and/or `p2/1` when appropriate.
  """

  @callback setup() :: {:ok, term}
  @callback p1(Map.t) :: {:ok, term}
  @callback p2(Map.t) :: {:ok, term}
end

If you look at the whole file here, it also defines some timing functions as a mix task, which is fun. There’s a crazy challenge for solving all puzzles under a certain time limit… I think this year I’ll have to settle for just solving all puzzles.

This pattern really became really fun for today’s puzzle (Day 3). As I started into part 2, more of p1 moved up into setup/0 so that p1/1 and p2/1 are just the specific solutions to their part.

For instance:

def p1(%{intersections: intersections}) do
  intersections
  |> Enum.min_by(fn({x, y}) -> abs(x) + abs(y) end)
  |> (fn({x, y}) -> abs(x) + abs(y) end).()
end

and

def p2(%{wires: {wire1, wire2}, intersections: intersections}) do
  intersections
  |> Enum.map(fn(coord) ->
    Enum.find_index(Enum.reverse(wire1), & coord == &1) +
    Enum.find_index(Enum.reverse(wire2), & coord == &1)
  end)
  |> Enum.min
end

You can see my full day 3 solution here. Let me know if you have a better way of managing the problems, this is my favorite so far.


🔖
Changelog
  • 2020-06-18 14:26:02 -0500

    Move everything to CST

    Don't know why I didn't do that before. It caused _no_ end of
    problems.

  • 2019-12-03 12:39:48 -0600

    Post: Using @callback for elixir