# Advent of Code Day 11 - Discussion

This topic is about Day 11 of the Advent of Code 2022 .

Sorry for delaying this post. I bet folks already solved it, let’s see how. I’ll start my attempt in a bit.

Link to our leaderboard:

https://adventofcode.com/2022/leaderboard/private/view/370884

The entry code is:
370884-a6a71927

Good luck!

2 Likes

I will make an attempt now. I am thinking gen_server and simulate the throws and catch. I am hoping catch and throw are allowable terms in Erlang.

1 Like

I had to scratch that idea. I initially though monkeys will be concurrently throwing and catching but I was wrong. It was a lot simpler than that. Although I had to go for hints for Part 2.

Here’s how I did this:

-module(day_11).

-export([solve/0]).

input() ->
element(2, file:read_file("data/11.txt")).

solve() ->
MonkeyInfo = parse(input()),
{solve1(MonkeyInfo), solve2(MonkeyInfo)}.

solve1(MonkeyInfo) ->
top_2(rounds(MonkeyInfo, 20, 3, 'div')).

solve2(MonkeyInfo) ->
RelaxFactor =
lists:foldl(fun(X, Acc) -> X * Acc end,
1,
[maps:get(divisible, X) || {_, X} <- maps:to_list(MonkeyInfo)]),
top_2(rounds(MonkeyInfo, 10_000, RelaxFactor, 'rem')).

rounds(MonkeyInfo, Count, RelaxFactor, Relaxor) ->
lists:foldl(fun(_, Acc) -> single_round(Acc, RelaxFactor, Relaxor) end,
MonkeyInfo,
lists:seq(1, Count)).

top_2(Rounds) ->
[{_, #{inspected := A}}, {_, #{inspected := B}} | _] =
lists:sort(fun({_, X}, {_, Y}) -> maps:get(inspected, X) > maps:get(inspected, Y) end,
maps:to_list(Rounds)),
A * B.

single_round(MonkeyInfo, RelaxFactor, Relaxor) ->
Monkeys = lists:seq(0, map_size(MonkeyInfo) - 1),
UpdateMonkeyInfo =
fun(WorryLevel, Target, Source, Map) ->
maps:update_with(Target,
fun(X) -> maps:update_with(items, fun(I) -> I ++ [WorryLevel] end, X) end,
maps:update_with(Source,
fun(X) ->
X#{items := [], inspected := maps:get(inspected, X) + 1}
end,
Map))
end,
Reducer =
fun(Idx, AccMonkey) ->
#{items := Items,
op := Op,
divisible := Divisible,
throw1 := Throw1,
throw2 := Throw2} =
maps:get(Idx, AccMonkey),
UpdatedWorryLevel = [Op(I) || I <- Items],
AfterRelax = [apply(erlang, Relaxor, [W, RelaxFactor]) || W <- UpdatedWorryLevel],
lists:foldl(fun(X, Acc) ->
case X rem Divisible of
0 -> UpdateMonkeyInfo(X, Throw1, Idx, Acc);
_ -> UpdateMonkeyInfo(X, Throw2, Idx, Acc)
end
end,
AccMonkey,
AfterRelax)
end,
lists:foldl(Reducer, MonkeyInfo, Monkeys).

parse(Input) ->
RawMonkeyInfo = binary:split(Input, <<"\n\n">>, [global]),
maps:from_list([parse_monkey(X) || X <- RawMonkeyInfo]).

parse_monkey(MonkeyRow) ->
[<<"Monkey ", Id:8, ":">>,
<<"  Starting items: ", Items/binary>>,
<<"  Operation: new = old ", Op/binary>>,
<<"  Test: divisible by ", Divisible/binary>>,
<<"    If true: throw to monkey ", Throw1/binary>>,
<<"    If false: throw to monkey ", Throw2/binary>>] =
binary:split(MonkeyRow, <<"\n">>, [global]),
{Id - \$0,
#{items =>
lists:map(fun(X) -> binary_to_integer(X) end, binary:split(Items, <<", ">>, [global])),
inspected => 0,
op => parse_operator(Op),
divisible => binary_to_integer(Divisible),
throw1 => binary_to_integer(Throw1),
throw2 => binary_to_integer(Throw2)}}.

parse_operator(Op) ->
[X, Operand] = binary:split(Op, <<" ">>),
Operator = binary_to_atom(X),
case Operand of
<<"old">> ->
fun(Old) -> apply(erlang, Operator, [Old, Old]) end;
C ->
fun(Old) -> apply(erlang, Operator, [Old, binary_to_integer(C)]) end
end.
2 Likes

State of my code was a complete mess as I solved the challenge, and now I tidied it up a bit so it’s not as painful to read:

main(File) ->
{ok, RawData} = file:read_file(File),
Data = maps:from_list([ parse_monkey(Block) || Block <- binary:split(RawData, <<"\n\n">>, [global, trim]) ]),
io:format("part 1: ~p~n", [solve1(Data)]),
io:format("part 2: ~p~n", [solve2(Data)]).

parse_monkey(Block) ->
[N1, N2, N3, N4, N5, N6] = binary:split(Block, <<"\n">>, [global, trim]),
[Id] = get_integers(N1),
{Id, #{items   => get_integers(N2),
op      => parse_operation(N3),
test    => hd(get_integers(N4)),
targets => get_integers(<<N5/binary, N6/binary>>),
count   => 0}}.

get_integers(Bin) ->
{match, Match} = re:run(Bin, <<"([-\\d]+)">>, [{capture, all_but_first, binary}, global]),
[ binary_to_integer(X) || [X] <- Match ].

parse_operation(Bin) ->
[_, Operation0] = binary:split(Bin, <<"new = old ">>, [global, trim]),
<<Op:1/binary, " ", Arg/binary>> = Operation0,
Fun = case Op of
<<"*">> -> fun erlang:'*'/2;
<<"+">> -> fun erlang:'+'/2
end,
case Arg of
<<"old">> -> fun (X) -> Fun(X, X) end;
N0        -> fun (X) -> Fun(X, binary_to_integer(N0)) end
end.

solve1(Data) ->
simulate(Data, 3, 20).

solve2(Data) ->
simulate(Data, 1, 10000).

simulate(Map, DivideBy, Iterations) ->
NewMap   = lists:foldl(fun (_, X) -> simulate(X, DivideBy) end, Map, lists:seq(1, Iterations)),
[A, B|_] = lists:reverse(lists:sort([ X || #{count := X} <- maps:values(NewMap) ])),
A * B.

simulate(Map, DivideBy) ->
LCD = lists:foldl(fun erlang:'*'/2, 1, [ X || #{test := X} <- maps:values(Map) ]),
lists:foldl(fun (Id, Acc) -> simulate(Id, DivideBy, LCD, Acc) end, Map, maps:keys(Map)).

simulate(Id, DivideBy, LCD, Map) ->
#{items := Items,
count := Count} = Monkey = maps:get(Id, Map),
NewMap = Map#{Id => Monkey#{items => [],
count => Count + length(Items)}},
lists:foldl(fun (I, Acc) -> examine(I, DivideBy, LCD, Monkey, Acc) end, NewMap, Items).

examine(N, DivideBy, LCD, Monkey, Acc) ->
#{op      := Op,
test    := Test,
targets := [IfTrue, IfFalse]} = Monkey,
NewN    = Op(N) div DivideBy rem LCD,
ThrowTo = case NewN rem Test of
0 -> IfTrue;
_ -> IfFalse
end,
maps:update_with(ThrowTo, fun(M = #{items := Items}) -> M#{items => Items ++ [NewN]} end, Acc).
1 Like