Advent of Code 2021 - Day 1

Tail-recursive solution of Part 2:

defmodule AoC2021_Part2 do
  @spec count_increasing([number]) :: non_neg_integer
  def count_increasing(input) do
    do_count_increasing(input, 0)
  end

  defp do_count_increasing([a | [_, _, d | _] = tail], acc) do
    do_count_increasing(tail, acc + (if a < d, do: 1, else: 0))
  end

  defp do_count_increasing(_, acc), do: acc
end
2 Likes

Day 1: two solutions for part one, part two + tests. Would love to hear some critic/thoughts/suggestions.

defmodule AOC_2021.Day1 do
  @input "input.txt"
  def part_one_1 do
    [_, result] =
      @input
      |> list_of_numbers()
      |> Enum.reduce([0, 0], fn item, [last_item, result] ->
        cond do
          last_item == 0 ->
            [item, result]

          last_item < item ->
            [item, result + 1]

          true ->
            [item, result]
        end
      end)

    result
  end

  def part_one_2 do
    result =
      @input
      |> list_of_numbers()
      |> Enum.to_list()
      |> do_job(0)

    result
  end

  defp do_job([first | [second | _] = tail], result) when first < second,
    do: do_job(tail, result + 1)

  defp do_job([_ | [_ | _] = tail], result), do: do_job(tail, result)

  defp do_job([_ | _], result), do: result

  def part_two do
    [_item, _, result] =
      @input
      |> list_of_numbers()
      |> Enum.chunk_every(3, 1, :discard)
      |> Enum.reduce([0, 0, 0], fn [first, second, third], [last_item, window_sum, result] ->
        cond do
          last_item == 0 ->
            [first + second + third, second + third, result]

          last_item < window_sum + third ->
            [window_sum + third, second + third, result + 1]

          true ->
            [window_sum + third, second + third, result]
        end
      end)

    result
  end

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

case System.argv() do
  ["--test"] ->
    ExUnit.start()

    defmodule AOC_2021.Day1Test do
      use ExUnit.Case

      @expected_part_one 1121
      @expected_part_two 1065

      test "tests" do
        response = fn _, _, _ ->
          [
            AOC_2021.Day1.part_one_1(),
            AOC_2021.Day1.part_one_2(),
            AOC_2021.Day1.part_two()
          ]
        end

        assert [@expected_part_one, @expected_part_one, @expected_part_two] ==
                 response.(1121, 1121, 1065)
      end
    end

  ["--run"] ->
    AOC_2021.Day1.part_one_1()
    |> IO.inspect(label: "\npart_one_1 \n")

    AOC_2021.Day1.part_one_2()
    |> IO.inspect(label: "\npart_one_2 \n")

    AOC_2021.Day1.part_two()
    |> IO.inspect(label: "\npart_two \n")

  _ ->
    IO.puts(:stderr, "\nplease specify --run or --test flag")
end

While looking at your solution, I was thinking the same that why didnā€™t you use either a < b or b > a. I was about to ask you. :grinning:

Elixir newbie here! Just wanted a reason to start using Livebook.

report = ("inputs/day01.txt")
|> File.read!
|> String.split(~r/\n/, trim: true)
|> Enum.map(&String.to_integer/1)

defmodule Sonar do
  def depthReport([]), do: []  
  def depthReport([_head | []]), do: []
  def depthReport([head | tail]) do 
    #IO.puts("head #{inspect(head)} \t tail #{inspect(tail)}")
    
    [(if (head < hd(tail)), do: :up, else: :down) | depthReport(tail)]
  end

  def is_up(a) when is_atom(a), do: a == :up
end
# part 1 
report
|> Sonar.depthReport
|> Enum.filter(&Sonar.is_up/1)
|> length

# part 2
report
|> Enum.chunk_every(3, 1, :discard)
|> Enum.map(&Enum.sum/1)
|> Sonar.depthReport
|> Enum.filter(&Sonar.is_up/1)
|> length


Found out about chunk_every after part1 :man_facepalming:

2 Likes

I am waiting to see how Jose solves things with Livebook tomorrow. I missed the first part of the broadcast today but I did see Lit HTML and I was likeā€¦ how do I rewind this video! Really looking forward.

I created another mix project called aoc2021 and rather than create a new mix project for every day I will put everything in this one project.
My solution of the Day 1 - Github

defmodule Day01 do
  @sample ~w/199
             200
             208
             210
             200
             207
             240
             269
             260
             263/
             |> Enum.map(&String.to_integer/1)

  @report_list "assets/day01.txt"
               |> File.read!()
               |> String.split(~r/\n/, trim: true)
               |> Enum.map(&String.to_integer/1)

  def list do
    @report_list
  end

  defp larger_than_previous([]), do: []

  defp larger_than_previous([h1 | [h2 | _] = t]) do
    [h2 > h1 | larger_than_previous(t)]
  end

  defp larger_than_previous([_ | t]), do: larger_than_previous(t)

  def count do
    ltp_count(list())
  end

  def count_sample do
    ltp_count(@sample)
  end

  # Puzzle 2

  defp sum3([]), do: []

  defp sum3([h1 | [h2 | [h3 | _]] = t]) do
    [h1 + h2 + h3 | sum3(t)]
  end

  defp sum3([_ | t]), do: sum3(t)

  def count2_sample do
    sum3 = sum3(@sample)
    ltp_count(sum3)
  end

  def count2 do
    sum3 = sum3(@report_list)
    ltp_count(sum3)
  end

  defp ltp_count(list) do
    list_ltp = larger_than_previous(list)
    Enum.count(list_ltp, fn x -> x == true end)
  end
end

# Results of the Sample
IO.puts("The result of the 1st sample is: #{Day01.count_sample()}")
IO.puts("The result of the 2nd sample is: #{Day01.count2_sample()}")

# Results of the Puzzle
IO.puts("The result of the 1st puzzle is: #{Day01.count()}")
IO.puts("The result of the 2nd puzzle is: #{Day01.count2()}")

run iex -S mix:

iex(1)> r Day01
The result of the 1st sample is: 7
The result of the 2nd sample is: 5
The result of the 1st puzzle is: 1462
The result of the 2nd puzzle is: 1497
{:reloaded, Day01, [Day01]}

@seanmor5 posted a Nx solution on twitter - which is very interesting and different from Enum/Streamā€¦

It is actually the same as Enum/Stream solution, just doing everything on structure that conceptually is parallel.

1 Like

My solutions for day 1 can be found here: aoc/day-01.livemd at main Ā· josevalim/aoc Ā· GitHub

I used Livebook and I solved using different styles: enum, nx, and recursion. You can find the streaming here: Twitch

7 Likes

When people say theyā€™re using Livebook for these exercises, does that have any practical impact on the code? Is Livebook just a cloud-based dev environment like cloud9 or something?

Livebooks are like Jupyter notebooks streamlined for Elixir, it works both locally and remotely. Not sure if you are familiar with the latter, itā€™s quite big in data science communities.

https://livebook.dev

I think I understand the concept, Iā€™m just puzzled over using it in this context. It seems like something you would use more to collaborate with a team than for this kind of solo exercise.

Love your live stream (though I was sleeping at that time). You really complicated that simple problem to a whole new level :rofl:

My solution here: elixir-advent-of-code-2021/part1.exs at main Ā· perrycate/elixir-advent-of-code-2021 Ā· GitHub

Iā€™m doing AoC to (re)learn Elixir and am very rusty right now, I ended up implementing Enum.chunk_by myself, effectively, because I didnā€™t realize it already existed. :man_facepalming:

Any feedback would be much appreciated!

Check out my streaming if you want, where I use Livebook to develop the solution: Twitch The best part about Livebook is getting immediate feedback on the code and from code snippets.

4 Likes

I summarized your day 1 stream just in case some donā€™t have the time to watch the entire thing (Iā€™ll be summarizing each day!). I hope itā€™s alright with you; if itā€™s not then Iā€™ll take it down.

Day 1 summary: How Jose Valim Solved Advent of Code (Day 1) with Elixir and LiveBook - YouTube

cc @stevensonmt

1 Like

Definitely alright with me. :slight_smile:

2 Likes

Btw, if you want to send a PR here with a link to the summary, then it would be very appreciated too: aoc/day-01.livemd at main Ā· josevalim/aoc Ā· GitHub

1 Like

Sure thing! I made one just now. Thanks. :smiley:

part 1:

part 2: