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, &(&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, &(&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, &max(&1 - 1, 0))
end
end)
|> Map.values()
|> Enum.sum()