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

Day 6: Probably a Fire Hazard

2015/day06.livemd

Day 6: Probably a Fire Hazard

Mix.install([:kino])

Input

input = Kino.Input.textarea("Input")
convert_range = fn range ->
  range
  |> String.split([" through ", ","])
  |> Enum.map(&String.to_integer/1)
  |> then(fn [x1, y1, x2, y2] -> {{x1, y1}, {x2, y2}} end)
end

instructions =
  input
  |> Kino.Input.read()
  |> String.split("\n")
  |> Enum.map(fn
    "turn on " <> range ->
      {:on, convert_range.(range)}

    "turn off " <> range ->
      {:off, convert_range.(range)}

    "toggle " <> range ->
      {:toggle, convert_range.(range)}
  end)

Part 1

selection = fn {x1, y1}, {x2, y2} ->
  for x <- x1..x2, y <- y1..y2, into: MapSet.new(), do: {x, y}
end

Enum.reduce(instructions, MapSet.new(), fn
  {:toggle, {a, b}}, lights ->
    MapSet.symmetric_difference(lights, selection.(a, b))

  {:on, {a, b}}, lights ->
    MapSet.union(lights, selection.(a, b))

  {:off, {a, b}}, lights ->
    MapSet.difference(lights, selection.(a, b))
end)
|> Enum.count()

Part 2

Enum.reduce(instructions, %{}, fn
  {:toggle, {{x1, y1}, {x2, y2}}}, lights ->
    for x <- x1..x2, y <- y1..y2, reduce: lights do
      lights -> Map.update(lights, {x, y}, 2, &amp;(&amp;1 + 2))
    end

  {:on, {{x1, y1}, {x2, y2}}}, lights ->
    for x <- x1..x2, y <- y1..y2, reduce: lights do
      lights -> Map.update(lights, {x, y}, 1, &amp;(&amp;1 + 1))
    end

  {:off, {{x1, y1}, {x2, y2}}}, lights ->
    for x <- x1..x2, y <- y1..y2, reduce: lights do
      lights -> Map.update(lights, {x, y}, 0, &amp;max(&amp;1 - 1, 0))
    end
end)
|> Map.values()
|> Enum.sum()