You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
98 lines
4.1 KiB
98 lines
4.1 KiB
-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(<<String/binary>>, 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(<<RestIn/binary>>, 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(<<Digit, Rest/binary>>, Acc) when Digit >= $0 andalso Digit =< $9 ->
|
|
parse_int0(Rest, Acc*10 + Digit - $0);
|
|
parse_int0(Binary, Acc) when is_binary(Binary) -> {Acc, Binary}.
|