Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Advent of Code 2024

2024/day14.livemd

Advent of Code 2024

Mix.install([
  {:req, "~> 0.5.8"},
  {:kino, "~> 0.14.2"}
])

Day 14

Kino.configure(inspect: [charlists: :as_lists])
input =
  "https://adventofcode.com/2024/day/14/input"
  |> Req.get!(headers: [cookie: "session=#{System.get_env("AOC_COOKIE")}"])
  |> Map.get(:body)
sample =
  """
  p=0,4 v=3,-3
  p=6,3 v=-1,-3
  p=10,3 v=-1,2
  p=2,0 v=2,-1
  p=0,0 v=1,3
  p=3,0 v=-2,-2
  p=7,6 v=-1,-3
  p=3,0 v=-1,-2
  p=9,3 v=2,3
  p=7,3 v=-1,2
  p=2,4 v=2,-3
  p=9,5 v=-3,-3
  """
defmodule Day14 do
  def parse(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(fn line ->
      [px, py, vx, vy] =
        Regex.run(~r/p=(-?\d+),(-?\d+) v=(-?\d+),(-?\d+)/, line, capture: :all_but_first)
        |> Enum.map(&String.to_integer/1)

      [{px, py}, {vx, vy}]
    end)
  end

  def step(robots, n, {w, h}) do
    robots
    |> Enum.map(fn [{px, py}, {vx, vy}] ->
      px = Integer.mod(px + n * vx, w)
      py = Integer.mod(py + n * vy, h)

      {px, py}
    end)
  end

  def step2(robots, {w, h}) do
    robots
    |> Enum.map(fn [{px, py}, {vx, vy}] ->
      [{Integer.mod(px + vx, w), Integer.mod(py + vy, h)}, {vx, vy}]
    end)
  end

  def to_string(robots, {w, h}) do
    grid = robots |> Enum.map(&Enum.at(&1, 0)) |> MapSet.new()

    for(
      y <- 0..(h - 1),
      x <- 0..(w - 1),
      do: if(MapSet.member?(grid, {x, y}), do: "o", else: " ")
    )
    |> Enum.chunk_every(w)
    |> Enum.map(&amp;Enum.join(&amp;1))
    |> Enum.join("\n")
  end
end
import Day14

Part 1

w = 101
h = 103
mid_x = div(w, 2)
mid_y = div(h, 2)

input
|> parse()
|> step(100, {w, h})
|> Enum.reject(fn {px, py} ->
  px == mid_x || py == mid_y
end)
|> Enum.frequencies_by(fn {px, py} ->
  case {px, py} do
    {px, py} when px < mid_x and py < mid_y -> 1
    {px, py} when px > mid_x and py < mid_y -> 2
    {px, py} when px < mid_x and py > mid_y -> 3
    {px, py} when px > mid_x and py > mid_y -> 4
  end
end)
|> Map.values()
|> Enum.product()

Part 2

robots =
  input
  |> parse()

{i, robots, out} =
  Stream.iterate(1, &amp;(&amp;1 + 1))
  |> Enum.reduce_while(robots, fn i, robots ->
    robots = step2(robots, {w, h})
    out = to_string(robots, {w, h})

    if String.contains?(out, "oooooooooo") do
      {:halt, {i, robots, out}}
    else
      {:cont, robots}
    end
  end)

[
  "Iteration: #{i}",
  "```",
  out,
  "```"
]
|> Enum.join("\n")
|> Kino.Markdown.new()