Advent of Code 2019 - Day 1

Really good use of doctests!

Thanks! If I had known about Stream.drop/1, my part2_with_stream_iteration function would have looked like @sasajuric 's solution.

My solution is eerily similar to a lot of the posts here:
https://github.com/trbngr/advent_of_code/blob/master/lib/advent_of_code/2019/day1.ex

I did go back and replace the tests with doctests. Should help keep things clear going forward. Great idea!

My solutions for Day 1

I wrote them in a way that I thought was easy for me to reason about. I haven’t seen the answers above yet (I’m going to do that later tonight — I always learn so much new that way :slight_smile: ). Snippets below:

Part 1

defmodule Part1 do
  @doc """
      iex> fuel_for_mass(12)
      2

      iex> fuel_for_mass(14)
      2

      iex> fuel_for_mass(1969)
      654

      iex> fuel_for_mass(100756)
      33583
  """
  def fuel_for_mass(mass) do
    div(mass, 3) - 2
  end

  def total_fuel(input_file) do
    File.stream!(input_file)
    |> Stream.map(&String.trim/1)
    |> Stream.map(&String.to_integer/1)
    |> Stream.map(&fuel_for_mass/1)
    |> Enum.sum()
  end
end

Part 2

defmodule Part2 do
  @doc """
      iex> fuel_for_mass(12)
      2

      iex> fuel_for_mass(14)
      2

      iex> fuel_for_mass(1969)
      654

      iex> fuel_for_mass(100756)
      33583
  """
  def fuel_for_mass(mass) do
    div(mass, 3) - 2
  end

  @doc """
      iex(9)> total_fuel_for_mass(14)
      2

      iex(10)> total_fuel_for_mass(1969)
      966

      iex(11)> total_fuel_for_mass(100756)
      50346
  """
  def total_fuel_for_mass(mass_or_fuel) do
    fuel = fuel_for_mass(mass_or_fuel)

    if fuel > 0 do
      fuel + total_fuel_for_mass(fuel)
    else
      0
    end
  end

  def total_fuel(input_file) do
    File.stream!(input_file)
    |> Stream.map(&String.trim/1)
    |> Stream.map(&String.to_integer/1)
    |> Stream.map(&total_fuel_for_mass/1)
    |> Enum.sum()
  end
end

I’m learning Elixir with doing AOC and got improved a lot. Thanks to this post, I borrowed a lot of brilliant ideas from @sasajuric @dimitarvp and improved my repo. :grin:

Here’s the day1 solution I end up with:

defmodule AOC.Y2019.Day01 do

  @masses "2019/day01.txt"
          |> AOC.Input.stream(&String.to_integer/1)
          |> Enum.to_list()

  def part1 do
    @masses
    |> Enum.reduce(0, &(div(&1, 3) - 2 + &2))
  end

  def part2 do
    @masses
    |> Enum.reduce(0, &(fuel(&1) + &2))
  end

  def fuel(mass) do
    case div(mass, 3) - 2 do
      x when x > 0 ->
        x + fuel(x)

      _ ->
        0
    end
  end
end

Here is mine

defmodule AOC.Day1 do
  def part1(file_stream) do
    file_stream
    |> Enum.reduce(0, fn n, acc -> acc + calculate_fuel(n) end)
  end

  def part2(file_stream) do
    file_stream
    |> Enum.reduce(0, fn n, acc -> acc + additional_fuel(n, 0) end)
  end

  defp calculate_fuel(n), do: div(n, 3) - 2

  defp additional_fuel(fuel, additional) when fuel < 6, do: additional

  defp additional_fuel(fuel, additional) do
    new_fuel = calculate_fuel(fuel)
    additional_fuel(new_fuel, additional + new_fuel)
  end
end

And here is test. I create file stream in the test

defmodule Day1Test do
  use ExUnit.Case

  alias AOC.Day1

  test "solution day 1 part 1" do
    input = read_input("test/input/day1_input")
    assert Day1.part1(input) == 3_152_919
  end

  test "solution day 1 part 2" do
    input = read_input("test/input/day1_input")
    assert Day1.part2(input) == 4_726_527
  end

  defp read_input(file) do
    file
    |> File.stream!([], :line)
    |> Stream.map(fn line ->
      {integer, _leftover} = Integer.parse(line)
      integer
    end)
  end
end

Repo: https://github.com/gsarwate/advent_of_code_2019/blob/master/lib/aoc/day1.ex

and incorporating great suggestions in this thread, my ā€œfinalā€ day 1 looks like:

defmodule AOC.Day1 do

  def part1 do
    AOC.integer_stream(1)
    |> Enum.reduce(0, fn mass, acc -> fuel(mass) + acc end)
  end

  def part2 do
    AOC.integer_stream(1)
    |> Stream.map(&fuel/1)
    |> Enum.reduce(0, fn mass, acc -> add_fuel(mass) + acc end)
  end

  def fuel(mass) do
    max(0, trunc(mass / 3) - 2)
  end

  def add_fuel(mass) do
    case fuel(mass) do
      0 -> 0
      f -> mass + add_fuel(f)
    end
  end

end

I’ll be keeping my progress in GitHub - sb8244/advent-of-code-2019 Ā· GitHub

Day One

Here’s mine:

That’s a great idea. Does the site let you start late or do you have to keep up every day?

Are you going to do some videos like last year? :smiley:

Yep, you can solve the tasks whenever you’d like, esp. if you don’t care about your place in the leaderboard :slight_smile:

Here’s mine solution: https://github.com/pozhega/AoC/blob/master/lib/aoc/Y2019/d1.ex

I decided to record my Erlang solutions this year, to show how much I can fail in trying to find a decently effective solution and how I come up with code.

Here’s Day 1: