%% to compile: erlc day3A.erl %% to run: erl -noshell -s day5 solve %% -module(day14). -export ([solve/0, solve/1, solve/2]). -export ([expand_pair/4, read_input/0]). -compile ([export_all]). solve() -> solve(['1']), solve(['2']), init:stop(). solve(A) -> solve(A, read_input()). solve(['1'], D) -> io:format("The solution to ~p puzzle1 is: ~p~n", [?MODULE, solve(1, D)]); solve(1, D) -> solution1(D); solve(['2'], D) -> io:format("The solution to ~p puzzle2 is: ~p~n", [?MODULE, solve(2, D)]); solve(2, D) -> solution2(D). read_input() -> {ok, IO} = file:open("input.txt", 'read'), Data = read_input(IO), file:close(IO), Data. read_input(IO) -> read_input(IO, []). read_input(IO, Input) -> case read_line(IO) of 'eof' -> make_map(Input); Line -> read_input(IO, Input ++ [Line]) end. read_line(IO) -> case file:read_line(IO) of 'eof' -> 'eof'; {ok, Line} -> parse_line(Line) end. parse_line(Line) -> string:tokens(Line, " ->\n"). make_map(Input) -> make_map(Input, maps:new()). make_map([], Map) -> Map; make_map([[P, [Res]]|Rest], Map) -> NewMap = maps:put(P, Res, Map), make_map(Rest, NewMap). solution1(PolyMap) -> Ets = ets:new(polymer, [set, public, named_table]), Polymer = "FNFPPNKPPHSOKFFHOFOC", init_counters(Ets, PolyMap), io:format("~p~n", [ets:tab2list(Ets)]), Pairs = make_pairs(Polymer, []), io:format("expand pairs: ~p~n",[Pairs]), count_pairs(Pairs, Ets), [count_elems(Pair, Ets, 1) || Pair <- Pairs], loop(Ets, PolyMap, 10), get_max_elm(Ets) - get_min_elm(Ets). solution2(PolyMap) -> Ets = ets:new(polymer, [set, public, named_table]), Polymer = "FNFPPNKPPHSOKFFHOFOC", init_counters(Ets, PolyMap), io:format("~p~n", [ets:tab2list(Ets)]), Pairs = make_pairs(Polymer, []), io:format("expand pairs: ~p~n",[Pairs]), count_pairs(Pairs, Ets), [count_elems(Pair, Ets, 1) || Pair <- Pairs], loop(Ets, PolyMap, 40), get_max_elm(Ets) - get_min_elm(Ets). get_max_elm(Ets) -> Ents = ets:tab2list(Ets), {V, _} = lists:max([{V,X} || {{elem, X},V} <- Ents]), V. get_min_elm(Ets) -> Ents = ets:tab2list(Ets), {V, _} = lists:min([{V,X} || {{elem, X},V} <- Ents]), V. loop(Ets, _PolyMap, 0) -> ets:tab2list(Ets); loop(Ets, PolyMap, Count) -> Counters = get_counters(Ets), [update_ets(Ets, PolyMap, Counter) || Counter <- Counters], loop(Ets, PolyMap, Count - 1). init_counters(Ets, PolyMap) -> maps:foreach(fun([X,Y] = Key, _Value) -> ets:insert(Ets, {{elem,X},0}), ets:insert(Ets, {{elem,Y},0}), ets:insert(Ets, {{pair, Key}, 0}) end, PolyMap). update_ets(_Ets, _PolyMap, {_Key, 0}) -> ok; update_ets(Ets, PolyMap, {[X,Y] = Key, Value}) -> V = maps:get(Key, PolyMap), count_pair([X,V],Ets, Value), count_pair([V,Y],Ets, Value), count_pair(Key,Ets, -1 * Value), count_elem(V, Ets, Value). get_counters(Ets) -> Ents = ets:tab2list(Ets), [{X,V} || {{pair, X},V} <- Ents]. make_pairs([_], Pairs) -> Pairs; make_pairs([X,Y|Rest], Pairs) -> make_pairs([Y|Rest], [[X,Y]|Pairs]). count_pairs([], _Ets) -> ok; count_pairs([P|Rest], Ets) -> count_pair(P, Ets, 1), count_pairs(Rest, Ets). count_pair(P, Ets, V) -> ets:update_counter(Ets, {pair,P}, {2,V}). count_elems([X,Y], Ets, V) -> count_elem(X, Ets, V), count_elem(Y, Ets, V). count_elem(Elem, Ets, V) -> ets:update_counter(Ets, {elem,Elem}, {2,V}). expand_pair(_P, _PolyMap, _Ets, 0) -> ok; expand_pair([P1,P2], PolyMap, Ets, Count) when is_list(P1), is_list(P2) -> expand_pair(P1,PolyMap, Ets, Count - 1); expand_pair([X,Y] = P, PolyMap, Ets, Count) -> [V] = maps:get(P, PolyMap), ets:update_counter(Ets, [V], {2,1}), [expand_pair(Pa, PolyMap, Ets, Count) || Pa <- [[X,V]] ++ [[V,Y]]]. pairs([], _PolyMap, Pairs, _Ets, 0) -> Pairs; pairs([], PolyMap, Pairs, Ets, Count) -> io:format("new pairs: ~p~n", [Pairs]), pairs(Pairs, PolyMap, [], Ets, Count - 1); pairs([[X,Y] = P|Rest], PolyMap, Pairs, Ets, Count) -> [V] = maps:get(P, PolyMap), ets:update_counter(Ets, [V], {2,1}), pairs(Rest, PolyMap, Pairs ++ [[X,V]] ++ [[V,Y]], Ets, Count). count(Polymer) -> Ps = lists:usort(Polymer), [ {count_p(P, Polymer), [P]} || P <- Ps]. count_p(P, Polymer) -> lists:foldl(fun(X, Acc) -> case X == P of 'true' -> Acc + 1; _ -> Acc end end, 0, Polymer). steps(Polymer, _PolyMap, 0) -> Polymer; steps(Polymer, PolyMap, Count) -> Insert = get_insert_list(Polymer, PolyMap), steps(lists:flatten(lists:zipwith(fun(A, B) -> [A,B] end, Polymer, Insert)), PolyMap, Count - 1). get_insert_list(Polymer, PolyMap) -> get_insert_list(Polymer, PolyMap, []). get_insert_list([_], _PolyMap, InsertList) -> lists:reverse([[]|InsertList]); get_insert_list([P1,P2|Rest], PolyMap, InsertList) -> V = maps:get([P1,P2], PolyMap), get_insert_list([P2|Rest], PolyMap, [V|InsertList]).