Powered by AppSignal & Oban Pro

AoC 2018 Day 23

2018/day23.livemd

AoC 2018 Day 23

Mix.install([:kino])

defmodule Utils do
  def split(line, sep \\ "") do
    String.split(line, sep, trim: true)
  end

  def split_all_lines(text, sep \\ "") do
    text
    |> String.split("\n", trim: true)
    |> Enum.map(&split(&1, sep))
  end

  def to_numbers(number) when is_binary(number) do
    String.to_integer(number)
  end

  def to_numbers(numbers) when is_list(numbers) do
    Enum.map(numbers, &to_numbers/1)
  end

  def to_matrix(text, sep \\ "") do
    text
    |> split_all_lines(sep)
    |> then(fn data ->
      for {row, r} <- Enum.with_index(data), {col, c} <- Enum.with_index(row) do
        {{r, c}, col}
      end
    end)
    |> Map.new()
  end
end

Setup

import Utils
input = Kino.Input.textarea("Input:")
text = Kino.Input.read(input)
data =
  split(text, "\n")
  |> Enum.map(fn line -> Regex.scan(~r/-?\d+/, line) end)
  |> List.flatten()
  |> to_numbers()
  |> Enum.chunk_every(4)

P1

defmodule P1 do
  def solve(data) do
    [x, y, z, r] = Enum.max_by(data, &List.last/1)

    data
    |> Enum.map(&Enum.drop(&1, -1))
    |> Enum.filter(&(dis(&1, [x, y, z]) <= r))
    |> Enum.count()
  end

  def dis(a, b) do
    Enum.zip_with(a, b, fn l, r -> abs(l - r) end) |> Enum.sum()
  end
end

P1.solve(data)

P2

defmodule P2 do
  def solve(data) do
    data =
      Enum.map(data, fn [x, y, z, r] ->
        %{x: (x - r)..(x + r), y: (y - r)..(y + r), z: (z - r)..(z + r), r: r, p: [x, y, z]}
      end)

    overlaps =
      data
      |> Enum.map(&overlaps(data, &1))

    {_, max} =
      overlaps
      |> Enum.max_by(&elem(&1, 1))

    overlaps
    |> Enum.filter(&(elem(&1, 1) == max))
    |> Enum.map(fn {bot, _} ->
      diff = P1.dis(bot.p, [bot.x.first, bot.y.first, bot.z.first])
      {Enum.sum([diff - bot.r, bot.x.first, bot.y.first, bot.z.first]), bot}
    end)
    |> Enum.max()
  end

  def overlaps(data, bot) do
    Enum.reduce(data, {bot, 0}, fn
      %{x: x2, y: y2, z: z2, r: r2, p: p2}, {%{x: x1, y: y1, z: z1, r: r1, p: p1} = bot, count} ->
        if P1.dis(p1, p2) > r1 + r2 do
          {bot, count}
        else
          [x, y, z] =
            Enum.zip_with([x1, y1, z1], [x2, y2, z2], fn as..ae, bs..be ->
              max(as, bs)..min(ae, be)//1
            end)

          if Enum.all?([x, y, z], &(Range.size(&1) > 0)) do
            {%{x: x, y: y, z: z, r: r1, p: p1}, count + 1}
          else
            {bot, count}
          end
        end
    end)
  end
end

P2.solve(data)