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

Day 5

day05.livemd

Day 5

Helpers

Mix.install([
  {:nimble_parsec, "~> 1.2"}
])
defmodule Helpers do
  def data() do
    "data/day05.txt"
    |> File.stream!()
    |> Stream.map(&String.trim/1)
    |> Stream.map(&parse_line/1)
  end

  def parse_line(line) do
    {:ok, [x1, y1, x2, y2], "", _, _, _} = Parser.vent_line(line)
    {x1, y1, x2, y2}
  end

  def horizontal?({x1, _y1, x2, _y2}), do: x1 == x2
  def vertical?({_x1, y1, _x2, y2}), do: y1 == y2
  def diagonal?({x1, y1, x2, y2}), do: abs(x2 - x1) == abs(y2 - y1)

  # vertical points
  def points({x, y1, x, y2}), do: for(y <- y1..y2, do: {x, y})
  # horizontal points
  def points({x1, y, x2, y}), do: for(x <- x1..x2, do: {x, y})
  # diagonal points sloping down
  def points({x1, y1, x2, y2}) when x2 - x1 == y2 - y1 do
    for i <- 0..(x2 - x1), do: {x1 + i, y1 + i}
  end

  # diagonal points sloping up
  def points({x1, y1, x2, y2}) when x2 - x1 == y1 - y2 do
    for i <- 0..(x2 - x1), do: {x1 + i, y1 - i}
  end
end
defmodule Parser do
  import NimbleParsec

  defparsec(
    :vent_line,
    integer(min: 1, max: 3)
    |> ignore(string(","))
    |> integer(min: 1, max: 3)
    |> ignore(string(" -> "))
    |> integer(min: 1, max: 3)
    |> ignore(string(","))
    |> integer(min: 1, max: 3)
  )
end

Part 1

import Helpers

data()
|> Stream.filter(fn coords -> horizontal?(coords) || vertical?(coords) end)
|> Stream.flat_map(&amp;points/1)
|> Enum.frequencies()
|> Enum.filter(fn {_p, count} -> count >= 2 end)
|> Enum.count()

Part 2

import Helpers

data()
|> Stream.filter(fn coords -> horizontal?(coords) || vertical?(coords) || diagonal?(coords) end)
|> Stream.flat_map(&amp;points/1)
|> Enum.frequencies()
|> Enum.filter(fn {_p, count} -> count >= 2 end)
|> Enum.count()