ShareMyScreen/AdventOfCode/2022/11/MonkeyInTheMiddle

From J Wiki
Jump to navigation Jump to search

The Problem << >>

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.