EatDrink.ash: Optimize your daily diet (and see how your old diet stacks up).

Regarding maximizing marginal value vs maximizing total value, wouldn't A* over 3 dimensions work? My guess (from houeland suggesting extra-greasy sliders) is that houeland is doing something along those lines, though I guess it could be using good heuristics to generate a set of potentials and comparing the options. (I also may be underestimating the growth of the search space, or missing some subtlety that invalidates A*'s guarantees.)

I think he's using some variant of https://en.wikipedia.org/wiki/Knapsack_problem#Unbounded_knapsack_problem, from reading the javascript.
Of note: houeland "only" solves the problem for conditions where you have unlimited access to everything at (relatively) unchanging prices (maybe with some exceptions). EatDrink has to contend with changing prices and supplies running out.
 
Aside (in value()):
Code:
  if (breakfast)
  {
    advscore = ((averange(con.adv) + (3 * full)) * VALUE_OF_ADVENTURE);
    statscore *= 3;
  }

Why is advscore not divided by full when eating first meal of day as appropriately-skilled jarlsberg? It's then added with statscore, which still is per-full, so you get unit mismatch, at the very least. Should that part be just "advscore += 3*VALUE_OF_ADVENTURE", because you get that much out of each fullness unit of the food item?



Also, huh at this in the final report(s):
Spent 18396 meat with additional expenditures of 8617 meat.
Field gar and milk are the 8617, but 5 lasagna @ 4599 with none owned is 22995, why is it counting only 4 (4*4599=18396)?
Also, why is it not counting the chocolates as spent? It is definitely including them in adventures gained.

Spent 150500 meat with additional expenditures of 625 meat.
150500=21500*7, so it didn't count the snow crab as spent. That is probably wrong even though owned=9, because "Price will be a factor if you own it already."
And the chocolates are also not counted as spent, again.
 
We don't divide by size because as AoJ breakfast, the bigger, the better. Let's use non-AoJ food to make our examples using above.
Snowcrab is 6+(1*3)=9 adventures. The value of it would be 9*3000=27000-3395=23605 meat.
Tin cup is 17+(2*3)=23 adventures. The value of it would be 23*3000=69000-21500=47500/2=23750 meat per fullness.
Notice that the value-difference is the same? They're still 145 apart, just like before. Which would be great, if we can have breakfast for every food item. But we can't. So if we picked the snow crab over the tin cup, we're not just losing the 2.5 average adventures, but 5.5 adventures, because we picked a 1-point smaller first-item. That's a bad choice.
That's why we don't divide again. Because the size of the item actually matters.

Spent only refers to spent meat. As in difference of my_meat() before and after. This is important for fistcore, stepmeat, and other low-meat runs. Total meat value is used by budget and other such things. But that's unrelated to the spent meat. Because it's not actually spent.

As has been written before, if you set pre-owned item price to not be a factor, it has a price of 0. So rather than having a value of 14815, it would have had a value of 18000, because 6*3000=18000.

Chocolate code wasn't actually mine or added during my tenure as maintainer. Someone (frono?) added Bale's chocolate code to ED back 4 or 5 years ago. Since it's external code that simply got plugged in, it makes sense that it doesn't have the same checks or reporting. It might get updated later. Maybe. But things like why the basic checks aren't accurate anymore would definitely need to be sorted out first before I'd worry about tweaking aftercore-only stuff.
 
We don't divide by size because as AoJ breakfast, the bigger, the better. Let's use non-AoJ food to make our examples using above.
Snowcrab is 6+(1*3)=9 adventures. The value of it would be 9*3000=27000-3395=23605 meat.
Tin cup is 17+(2*3)=23 adventures. The value of it would be 23*3000=69000-21500=47500/2=23750 meat per fullness.
Notice that the value-difference is the same? They're still 145 apart, just like before. Which would be great, if we can have breakfast for every food item. But we can't. So if we picked the snow crab over the tin cup, we're not just losing the 2.5 average adventures, but 5.5 adventures, because we picked a 1-point smaller first-item. That's a bad choice.
That's why we don't divide again. Because the size of the item actually matters.

(I think you forgot to explicitly note a very important point in that argument: if we are doing "breakfast", then ALL food is getting recalculated like that, which means that it is never compared to per-fullness values)
But then you should also recalculate cost as full, and not per-fullness. Adding together values with different units just screams BUG.
But then, it's only jarlsberg food competing against other jarlsberg food, so cost is probably zero, or close to zero.

Spent only refers to spent meat. As in difference of my_meat() before and after.

We are quoting a simulation only run. There is no meat actually spent. The value reported is calculated from the consumption plan, and it appears to be calculated wrong.

As has been written before, if you set pre-owned item price to not be a factor,

Irrelevant, because "Price will be a factor if you own it already.".



(I still can't see how it would overshoot the value calculation like that, unless value of substates somehow changed from 0? Why did tin cup overshoot by almost 4k, when snow crab overshot only by 210 and lasagna 33???)
 
So, some math.
Let's compare two simple consumption plans for food with jarlsberg breakfast.
Plan 1: one big food, size F+G, quality A
Plan 2: one smaller food, size F, quality B; and something to fill the remaining size G with average quality C
(also assume that everything is free, to simplify things - does EatDrink assign meat cost to jarlsberg food?)
(and that F>=1, G>=1, so that we are not eating "empty" foods)

Plan 1 yields (A+3)*(F+G) adventures
Plan 2 yields (B+3)*F+C*G adventures

Now what does each of the two heuristics do when picking the first food, and when does it fail.

A) assuming value just increases by 3
gives plan 1 value A+3
gives plan 2 value B+3
Is wrong (picks the wrong plan) when

a1) picks plan 1 when it should pick plan 2: A+3>B+3 and (A+3)*(F+G)<=(B+3)*F+C*G
which is:
Code:
B+3 < A+3 <= ((B+3)*F+C*G)/(F+G) // assumes F+G>0, which it probably is, with F and G being sizes of foods
*(F+G): (B+3)*(F+G) < (B+3)*F+C*G
(B+3)*G < C*G
B+3 < C // when G>0, which it is
which together gives A>B and B<C-3
.. but that means it could be even better to reorder plan 2 into ...

plan 3: size G with average quality C (and the bonus breakfast +3 for at least the first full), then size F with quality B
(actual yield >= G*C+F*B+3 >= , heuristic value >=C+3>B+6)
So to be wrong, this heuristic must also pick plan 1 over plan 3 (which means A>C). Worst case for me:
Code:
B<A, B<C-3; C<A, (A+3)*(F+G)<=G*C+F*B+3
// use C<A, then B<C-3; expanding 0=3-3 and -3=3-6 along the way
(C+3)*(F+G) <= G*C+F*B+3 = G*(C+3)-3*G+F*B+3 <= G*(C+3) - 3*G +F*(C-3)+3 = G*(C+3) - 3*G + F*(C+3) - 6*F +3
// (C+3)*(F+G) cancels out
0 <= -3*G - 6*F + 3
G+2*F <= 1
But G and F are both at least 1, so G+2*G>=3>1.
That looks a lot like "can't happen". Which, in turn, looks a lot like "I must have made an error somewhere". I will look for it later.
On the other hand, it's not *that* surprising - I started with the bigger food being higher quality, and concluded that plan 2 should start with low quality food and end with higher quality food, contradicting the way EatDrink works.

or a2) picks plan 2 when it should pick plan 1: A+3<=B+3 and (A+3)*(F+G)>(B+3)*F+C*G
Code:
(B+3)*(F+G)>=(A+3)*(F+G)>(B+3)*F+C*G
(B+3)*G>=C*G
B+3>C
So A+3<=B+3<C - the big food is not better than the leading small food, and there is nothing left to fill the remaining space that would approach the leading small food in quality (by more than 3 whole points of quality).
Cursory glance at jarlsberg food tells me that this is never the case. Bigger foods have higher quality.

B) valuing everything at (adventures+3*fullness)*VOA
(I will factor out the VOA, because I assumed zero costs, because jarlsberg)

gives plan 1 value (A+3)*(F+G)
gives plan 2 value (B+3)*F

(is actually obviously correct in practice, because this is jarlsberg food and therefore A>B and A>C)

Is wrong when:

b1) picks plan 1 when it should pick plan 2: (A+3)*(F+G)>(B+3)*F, (A+3)*(F+G)<=(B+3)*F+C*G
Code:
(B+3)*F<(A+3)*(F+G)<=(B+3)*F+C*G
0<C*G
That doesn't really say much.
OK, let's also assume B=C+1=A+4, then plan 2 should definitely be picked. To make this heuristic pick plan 1 instead, we have
Code:
(A+3)*(F+G)>(A+7)*F
(A+3)*G>4*F
A>4*F/G-3
And with an even split (F=G), this is A>1, so ... it will always be wrong under those "simple" conditions.
In fact, even with F=5, G=3, this would be A>20/3-3=3.66 (and would mean that the big food is size at least 8, which probably makes


or b2) picks plan 2 when it should pick plan 1: (B+3)*F>(A+3)*(F+G), (B+3)*F+C*G<=(A+3)*(F+G)
This should be impossible. Increasing a value shouldn't decrease it.
Code:
(B+3)*F+C*G<=(B+3)*F
C*G<=0 // contradicts basic truths about food - namely that both size and quality are positive numbers
OK.

----
Summary:
neither method is wrong in jarlsberg.
 
Last edited by a moderator:
(I think you forgot to explicitly note a very important point in that argument: if we are doing "breakfast", then ALL food is getting recalculated like that, which means that it is never compared to per-fullness values)

*Sighs* No. You're working on some wrong assumptions, unless something changed since I last ran AoJ.
Look again at the code you quoted. It notes breakfast. And breakfast is defined as:
Code:
      breakfast = (my_class() == $class[Avatar of Jarlsberg] && iteration == 1 && have_skill($skill[The Most Important Meal]) && get_fullness() == 0 && count(position) == 0);
Or, for people who don't like to read code, only applying to AoJ with TMIM with the item being being in the first position for food, while no other food has been consumed or queued. As soon as any other food has been selected and queued, it is no longer breakfast...

Anyways, we've wandered into the weeds and are no longer actually dealing with the script and useful information. At this point, we're just frustrating each other, since we apparently disagree on whether a 22.5 adventure meatloaf followed by an 8 adventure ESS for 30.5 total adventures is better than a 20 adventure egg salad sandwich followed by a 7.5 adventure ML for 27.5 total adventures if both will definitely be consumed. :)
 
You could have pointed out the part that's actually wrong, instead of "proving" that if(breakfast) doesn't mean if(breakfast).
 
Last edited:
Well, breakfast is defined as only being AoJ with TMIM and no fullness and no items queued. As soon as an item is queued, breakfast should return false, and the boosted-adventure calculation that prefers larger items earlier is no longer used. Since most people will try for epic UBS, LBP, or OS for their 56 adventures, it's not likely that the lower calculation matters much... but for newbies just starting their AoJ series, the choice of what to consume first may actually come into play.

It's possible that the un-breakfast triggers too fast. I noted that it un-breakfasts at the start of the queuing process. But that wouldn't be related to what you noted, because if that is the case, then it means that it simply wipes the breakfast calculations immediately and uses the regular calculations instead. We'd need to test that with a limited set of items available, as with the ones I mentioned. But... if that is the case, it's that the breakfast piece you mentioned NEVER happens, not that it happens too much. :)
 
Does simulation ignore the current class? I am a Sauceror with Saucemaven, but EatDrink.ash is reporting the following:
Code:
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:13837
By my calculation, the adv should be 34.5. (In fact, by my calculation the value should be 13,760 when adv is 24.5 and meat is 4698.)
As a side-note, Mafia reports 28.5 adv from hot hi mein in the food section of the Item Manager, which not only seems incorrect but also differs from EatDrink.ash. houeland seems to be the only one using 34.5 adv for hi mein, calculating 104 adv total from 3 hot hi mein with Saucemaven as a Sauceror (without milk.)

Looking closer at the output, the valuation seems really weird. The value of hot hi mein drops the further into EatDrink.ash it gets?
Code:
Refreshing stash contents...
Stash list retrieved.
Internal checkpoint created.
Loading favorite consumables from user settings...
adding favorite: hot hi mein
Starting EatDrink.ash (version 3.2).
Consuming up to 15 food, 0 booze, and 0 spleen
Considering food from inventory closet Hagnk's Coinmasters NPCs the mall. Per-item budget cap is 25000.0.
Retrieval cap is 20000. Price will be a factor if you own it already.
An adventure has the value of 3000 meat. Mysticality subpoint is 0.0. Nonprime stat subpoint is 0.0.
Simulating only; no purchases or food/drink/spleen consumption.
Pass 1: food.
food: At 0, consuming to 15 with 5302129 meat.
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:13837
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:13837
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:13837
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:13837
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:13837
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:13837
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:13837
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:13837
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:13837
Favorite hot hi mein is too fattening (5.0)- removing from consideration.
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:12205
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:12205
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:12205
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:12205
Favorite hot hi mein is too fattening (5.0)- removing from consideration.
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:10574
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:10574
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:10574
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:10574
Favorite hot hi mein is too fattening (5.0)- removing from consideration.
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:8942
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:8942
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:8942
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:8942
Favorite hot hi mein is too fattening (5.0)- removing from consideration.
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:7310
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:7310
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:7310
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:7310
Favorite hot hi mein is too fattening (5.0)- removing from consideration.
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:5679
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:5679
Fav: <b>hot hi mein</b> lev:7 gain:5.0 adv:24.5 musc:73.0 myst:17.5 mox:32.5 meat:4698 own:91 value:5679
Favorite hot hi mein is too fattening (5.0)- removing from consideration.
simulating retrieval of one long pork lasagna.
simulating retrieval of one long pork lasagna.
simulating retrieval of one long pork lasagna.
simulating retrieval of one long pork lasagna.
simulating retrieval of one long pork lasagna.
0: milk of magnesium price: 619 value: 44381
0: potion of the field gar price: 7850 value: 67150
1: long pork lasagna lev:10 gain:3.0 adv:19.0 musc:0.0 myst:50.5 mox:0.0 meat:5700 own:11 value:17268
2: long pork lasagna lev:10 gain:3.0 adv:19.0 musc:0.0 myst:50.5 mox:0.0 meat:5700 own:11 value:17268
3: long pork lasagna lev:10 gain:3.0 adv:19.0 musc:0.0 myst:50.5 mox:0.0 meat:5700 own:11 value:17268
4: long pork lasagna lev:10 gain:3.0 adv:19.0 musc:0.0 myst:50.5 mox:0.0 meat:5700 own:11 value:17268
5: long pork lasagna lev:10 gain:3.0 adv:19.0 musc:0.0 myst:50.5 mox:0.0 meat:5700 own:11 value:17268
Pass 2: booze.
Skipping booze.
Pass 3: spleen.
Skipping spleen.
Pass 4: booze.
Skipping booze.
choc: Checking non-filling crimbo chocolates - all 3 kinds
simulating retrieval of one choco-Crimbot.
1: choco-Crimbot lev:0 gain:1.0 adv:5.0 musc:0.0 myst:0.0 mox:0.0 meat:7700 own:0 value:7300
simulating retrieval of one chocolate saucepan.
2: chocolate saucepan lev:0 gain:1.0 adv:2.0 musc:0.0 myst:0.0 mox:0.0 meat:3210 own:0 value:2790
Best find was chocolate saucepan with a value of -210. That's no good, so not consuming and moving on.
Finished.
Spent 28192 meat with additional expenditures of 8469 meat. Gained Fullness: 15. Inebriety: 0. Spleen: 0.
Adventures: 117. Muscle: 0. Moxie: 0. Mysticality: 250.
Eating, drinking, and spleening complete. Commence merrymaking (at your own discretion).
 
Last edited:
Does simulation ignore the current class?
It certainly takes class into account.
Code:
    if ($items[cold hi mein, hot hi mein, sleazy hi mein, spooky hi mein, stinky hi mein, hell ramen, fettucini Inconnu, gnocchetti di Nietzsche, spaghetti with Skullheads, spaghetti con calaveras] contains con.it && have_skill($skill[saucemaven]))
    {   
        if (my_class() == $class[Sauceror] || my_class() == $class[Pastamancer])
        {
                con.adv.min += 10;
                con.adv.max += 10;
        }
        else
        {
                con.adv.min += 5;
                con.adv.max += 5;
        }
    }
 
Where is that? I searched for both "saucemaven" and "hot hi mein" in EatDrink.ash and found no hits. I also verified that the svn checkout is up to date and has no local modifications.
 
Regarding that, it runs 5 valuations with an 1/9th drop in your VoA each time. So 9/9, 8/9, 7/9, 6/9, 5/9. So with your 3000 VoA, you'd then compare against 2.6k, 2.3k, 2k, and 1.6k as your final. Sometimes dropping the VoA makes it pick items which actually end up giving you more adventures.
 
Ah, looks like this was edgy's hardcoded bit from post 1844.

We were waiting for mafia to add official support first... We noted an issue with get_ingredients where there was a bug/feature where mafia wouldn't actually report ingredients accurately if you didn't have the skills to create the item yourself - post 1845. I don't think we ever got an official response on that, so I'll probably just add it and let the new bug reports hit again.
 
Last edited:
Ah, looks like this was edgy's hardcoded bit from post 1844.

We were waiting for mafia to add official support first... We noted an issue with get_ingredients where there was a bug/feature where mafia wouldn't actually report ingredients accurately if you didn't have the skills to create the item yourself - post 1845. I don't think we ever got an official response on that, so I'll probably just add it and let the new bug reports hit again.
Drat. I thought I could quote a bit of code without being wrong. I totally forgot I'm not on the SVN until it updates.
 
Hey, at least it's easier than the previous thing I noted. Unfortunately, I think that the code checked in is wrong. The condition is not simply whether a scrumptious reagent was involved. According to the wiki both Hell broth and fleetwood mac 'n' cheese are not affected by saucemaven.

In fact, the code is not working for hot hi mein for me. It may be related to:
Code:
> ash get_ingredients($item[hot hi mein])

Returned: aggregate int [item]
dry noodles => 1
hot and sour sauce => 1
MSG => 1
Even if checking the ingredients was correct, it looks like the check would have to be recursive on all ingredients. But as I mentioned above, I don't think that this is a valid check to do.
 
Hardcoded list it is then. Until mafia actually notices that the passive skill has an effect and accurately shows the adventures, at which point we remove our hack-code. :)
 
Until mafia actually notices that the passive skill has an effect and accurately shows the adventures, at which point we remove our hack-code. :)
That is working properly. Sorry for saucemaven being so annoying.
Yeah.

This is the kind of talk that inspires me to (usually) simply ignore what Theraze posts, unless & until somebody else steps up and gives real data and reproducible use cases that point to a KoLmafia bug, rather than just vague anecdotes.
 
Back
Top