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

Day 14: Restroom Redoubt

day14.livemd

Day 14: Restroom Redoubt

Mix.install([:kino])

Section

input = Kino.Input.textarea("input")
input = Kino.Input.read(input)

Part 1 & 2

defmodule RestroomRedoubt do
  def solve_part1(input) do
    max_x = 100
    max_y = 102

    parse_input(input)
    |> tick(0, {max_x, max_y}, 100, false)
    |> sum_quadrants({max_x, max_y})
  end

  def solve_part2(input) do
    max_x = 100
    max_y = 102

    parse_input(input)
    |> tick(0, {max_x, max_y}, 10000, true)
  end

  defp draw_position_point({x, y}, robots_data) do
    if Enum.any?(robots_data, fn [coord, _] -> {x, y} == coord end) do
      "X"
    else
      "."
    end
  end

  defp draw_map(robots_data, tick_num, {max_x, max_y}) do
    map =
      for y <- 0..max_y, x <- 0..max_x, into: "" do
        case {x, y} do
          {x, y} when x == max_x ->
            draw_position_point({x, y}, robots_data) <> "\n"

          {x, y} ->
            draw_position_point({x, y}, robots_data)
        end
      end

    if String.contains?(map, "XXXXXXX") do
      IO.puts(tick_num)
      IO.puts(map)
    end

    robots_data
  end

  defp parse_input(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&amp;parse_input_line(&amp;1))
  end

  defp sum_quadrants(robots_data, {max_x, max_y}) do
    Enum.group_by(robots_data, fn [{x, y}, _] ->
      case {x, y} do
        {x, y} when x in 0..(div(max_x, 2) - 1)//1 and y in 0..(div(max_y, 2) - 1)//1 ->
          1

        {x, y} when x in (div(max_x, 2) + 1)..max_x//1 and y in 0..(div(max_y, 2) - 1)//1 ->
          2

        {x, y} when x in 0..(div(max_x, 2) - 1)//1 and y in (div(max_y, 2) + 1)..max_y//1 ->
          3

        {x, y} when x in (div(max_x, 2) + 1)..max_x//1 and y in (div(max_y, 2) + 1)..max_y//1 ->
          4

        _ ->
          nil
      end
    end)
    |> Enum.reject(fn {k, _val} -> k == nil end)
    |> Enum.map(fn {_i, list} -> length(list) end)
    |> Enum.product()
  end

  defp parse_input_line(line) do
    [position, velocity] = String.split(line, ["p=", " v="], trim: true)
    [x, y] = String.split(position, ",") |> Enum.map(&amp;String.to_integer(&amp;1))
    [vx, vy] = String.split(velocity, ",") |> Enum.map(&amp;String.to_integer(&amp;1))
    [{x, y}, {vx, vy}]
  end

  defp tick(robots_data, tick_num, _map_size, max_tick, _draw) when tick_num == max_tick do
    robots_data
  end

  defp tick(robots_data, tick_num, {max_x, max_y}, max_tick, draw) do
    Enum.map(robots_data, fn
      [{x, y}, {vx, vy}] ->
        new_pos_x =
          case x + vx do
            new_x when new_x > max_x ->
              new_x - max_x - 1

            new_x when new_x < 0 ->
              max_x + new_x + 1

            new_x ->
              new_x
          end

        new_pos_y =
          case y + vy do
            new_y when new_y > max_y ->
              new_y - max_y - 1

            new_y when new_y < 0 ->
              max_y + new_y + 1

            new_y ->
              new_y
          end

        [{new_pos_x, new_pos_y}, {vx, vy}]
    end)
    |> then(fn data ->
      if(draw, do: draw_map(data, tick_num, {max_x, max_y}))
      data
    end)
    |> tick(tick_num + 1, {max_x, max_y}, max_tick, draw)
  end
end

RestroomRedoubt.solve_part1(input)
RestroomRedoubt.solve_part2(input)