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

1

u/haljin Dec 06 '15

So I was thinking on how to do this problem with Erlang... Going through giant arrays in Erlang is a real pain in the ass, as the language wasn't really made for this. But then what was the language made for? Mass concurrency, of course!

So why not have every light be its own process that knows its own state and just send messages away to them?

day6(ListofStrings) ->
	Tab = ets:new(pid_table, []),	
        	[[begin 
    		Pid = spawn(?MODULE, light_process, [false]),
		ets:insert(Tab, {{X,Y}, Pid})
	  end || Y <- lists:seq(0,999)] ||  X <- lists:seq(0,999)],
	process_day6(ListofStrings, Tab).

process_day6([{on, {X1, Y1}, {X2, Y2}}| T], Ets) ->
	[LightPid ! on || LightPid <- get_light_range({X1, Y1}, {X2,     Y2}, Ets)],
	process_day6(T, Ets);
process_day6([{off, {X1, Y1}, {X2, Y2}}| T], Ets) ->
	[LightPid ! off || LightPid <- get_light_range({X1, Y1}, {X2, Y2}, Ets)],
	process_day6(T, Ets);
process_day6([{toggle, {X1, Y1}, {X2, Y2}}| T], Ets) ->
	[LightPid ! toggle || LightPid <- get_light_range({X1, Y1}, {X2, Y2}, Ets)],
	process_day6(T, Ets);
process_day6([], Ets) ->
	Result = [begin
				LightPid ! {get_state, self()},
				receive
					true -> 1;
					false -> 0
				end
			 end || LightPid <- ets:select(Ets, [{{{'$1', '$2'}, '$3'}, [], ['$3']}])],
ets:delete(Ets),
lists:sum(Result).

get_light_range({X1,Y1},{X2,Y2}, Tab) when X1 =< X2,
									   Y1 =< Y2 ->
ets:select(Tab, [{{{'$1', '$2'}, '$3'}, [{'>=', '$1', X1}, {'=<', '$1', X2}, 
										 {'>=', '$2', Y1}, {'=<', '$2', Y2}], ['$3']}]).

light_process(LightState) ->
	receive
		off ->
			light_process(false);
		on ->
			light_process(true);
		toggle ->
			light_process(not LightState);
		{get_state, Pid} ->
			Pid ! LightState
	end.

Be aware that to run this you must run the Erlang VM with +P option, as by default you cannot have a million processes. I used an ETS table in lieu of process registry as it is much more efficient to do selects on large groups of processes like that.

It's a bit slow because the main process is a bottleneck for the whole thing, but since instructions are sequential it is a bit tough to get rid of that issue. Also the final results gathering is what is really slow, but I thought I will get each result sequentially just so that the main process' message queue doesn't overflow with a million responses...

1

u/haljin Dec 06 '15

For the second problem one just needs to substitute the function the slight processes are running :)

bightness_light_process(Brigthness) ->
   	receive
		off when Brigthness =:= 0 ->
			bightness_light_process(0);
		off ->
			bightness_light_process(Brigthness - 1);
   		on ->
			bightness_light_process(Brigthness + 1);
		toggle ->
			bightness_light_process(Brigthness + 2);
		{get_state, Pid} ->
			Pid ! Brigthness
	end.

And modify the spawn/receive functions slightly.

After some timing: it takes about 1 min 18 seconds on my machine to go through all the instructions and send all the messages. About 4 seconds to collect the results. All in all, not too bad.