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
Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!
Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.
Top comments (0)