-module(day11). -export([solve/1]). solve(Input) -> Monkeys = parse_monkeys(Input), Monkeys1 = worry_management(Monkeys, 1), Monkeys2 = worry_management(Monkeys, 2), {score(run(20, Monkeys1)), score(run(10000, Monkeys2))}. worry_management(Monkeys, Part) -> Product = maps:fold(fun(_, #{test_divisor := TestDivisor}, Acc) -> Acc * TestDivisor end, 1, Monkeys), maps:map(fun (_, #{operation := Operation} = Monkey) -> Monkey#{operation := case Part of 1 -> fun (Level) -> Operation(Level) div 3 end; 2 -> fun (Level) -> Operation(Level) rem Product end end} end, Monkeys). score(Monkeys0) -> Monkeys1 = maps:values(Monkeys0), Monkeys2 = lists:map(fun(#{interact:=Interact}) -> Interact end, Monkeys1), [M1, M2|_Rest] = lists:sort(fun erlang:'>'/2, Monkeys2), M1*M2. run(0, Monkeys) -> Monkeys; run(N, Monkeys) -> run(N-1, run_round(Monkeys)). run_round(Monkeys) -> lists:foldl( fun (Id, State) -> run_turn(Id, State) end, Monkeys, maps:keys(Monkeys) ). run_turn(Id, Monkeys) -> #{Id := CurrentMonkey} = Monkeys, #{operation := Operation, test := Test, queue := Queue} = CurrentMonkey, case Queue of [] -> Monkeys; [Item|_Rest] -> NewLevel = Operation(Item), Target = Test(NewLevel), run_turn(Id, throw_item(Monkeys, Id, Target)) end. increment_interacts(Monkey) -> maps:update_with(interact, fun(X)->X+1 end, 1, Monkey). throw_item(Monkeys, From, To) -> #{From := #{operation := Op, queue:= [Item|FromQueue]} = FromMonkey, To := #{queue := ToQueue} = ToMonkey} = Monkeys, Monkeys#{From := increment_interacts(FromMonkey#{queue:=FromQueue}), To := ToMonkey#{queue := ToQueue ++ [Op(Item)]}}. parse_monkeys(Input) -> parse_monkeys(Input, #{}). parse_monkeys(<<>>, Acc) -> Acc; parse_monkeys(<<$\n, Rest/binary>>, Acc) -> parse_monkeys(Rest, Acc); parse_monkeys(Input, Storage) -> <<"Monkey ", MonkeyDigit, $:, $\n, Rest1/binary>> = Input, MonkeyNumber = MonkeyDigit - $0, <<" Starting items:", Rest2/binary>> = Rest1, {Items, Rest3} = parse_items(Rest2, []), <<" Operation: new = ", Rest4/binary>> = Rest3, {Operation, Rest5} = parse_operation(Rest4, []), <<" Test: divisible by ", Rest6/binary>> = Rest5, {TestDivisor, <<$\n, Rest7/binary>>} = parse_int(Rest6), <<" If true: throw to monkey ", TrueDigit, $\n, Rest8/binary>> = Rest7, True = TrueDigit - $0, <<" If false: throw to monkey ", FalseDigit, $\n, Rest9/binary>> = Rest8, False = FalseDigit - $0, Test = fun (Var) -> case Var rem TestDivisor of 0 -> True; _ -> False end end, parse_monkeys(Rest9, Storage#{MonkeyNumber => #{queue => Items, operation => Operation, test_divisor => TestDivisor, test => Test}}). parse_operation(<<$\n, Rest/binary>>, [Rhs, Op, Lhs]) -> {fun(Old) -> Op(Rhs(Old), Lhs(Old)) end, Rest}; parse_operation(<<$\s, Rest/binary>>, Acc) -> parse_operation(Rest, Acc); parse_operation(<<$+, Rest/binary>>, Acc) -> parse_operation(Rest, [fun erlang:'+'/2|Acc]); parse_operation(<<$*, Rest/binary>>, Acc) -> parse_operation(Rest, [fun erlang:'*'/2|Acc]); parse_operation(<<"old", Rest/binary>>, Acc) -> parse_operation(Rest, [fun(Old) -> Old end | Acc]); parse_operation(<>, Acc) -> {Literal, Rest} = parse_int(String), parse_operation(Rest, [fun(_Old) -> Literal end | Acc]). parse_items(<<$\n, Rest/binary>>, Items) -> {lists:reverse(Items), Rest}; parse_items(<<$,, Rest/binary>>, Items) -> parse_items(Rest, Items); parse_items(<<$\s, Rest/binary>>, Items) -> parse_items(Rest, Items); parse_items(<>, Items) -> {Item, RestOut} = parse_int(RestIn), parse_items(RestOut, [Item|Items]). parse_int(<<$-, Rest/binary>>) -> {AbsoluteValue, Binary} = parse_int0(Rest, 0), {-1 * AbsoluteValue, Binary}; parse_int(Binary) -> parse_int0(Binary, 0). parse_int0(<>, Acc) when Digit >= $0 andalso Digit =< $9 -> parse_int0(Rest, Acc*10 + Digit - $0); parse_int0(Binary, Acc) when is_binary(Binary) -> {Acc, Binary}.