diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/day08.exs | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/src/day08.exs b/src/day08.exs new file mode 100644 index 0000000..0d312e1 --- /dev/null +++ b/src/day08.exs @@ -0,0 +1,134 @@ +defmodule Day08 do + defmodule Grid do + defstruct [:grid, :height, :width] + + def from_data(data) do + grid = data + |> String.split("\n", trim: true) + |> Enum.map(& &1 |> String.codepoints() |> :array.from_list()) + |> :array.from_list() + + height = :array.size(grid) + + width = :array.size(:array.get(0, grid)) + + %Grid{grid: grid, height: height, width: width} + end + + def at(grid, i, j) do + :array.get(j, :array.get(i, grid.grid)) + end + + def in?(grid, i, j) do + i >= 0 and i < grid.height and j >= 0 and j < grid.width + end + + def find_elements(grid, regex) do + for i <- 0..grid.height-1, + j <- 0..grid.width-1, + v = at(grid, i, j) do + if String.match?(v, regex) do + {v, i, j} + end + end + |> Enum.filter(& &1 != nil) + end + end + + def part1(data) do + grid = data + |> Grid.from_data() + + antennas = grid + |> Grid.find_elements(~r/[[:alnum:]]/) + |> Enum.group_by(fn {k, _, _} -> k end, fn {_, i, j} -> {i, j} end) + + antennas + |> Enum.map(fn {_, a} -> antinodes([], a) end) + |> Enum.flat_map(& &1) + |> Enum.uniq() + |> Enum.filter(fn {i, j} -> Grid.in?(grid, i, j) end) + |> Enum.count() + end + + defp antinodes(acc, [a | rest]) do + rest + |> Enum.reduce(acc, fn b, acc -> antinodes(acc, a, b) end) + |> antinodes(rest) + end + + defp antinodes(acc, []), do: acc + + defp antinodes(acc, {ai, aj}, {bi, bj}) do + di = abs(ai - bi) + dj = abs(aj - bj) + [{min(ai, bi) - di, if aj < bj do aj - dj else aj + dj end} | + [{max(ai, bi) + di, if aj < bj do bj + dj else bj - dj end} | + acc]] + end + + def part2(data) do + grid = data + |> Grid.from_data() + + antennas = grid + |> Grid.find_elements(~r/[[:alnum:]]/) + |> Enum.group_by(fn {k, _, _} -> k end, fn {_, i, j} -> {i, j} end) + + antennas + |> Enum.map(fn {_, a} -> antinodes2([], a, grid) end) + |> Enum.flat_map(& &1) + |> Enum.uniq() + |> Enum.count() + + end + + defp antinodes2(acc, [a | rest], grid) do + rest + |> Enum.reduce(acc, fn b, acc -> antinodes2(acc, a, b, grid) end) + |> antinodes2(rest, grid) + end + + defp antinodes2(acc, [], _), do: acc + + defp antinodes2(acc, {ai, aj}, {bi, bj}, grid) do + di = abs(ai - bi) + dj = abs(aj - bj) + + cond do + di == 0 -> + acc + |> antinodes2(ai, aj, 0, -1, grid) + |> antinodes2(ai, aj, 0, 1, grid) + dj == 0 -> + acc + |> antinodes2(ai, aj, -1, 0, grid) + |> antinodes2(ai, aj, 1, 0, grid) + true -> + g = Integer.gcd(di, dj) + di = div(di, g) + dj = div(dj, g) + acc + |> antinodes2(ai, aj, -di, if aj < bj do -dj else dj end, grid) + |> antinodes2(ai, aj, di, if aj < bj do dj else -dj end, grid) + end + end + + defp antinodes2(acc, i, j, di, dj, grid) do + if Grid.in?(grid, i, j) do + antinodes2([{i, j} | acc], i+di, j+dj, di, dj, grid) + else + acc + end + end +end + +data = IO.read(:stdio, :eof) + +{time1 , ans1} = :timer.tc(fn -> Day08.part1(data) end) +IO.puts("Time : #{time1 / 1000000}") +IO.puts("Answer: #{ans1}") + +{time2 , ans2} = :timer.tc(fn -> Day08.part2(data) end) +IO.puts("Time : #{time2 / 1000000}") +IO.puts("Answer: #{ans2}") |