Powered by AppSignal & Oban Pro

Aoc 2019 - day 10

2019/elixir/day-10.livemd

Aoc 2019 - day 10

Mix.install([
  {:kino, "~> 0.5.2"}
])

Setup

input = Kino.Input.textarea("input")
coords_map =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.with_index(0)
  |> Enum.flat_map(fn {line, y} ->
    line
    |> String.codepoints()
    |> Enum.with_index(0)
    |> Enum.filter(fn {c, _} -> c == "#" end)
    |> Enum.map(fn {_c, x} -> {{x, y}, 0} end)
  end)
  |> Enum.into(%{})

Part 1

get_max_coord = fn coords_map ->
  coords_map
  |> Enum.reduce(coords_map, fn {{x, y}, _}, crds_map ->
    coords_map
    |> Map.delete({x, y})
    |> Stream.map(fn {{x1, y1}, _} -> {x1 - x, y1 - y} end)
    |> MapSet.new(fn
      {0, y} when y >= 0 -> :inf
      {0, y} when y < 0 -> :neg_inf
      {x, y} when x > 0 -> {:pos, y / x}
      {x, y} -> {:neg, y / x}
    end)
    |> MapSet.size()
    |> then(&Map.put(crds_map, {x, y}, &1))
  end)
  # |> Enum.sort_by(&elem(&1, 1), :desc)
  |> Enum.max_by(&elem(&1, 1))
end

get_max_coord.(coords_map)

Part 2

mark = {mark_x = 26, mark_y = 28}
{0, 0, 1} > {0, 1}
angle_fn = fn
  {0, y} when y >= 0 -> :pos_inf
  {0, y} when y < 0 -> :neg_inf
  {x, y} when x > 0 -> {:pos, y / x}
  {x, y} -> {:neg, y / x}
end

mapper = fn
  %{angle: :neg_inf, phase: phs} ->
    {phs, 0, 0}

  %{angle: {:pos, angle}, phase: phs} ->
    {phs, 1, angle}

  %{angle: :pos_inf, phase: phs} ->
    {phs, 2, 0}

  %{angle: {:neg, angle}, phase: phs} ->
    {phs, 3, angle}
end

dist_fn = fn {{x, y}, _} ->
  x * x + y * y
end

coords_map
|> get_max_coord.()
|> then(fn {mark, _} -> {coords_map, mark} end)
|> then(fn {coords_map, {mark_x, mark_y}} ->
  coords_map
  |> Map.delete({mark_x, mark_y})
  |> Enum.sort_by(&dist_fn.(&1))
  |> Enum.map_reduce(%{}, fn {{x, y} = crd, _}, angle_counter ->
    angle = angle_fn.({x - mark_x, y - mark_y})
    {phase, updated_ac} = Map.get_and_update(angle_counter, angle, &{&1 || 0, (&1 || 0) + 1})
    {%{phase: phase, coord: crd, angle: angle}, updated_ac}
  end)
end)
|> elem(0)
|> Enum.sort_by(&mapper.(&1))
# |> Enum.take(200)
|> Enum.at(19)

# |> Map.get(:coord)