Andrew Fontaine

Mostly code

Advent of Code 2019: Day 1

03 Dec 2019
Code Snippet for Advent of Code 2019: Day 1

Day 1 ended up being a nice, gentle introduction to the year, with a bit of flexing of some fun programmatic skills. The macro from Day 0 came in handy to really keep things small and simplified.

Puzzle 1

Puzzle 1 was pretty straightforward. Given the masses of a number of modules, calculate all the fuel required.

import AdventOfCode

aoc 2019, 1 do
  def p1 do
    input_stream()
    |> Stream.map(&String.to_integer/1)
    |> Stream.map(&simple_fuel/1)
    |> Enum.sum()
  end

  def simple_fuel(x) do
    floor(x / 3) - 2
  end
end

First I take the input, map it to integers, calculate the fuel for each module, and then sum it up! To ensure my calculations are correct, I can make some tests given the sample information.

defmodule Aoc2019.Y2019.D1Test do
  use ExUnit.Case

  alias AdventOfCode.Y2019.D1

  describe "simple_fuel/1" do
    test "it computs the correct mass" do
      assert 2 = D1.simple_fuel(12)
      assert 2 = D1.simple_fuel(14)
      assert 654 = D1.simple_fuel(1969)
      assert 33583 = D1.simple_fuel(100_756)
    end
  end
end

Generally, these tests come with something small and testable to help ensure my code works.

Puzzle 2

Puzzle 2 simply expands on this by complicating the calculation of fuel. Now, the mass of the fuel is part of the equation. This is a great use case for recursion

  def p2 do
    input_stream()
    |> Stream.map(&String.to_integer/1)
    |> Stream.map(&accurate_fuel/1)
    |> Enum.sum()
  end

  def accurate_fuel(x) when floor(x / 3) - 2 <= 0, do: 0

  def accurate_fuel(x) do
    fuel = floor(x / 3) - 2

    fuel + accurate_fuel(fuel)
  end

Again, testing the calculations quickly before hitting the GO button.

  describe "accurate_fuel/1" do
    test "it computes the correct mass " do
      assert 2 = D1.accurate_fuel(14)
      assert 966 = D1.accurate_fuel(1969)
      assert 50346 = D1.accurate_fuel(100_756)
    end
  end

Final Version

2 stars in the bank, nice and quick! I can clean up the code a bit, the final version are below.

lib/Y2019/1.ex:

import AdventOfCode

aoc 2019, 1 do
  def p1, do: p(&simple_fuel/1)

  def p2, do: p(&accurate_fuel/1)

  def p(f) do
    input_stream()
    |> Stream.map(&String.to_integer/1)
    |> Stream.map(f)
    |> Enum.sum()
  end

  def simple_fuel(x) do
    floor(x / 3) - 2
  end

  def accurate_fuel(x) do
    fuel = floor(x / 3) - 2

    case fuel do
      f when f <= 0 -> 0
      f -> f + accurate_fuel(f)
    end
  end
end

test/Y2019/D1Test.exs:

defmodule Aoc2019.Y2019.D1Test do
  use ExUnit.Case

  alias AdventOfCode.Y2019.D1

  describe "simple_fuel/1" do
    test "it computs the correct mass" do
      assert 2 = D1.simple_fuel(12)
      assert 2 = D1.simple_fuel(14)
      assert 654 = D1.simple_fuel(1969)
      assert 33583 = D1.simple_fuel(100_756)
    end
  end

  describe "accurate_fuel/1" do
    test "it computes the correct mass " do
      assert 2 = D1.accurate_fuel(14)
      assert 966 = D1.accurate_fuel(1969)
      assert 50346 = D1.accurate_fuel(100_756)
    end
  end
end

I decided to drop the pattern matching to only calculate fuel once, but it’s still readable.

I ❤ feedback. Let me know what you think of this article on Twitter @afontaine_ca