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

Day 14

livebook/14.livemd

Day 14

Part 1

# {input, size} = {File.read!("14/example.txt"), {11, 7}}
{input, size} = {File.read!("14/input.txt"), {101, 103}}
defmodule U do
  def parse(input) do
    String.split(input, "\n", trim: true) |> Enum.map(fn l ->
      [_, x, y, vx, vy] = Regex.run(~r/p=(\d+),(\d+) v=(-?\d+),(-?\d+)/, l)
      [x, y, vx, vy] = Enum.map([x, y, vx, vy], &String.to_integer/1)
      {{x, y}, {vx, vy}}
    end)
  end
end
robots = U.parse(input)
defmodule P1 do
  def step({{x, y}, {vx, vy} = v}, {w, h}) do
    x = (x + vx) |> handle_edge(w)
    y = (y + vy) |> handle_edge(h)
    {{x, y}, v}
  end

  def handle_edge(coord, max) do
    cond do
      coord < 0 -> max + coord
      coord >= max -> coord - max
      true -> coord
    end
  end

  def steps(robot, _, 0), do: robot
  def steps(robot, s, i), do: step(robot, s) |> steps(s, i - 1)


  def wait(robots, size, seconds) do
    Enum.map(robots, fn r -> steps(r, size, seconds) end)
  end

  def to_groups(robots, {w, h}) do
    mx = w / 2 |> trunc
    my = h / 2 |> trunc
    Enum.reduce(robots, %{}, fn 
      {{x, y}, _}, acc when x == mx or y == my -> acc
      {{x, y}, _}, acc ->
      k = {x > mx, y > my}
        Map.update(acc, k, 1, fn v -> v + 1 end)
    end)
  end

  def count(groups) do
    Map.values(groups) |> Enum.reduce(&amp;*/2)
  end
end
P1.wait(robots, size, 100) |> P1.to_groups(size) |> P1.count

Part 2

defmodule P2 do
  def step(robots, size) do
    Enum.map(robots, fn r -> P1.step(r, size) end)
  end

  def find(robots, size, step) do
    IO.inspect(step)
    if is_tree(robots, size) do
      step
    else
      step(robots, size) |> find(size, step + 1)
    end
  end

  def is_tree(robots, {w, h}) do
    m = MapSet.new(robots, fn {pos, _} -> pos end)
    Enum.any?(0..(h - 1), fn y ->
      detect_10_row(y, m, w)
    end)
  end
  
  def detect_10_row(y, m, w) do
    detect_10_row(0, y, 0, m, w)
  end
  def detect_10_row(_, _, 10, _, _), do: true
  def detect_10_row(x, _, _, _, x), do: false
  def detect_10_row(x, y, n, m, w) do
    detected = MapSet.member?(m, {x, y})
    n = if detected do
      n + 1
    else
      0
    end
    detect_10_row(x + 1, y, n, m, w)
  end
end
P2.find(robots, size, 0)