ShareMyScreen/AdventOfCode/2022/11/MonkeyInTheMiddle
Another simulation, this time with squirrely input. I will need to write a verb to convert each monkey's information.
What is that information? It looks like it can be described by 5 boxes per monkey, holding
list of items;phrase to modify the worry level;divisor;(divisibletarget,nondivisibletarget);nhandled
Each monkey's data is a string, and I will write a verb to process one such string, using the taketo and related string functions. I will use (x u;.1 y) to process the individual strings. (x u;.1 y) is like the u;._2 y that I have been using but I delimit the strings myself.
require 'strings' monkeydata =: {{ NB. y is the string for one monkey items =. ". LF taketo 'items:' takeafter y NB. list of items worrystg =. LF taketo 'new = ' takeafter y NB. update phrase divisor =. ". LF taketo 'divisible by ' takeafter y NB. divisor targ0 =. ". LF taketo 'true: throw to monkey ' takeafter y NB. divisible target targ1 =. ". LF taketo 'false: throw to monkey ' takeafter y NB. indivisible target items ; worrystg ; divisor ; (targ0 , targ1) ; 0 NB. init nhandled to 0 }} ]data =: (monkeydata;.1~ 'Monkey'&E.) LF ,~ wd 'clippaste' +-----------+---------+--+---+-+ |79 98 |old * 19 |23|2 3|0| +-----------+---------+--+---+-+ |54 65 75 74|old + 6 |19|2 0|0| +-----------+---------+--+---+-+ |79 60 97 |old * old|13|1 3|0| +-----------+---------+--+---+-+ |74 |old + 3 |17|0 1|0| +-----------+---------+--+---+-+
Now to run the simulation. I need a verb to handle one item, a verb to handle one monkey, and a verb to handle one round. They are straightforward:
simitem =: {{ NB. y is monkey number old =. ((<y,0),<0) {:: data NB. the item to throw - named to match the update string data =: (<y,0) (}.&.>@{)`[`]} data NB. remove the item from the thrower old =. <. 3 %~ ". (<y,1) {:: data NB. execute the update string, divide by 3 throwto =. (* ((<y,2){::data) | old) { ((<y,3){::data) NB. the monkey to throw to, depending on divisibility data =: (<throwto,0) (old ,~&.> {)`[`]} data NB. Throw the item to the selected monkey data =: (<y,4) (>:&.>@{)`[`]} data NB. increment the thrower's count of items handled i. 0 0 }}
This is where the work is done. x {:: y fetches from a nested box structure. For this use x has a list of 2 boxed selectors: ((<y,0),<0). The first selector (<y,0) is a standard selector as used in x { y; the boxed list signifies multidimensional indexing: I am taking monkey# y, column 0 (the items). After dropping into that box the next selector (<0) takes the first item in the list; that is, the first item for monkey y.
Updating the data array is a bit cumbersome and I use the form x V0`V1`V2} y which executes as (x V0 y) (x V1 y)} (x V2 y). x has the indexes I want to modify, and replacing [ and ] this becomes (x modifyingverb@{ y) x} y; that is, select from y, modify the selection, and put the modified value back into the same place in y. The selection here is boxed so I make the modification inside the box using &.>.
simmonkey =: {{ NB. y is monkey number while. # (<y,0){::data do. simitem y end. NB. throw items as long as there are any i. 0 0 }} simround =: {{ NB. nilad simmonkey"0 i. #data i. 0 0 }} simround^:20 '' NB. simulate 20 rounds data NB. See where we ended up +-----------------+---------+--+---+---+ |10 12 14 26 34 |old * 19 |23|2 3|101| +-----------------+---------+--+---+---+ |245 93 53 199 115|old + 6 |19|2 0|95 | +-----------------+---------+--+---+---+ | |old * old|13|1 3|7 | +-----------------+---------+--+---+---+ | |old + 3 |17|0 1|105| +-----------------+---------+--+---+---+
The answer is the highest two values in the last column, multiplied together.
*/ 2 {. \:~ > {:"1 data 10605
In part 2, I'll have to use modular arithmetic. That old*old will get to be too big even for extended precision.
But what modulus? Some monkeys use one, some another.
Oh dear, I'll have to use them all! Maybe I should turn each item into a list, one for each modulus. I'll just pick the one to use for the monkey being processed.
No, what am I thinking? The modulus doesn't have to be each monkey's modulus exactly, just a multiple of each, small enough to keep the numbers under control. I'll use the product of the monkeys' moduli. The change to simitem is just one line.
allmoduli =: */ > 2 {"1 data NB. modulus to use - product of individual moduli simitem =: {{ NB. y is monkey number old =. ((<y,0),<0) {:: data NB. the item to throw - named to match the update string data =: (<y,0) (}.&.>@{)`[`]} data NB. remove the item from the thrower old =. allmoduli | ". (<y,1) {:: data NB. execute the update string, apply modulus throwto =. (* ((<y,2){::data) | old) { ((<y,3){::data) NB. the monkey to throw to, depending on divisibility data =: (<throwto,0) (old ,~&.> {)`[`]} data NB. Throw the item to the selected monkey data =: (<y,4) (>:&.>@{)`[`]} data NB. increment the thrower's count of items handled i. 0 0 }}
Time to run it.
simround^:10000'' data +-----------------------------+---------+--+---+-----+ |63602 56040 11941 10573 61607|old * 19 |23|2 3|52166| +-----------------------------+---------+--+---+-----+ |90861 86149 27648 21340 76915|old + 6 |19|2 0|47830| +-----------------------------+---------+--+---+-----+ | |old * old|13|1 3|1938 | +-----------------------------+---------+--+---+-----+ | |old + 3 |17|0 1|52013| +-----------------------------+---------+--+---+-----+ */ 2 {. \:~ > {:"1 data 2713310158
Yes, that's it.