I spent way too much time at the beginning trying to be clever about parsing the input. The Regex named captures work great, but I also tried to dynamically create the functions using metaprogramming. Eventually I gave up on that and hard-coded the function combinations.
After constructing three neat layers of reduces for the rounds, monkeys, and items, I realized that the state needed to be modified within the round so that monkeys could throw to other monkeys coming later in the round. This is a prime example where a functional paradigm is actually less intuitive. I saw that one person on the Elixir forum used GenServers, which is a perfect way of thinking about state a little more procedurally. I opted for smushing the round and monkey reduces into a single reduce so that they could share the same accumulator.
Admittedly, I did not figure out the trick to the second part on my own. My brain was headed in the correct direction, but I didn't really want to spend my time figuring out a math problem. (Usually, I love doing math, but my primary goal has been to keep up with these Advent of Code problems.)
defmoduleDay11douseAOCdefnew("old","+","old"),do:fnold->old+oldenddefnew(left,"+","old"),do:fnold->String.to_integer(left)+oldenddefnew("old","+",right),do:fnold->old+String.to_integer(right)enddefnew("old","*","old"),do:fnold->old*oldenddefnew(left,"*","old"),do:fnold->String.to_integer(left)*oldenddefnew("old","*",right),do:fnold->old*String.to_integer(right)enddefparse_note(note)doinfo=Regex.named_captures(~r/Monkey (?<monkey>\d):
Starting items: (?<items>.*)
Operation: new = (?<left>.*) (?<op>.*) (?<right>.*)
Test: divisible by (?<test>.*)
If true: throw to monkey (?<throw_true>.*)
If false: throw to monkey (?<throw_false>.*)/,note){String.to_integer(info["monkey"]),%{count:0,items:info["items"]|>String.split(", ")|>Enum.map(&String.to_integer/1),operation:new(info["left"],info["op"],info["right"]),test:fntest->ifrem(test,info["test"]|>String.to_integer)==0doinfo["throw_true"]|>String.to_integer()elseinfo["throw_false"]|>String.to_integer()endend}}enddefnotes()doinput(11)|>String.split("\n\n",trim:true)|>Map.new(&parse_note/1)enddefpart1donotes=notes()Enum.reduce(1..(20*map_size(notes)),notes,fni,monkeys->current_monkey=rem(i-1,map_size(notes))%{items:items,operation:operation,test:test}=monkeys[current_monkey]Enum.reduce(items,monkeys,fnitem,state->new_worry_level=operation.(item)|>div(3)state|>update_in([test.(new_worry_level),:items],fnitems->items++[new_worry_level]end)|>update_in([current_monkey,:items],fn[_|items]->itemsend)|>update_in([current_monkey,:count],fncount->count+1end)end)end)|>Enum.sort_by(fn{_,%{count:count}}->countend,:desc)|>Enum.take(2)|>Enum.reduce(fn{_,%{count:first_count}},{_,%{count:second_count}}->first_count*second_countend)enddefpart2donotes=notes()Enum.reduce(1..(10_000*map_size(notes)),notes,fni,monkeys->current_monkey=rem(i-1,map_size(notes))%{items:items,operation:operation,test:test}=monkeys[current_monkey]Enum.reduce(items,monkeys,fnitem,state->new_worry_level=operation.(item)|>rem(9699690)state|>update_in([test.(new_worry_level),:items],fnitems->items++[new_worry_level]end)|>update_in([current_monkey,:items],fn[_|items]->itemsend)|>update_in([current_monkey,:count],fncount->count+1end)end)end)|>Enum.sort_by(fn{_,%{count:count}}->countend,:desc)|>Enum.take(2)|>Enum.reduce(fn{_,%{count:first_count}},{_,%{count:second_count}}->first_count*second_countend)endend
Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.
Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.
👥 Ideal for solo developers, teams, and cross-company projects
Top comments (0)