bjorng

bjorng

Erlang Core Team

Advent of Code 2024 - Day 2

Here is my solution for day 2 of Advent of Code:

https://github.com/bjorng/advent-of-code/blob/main/2024/day02/lib/day02.ex

Most Liked

dimitarvp

dimitarvp

Life sadly kept getting in the way but ultimately:

defmodule Day02 do
  @moduledoc ~S"""
  A solution to https://adventofcode.com/2024/day/2.
  """

  @type level :: pos_integer()
  @type distance :: integer()
  @type report :: [level()]

  @spec all_variants_with_one_element_removed(report()) :: [report()]
  def all_variants_with_one_element_removed(list) do
    for i <- 0..(length(list) - 1), do: list |> List.delete_at(i)
  end

  @spec sign(integer()) :: :zero | :minus | :plus
  def sign(0), do: :zero
  def sign(i) when i > 0, do: :plus
  def sign(i) when i < 0, do: :minus

  @spec distances(report()) :: [distance()]
  def distances([first | rest]) do
    rest
    |> Enum.reduce({first, []}, fn current_level, {previous_level, distances} ->
      {current_level, [current_level - previous_level | distances]}
    end)
    |> then(fn {_last_level, distances} -> Enum.reverse(distances) end)
  end

  @spec same_signs?([distance()]) :: boolean()
  def same_signs?(list) do
    list
    |> Enum.map(&sign/1)
    |> Enum.uniq()
    |> length()
    |> Kernel.==(1)
  end

  @spec safe?(report()) :: boolean()
  def safe?(report) do
    distances = distances(report)
    monotonical? = same_signs?(distances)

    safely_advancing? =
      distances |> Enum.map(&abs/1) |> Enum.all?(fn distance -> distance <= 3 end)

    monotonical? and safely_advancing?
  end

  @spec safe_with_a_dampener?(report()) :: boolean()
  def safe_with_a_dampener?(report) do
    safe?(report) or
      report |> all_variants_with_one_element_removed() |> Enum.any?(&safe?/1)
  end

  @doc ~S"""
  iex> Day02.part_1("7 6 4 2 1\n1 2 7 8 9\n9 7 6 2 1\n1 3 2 4 5\n8 6 4 4 1\n1 3 6 7 9\n")
  nil
  """
  @spec part_1(String.t()) :: non_neg_integer()
  def part_1(input \\ Aoc.input(2)) do
    input
    |> Aoc.parse_lines_of_integers()
    |> Enum.count(&safe?/1)
  end

  @spec part_2(String.t()) :: non_neg_integer()
  def part_2(input \\ Aoc.input(2)) do
    input
    |> Aoc.parse_lines_of_integers()
    |> Enum.count(&safe_with_a_dampener?/1)
  end
end

Did my best to make it readable and intuitive, even benchmarked three competing implementation I had ideas about, and only posted the one that won.

lud

lud

No optimization here, just building all possible list before trying them one by one :smiley:

defmodule AdventOfCode.Solutions.Y24.Day02 do
  alias AoC.Input

  def parse(input, _part) do
    Enum.map(Input.stream!(input, trim: true), &parse_line/1)
  end

  defp parse_line(line) do
    Enum.map(String.split(line, " "), &String.to_integer/1)
  end

  def part_one(problem) do
    problem
    |> Enum.filter(&safe?/1)
    |> length()
  end

  defp safe?([a, b | _] = list) when a < b, do: safe?(:asc, list)
  defp safe?([a, b | _] = list) when a > b, do: safe?(:desc, list)
  defp safe?([a, a | _]), do: false

  defp safe?(:asc, [a, b | rest]) when abs(a - b) in 1..3 and a < b, do: safe?(:asc, [b | rest])
  defp safe?(:desc, [a, b | rest]) when abs(a - b) in 1..3 and a > b, do: safe?(:desc, [b | rest])
  defp safe?(_, [_last]), do: true
  defp safe?(_, _), do: false

  def part_two(problem) do
    problem
    |> Enum.filter(&safeish?/1)
    |> length()
  end

  defp safeish?(list) do
    candidates = [list | Enum.map(0..(length(list) - 1), &List.delete_at(list, &1))]
    Enum.any?(candidates, &safe?/1)
  end
end

Edit:

candidates = Stream.concat([list], Stream.map(0..(length(list) - 1), &List.delete_at(list, &1)))

This would save memory but given the input size it’s actually slower.

Where Next?

Popular in Challenges Top

igorb
So… that’s it? Everyone is stuck on part 2? :slight_smile: I looked at Reddit hints and thought I probably wouldn’t have come up with the...
New
bjorng
This topic is about Day 10 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums ): https://adv...
New
bjorng
Here is my solution: https://github.com/bjorng/advent-of-code-2021/blob/77bdef7a51b428b58ffb4ab76f540901e636f841/day12/lib/day12.ex
New
sukhmeetsd
All in all, from what I understand, it is better not to use GenServer.cast when we want some concurrent operations to happen for sure, be...
New
liamcmitchell
A frustrating one for me. I spent a long time trying to understand why some combinations resulted in fewer presses and struggled to keep ...
New
gangstead
This is my second year doing AoC in Elixir and my first year doing it with Livebook. When I was doing just plain Elixir I usually set up...
New
seeplusplus
Hello all, hopefully I post this before someone else does and I don’t dupe. IMO Day 4 was much easier than Day 3 (yay, I can sleep befor...
New
stevensonmt
Anyone else think the prompt for this challenge is contradictory? The rules for comparing packets include If both values are lists, c...
New
rvnash
Anyone have a solution to Part 2 today? Part 1 was straight forward, but I can’t figure out a programatic way to do part 2. I understand ...
New
christhekeele
Thought I’d kick today’s thread off! Parsing Enum rocks, so most of my code was actually in parsing input. ▶ Preprocessing input Part 1...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New

We're in Beta

About us Mission Statement