-module(day21). -export([solve/1]). solve(InputData) -> Monkeys = parse_input(InputData), {eval(<<"root">>, Monkeys), undo(human_path(<<"root">>, p2fix(Monkeys), []))}. % turn the root into a big equal sign p2fix(#{<<"root">> := {_Op, Left, Right}} = Monkeys) -> Monkeys#{<<"root">> := {$-, Left, Right}}. parse_input(Input) -> parse_input(Input, #{}). parse_input(<<>>, Acc) -> Acc; parse_input(<<$\n, Rest/binary>>, Acc) -> parse_input(Rest, Acc); parse_input(<>, Monkeys) -> parse_input(Rest, Monkeys#{Name => {Op, Left, Right}}); parse_input(<>, Monkeys) -> {Value, RestOut} = parse_int(RestIn), parse_input(RestOut, Monkeys#{Name => Value}). eval(Name, Monkeys) -> #{Name := Node} = Monkeys, case Node of Value when is_integer(Value) -> Value; {Op, Left, Right} -> LeftValue = eval(Left, Monkeys), RightValue = eval(Right, Monkeys), case Op of $+ -> LeftValue + RightValue; $- -> LeftValue - RightValue; $/ -> LeftValue div RightValue; $* -> LeftValue * RightValue end end. undo(HumanPath) -> lists:foldl(fun do_undo/2, 0, HumanPath). do_undo({_Side, $+, Value}, Target) -> Target - Value; do_undo({_Side, $*, Value}, Target) -> Target div Value; do_undo({left, $/, Value}, Target) -> Target * Value; do_undo({right, $/, Value}, Target) -> Value div Target; do_undo({left, $-, Value}, Target) -> Target + Value; do_undo({right, $-, Value}, Target) -> Value - Target. human_path(<<"humn">>, _State, Path) -> lists:reverse(Path); human_path(Name, Monkeys, Path) -> #{Name := Node} = Monkeys, case Node of Value when is_integer(Value) -> not_found; {Op, Left, Right} -> case human_path(Left, Monkeys, [{left, Op, eval(Right, Monkeys)} | Path]) of not_found -> human_path(Right, Monkeys, [{right, Op, eval(Left, Monkeys)} | Path]); Found -> Found end end. %% Parse Integer standard code 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}.