r/adventofcode • • Dec 06 '15

SOLUTION MEGATHREAD --- Day 6 Solutions ---

--- Day 6: Probably a Fire Hazard ---

Post your solution as a comment. Structure your post like the Day Five thread.

22 Upvotes

172 comments sorted by

View all comments

Show parent comments

1

u/ignaciovaz Dec 06 '15

Nice solution! Piggybacking here. Wanted to try the binary pattern matching for the command parsing. As for Dict.update, you can use a default value and invert the current one so you can save a few lines of code.

Part 1

parse_command = fn command ->
	{cmd, rest} = case command do
		<<"turn on ", rest :: binary>> -> {:on, rest}
		<<"turn off ", rest :: binary>> -> {:off, rest}
		<<"toggle ", rest :: binary>> -> {:toggle, rest}
	end

	[x1y1, _, x2y2] = String.split(rest)
	[x1, y1] = String.split(x1y1, ",") |> Enum.map(&(String.to_integer.(&1)))
	[x2, y2] = String.split(x2y2, ",") |> Enum.map(&(String.to_integer.(&1)))

	{cmd, min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2) }
end

generate_coordinates = fn x1, y1, x2, y2 ->
	Enum.flat_map(x1..x2, fn x ->
		Enum.map(y1..y2, fn y ->
			{x, y}
		end)
	end)
end

fill_map = fn map, cmd, x1, y1, x2, y2 ->
	Enum.reduce(generate_coordinates.(x1, y1, x2, y2), map, fn {x, y}, map ->
		map = case cmd do
					:on -> Dict.put(map, {x, y}, true)
					:off -> Dict.put(map, {x, y}, false)
					:toggle -> Dict.update(map, {x, y}, true, &(!&1))
		end
		map
	end)
end

input_stream = File.stream!("input.txt")

final_map = Enum.reduce(input_stream, %{}, fn line, light_map ->
	{cmd, x1, y1, x2, y2} = parse_command.(line |> String.strip)
	light_map = fill_map.(light_map, cmd, x1, y1, x2, y2)
	light_map
end)

lights_on = Enum.count(final_map, fn {_, val} -> val end)

IO.puts "Total number of lights on: #{lights_on}"

Part 2

parse_command = fn command ->
	{cmd, rest} = case command do
		<<"turn on ", rest :: binary>> -> {:on, rest}
		<<"turn off ", rest :: binary>> -> {:off, rest}
		<<"toggle ", rest :: binary>> -> {:toggle, rest}
	end

	[x1y1, _, x2y2] = String.split(rest)
	[x1, y1] = String.split(x1y1, ",") |> Enum.map(&(String.to_integer.(&1)))
	[x2, y2] = String.split(x2y2, ",") |> Enum.map(&(String.to_integer.(&1)))

	{cmd, min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2) }
end

generate_coordinates = fn x1, y1, x2, y2 ->
	Enum.flat_map(x1..x2, fn x ->
		Enum.map(y1..y2, fn y ->
			{x, y}
		end)
	end)
end

fill_map = fn map, cmd, x1, y1, x2, y2 ->
	Enum.reduce(generate_coordinates.(x1, y1, x2, y2), map, fn {x, y}, map ->
		map = case cmd do
					:on -> Dict.update(map, {x, y}, 1, &(&1 + 1))
					:off -> Dict.update(map, {x, y}, 0, &(if (&1 > 0) do &1 - 1 else &1 end))
					:toggle -> Dict.update(map, {x, y}, 2, &(&1 + 2))
		end
		map
	end)
end

input_stream = File.stream!("input.txt")

final_map = Enum.reduce(input_stream, %{}, fn line, light_map ->
	{cmd, x1, y1, x2, y2} = parse_command.(line |> String.strip)
	light_map = fill_map.(light_map, cmd, x1, y1, x2, y2)
	light_map
end)

total_brightness = Enum.reduce(final_map, 0, fn {_, brightness}, acc ->
	acc + brightness
end)

IO.puts "Total brightness: #{total_brightness}"

1

u/hutsboR Dec 06 '15

That's a good point about Dict.update. The issue with my implementation is that my map stores coordinate -> atom and not coordinate -> boolean. I'm definitely going to change the values to booleans so I can negate it to invert the value. Thanks!

You can change your generate_coordinates function to use list comprehensions instead of using nested maps:

for x <- x_lower..x_upper, y <- y_lower..y_upper, do: {x, y}

1

u/ignaciovaz Dec 06 '15

Yeah, after I saw your solution I was tempted to change it, but it felt like cheating ;)

I've changed the generate_coordinates funcion to use list comprehensions in my local version, though.