soolar / sl_ascend

An ascension script for kolmafia
11 stars 5 forks source link

[experimental] Automatically identify efficient food and drink in TCRS #275

Closed jaspercb closed 4 years ago

jaspercb commented 4 years ago

Planning to test this later today, posting now as a request for comment. This is a big change.

The core of this is a bog-standard dynamic programming implementation of a knapsack solver (see https://medium.com/@fabianterh/how-to-solve-the-knapsack-problem-with-dynamic-programming-eb88c706d3cf for an intuitive explanation). As implemented, the solver has no food-specific code in it, which means it's pretty easy to adjust the input arrays to account for Microbrewery drinks (since microbrewery drinks aren't in Mafia as items), which I think is pretty awesome.

This knapsack-solver is then used to implement the following turngen flowchart in TCRS:

If we're below 10 adventures, drink the most efficient thing we can, up to about 10 drunkenness or so.

If we're beyond 8 drunkenness, acquire milk of magnesium (optionally) and use the new knapsack solver to pack our stomach with maximal efficiency. (TODO: If we're not using Milk of Magnesium in TCRS, there's no need to do all our stomach-filling at the same time.)

If we're beyond 8 drunkenness and full on stomach, knapsack-pack our drunkenness.

There's a subtle bug here with the implementation, where if we have (for example) only one perfect ice cube and 6 organ space to fill, if the "perfect" organs are optimal we'll try to use two perfect boozes. I have code that catches this (and so avoids halfway packing organ space), but not code that solves the issue - I see a few ways to solve it, but am very willing to procrastinate solving it until users complain. Right now we just skip eating/drinking, and if we run out of adventures before finding something that makes the problem unambiguous, then people can figure it out manually.

Not implemented yet:

Please don't comment (yet) on:

Phate4569 commented 4 years ago

If you are tackling eating, I made this awhile back to automate one of the more annoying parts of it in TCRS. It works GREAT with Special Seasoning. The only manual thing I do for eating anymore is astral dogs after level 11.

https://kolmafia.us/showthread.php?23874-2CRS-Catsup-Ketchup-eating-script

jaspercb commented 4 years ago

Yup, I saw that - it's a neat script. This is written to be a little more general - it doesn't have to be told "ketchup is very good in this class/moon combo", and will also automatically scan your inventory and acquirable items to figure out if (say) a specific pasta dish is the best way to finish off your 3 fullness instead of using an even-numbered epic catsup and then being confused about how to finish off the last 1 fullness.

I think of this as being a general-purpose in-run "diet maximizer". Since new IOTMs seem less generous with high-turngen consumables, maintaining this general-purpose system will be easier than hard-coding a specific diet plan for each of the 54 class/sign combinations (each of which might be invalidated as new consumables come along).

jaspercb commented 4 years ago

Oh, one more TODO: if we have Stooper, we should knapsack-pack our inebriety from 8+ to 16 instead of from 8+ to 15, and just skip the last booze returned. When we run out of adventures for the last time, we can automatically stooper, knapsack-pack the remaining single drink, and possibly even automatically nightcap (sample code for picking the best nightcap was just added, although it ignores cafe since it's just a proof-of-concept).

Phate4569 commented 4 years ago

Oh I get that, I guess I buried the lede. "It works GREAT with Special Seasoning." The best part of the script is that with 1 fullness items I (usually) use up all of my Special Seasoning. With special seasoning you can maximally get +15 adv per day, by eating all "small" foods. It also leads to weird rare cases where consuming + items of a lower quality ~ or better than eating items of a higher quality, especially when hardcoded items (like astral dogs) come into play. This is especially important in TCRS, where EPIC quality food is pretty rare.

For example, a path I recently played had Dogs @ 4 fullness. this reduced our expected adv gain to (((20+24)/2)/4)=5.5+1(Special seasoning)=23, whereas gorging myself on catsup/ketchup/lukewarm tea gives me (5 x 4)+(1 x 4)=24. With both 5 and 5.5 being averages the catsup's average adventures per fullness is better.

Additionally there is the Black label which functions similarly for base boozes. With Scurvy Prevention making rum so easily obtainable, and base boozes being found in the tavern cellar, it can very easily happen were even a good booze > an EPIC booze of the same size.

Finally, are you planning on using duffel bags and van keys before your calculations? Those have been a very valuable source of nighcaps or EPIC boozes for me (foods not so much).

jaspercb commented 4 years ago

As far as I know, as written this knapsack solver isn't able to deal with things like "the first seven things you consume have +1". I think the following logic will be roughly similar:

If we're looking at a 1-fullness item, and we have 15 fullness left and 8 special seasonings, I'm going to add 8/15 to the adventure value of that item (since if we only ate that item, we would run out of special seasoning and get 8 extra adventures).

If we're looking at a 2-fullness item, and we have 15 fullness left and 8 special seasonings, I'm going to add 1 to the adventure value of that item (since if we only ate that item, we wouldn't run out of special seasoning and would get 7 extra adventures).

Phate4569 commented 4 years ago

Makes sense. Black label is probably the worse one to consider since it is a wider variance in turngen, uses a smaller set of items, and is less applicable (I've only gotten 1-3 in a normal run).

Other things I was thinking of about this while doing turns:

Actually, you may want to consider disallowing ANY items that give effects. If an item does something like damaging attacking enemies, or dealing damage every turn, then you get into the situation like in the recent bug report where the script dumps dozens of adventures in the Junkyard because it keeps killing gremlins.

jaspercb commented 4 years ago

Magical sausages are unlikely to be picked in TCRS, since they're bad. I don't think they need to be specifically restricted.

I think KGB martinis are a pretty minor factor and am currently happy to ignore it.

Tuxedo shirts are a neat observation. Hm. Will give that some thought, but I don't think it'll be in this PR.

I think the best way to handle the Junkyard is to check in presool if we have any effects which are massively damaging, and removing them if so.

Phate4569 commented 4 years ago

About the sausages, all of these will only give you 1 adv:

rugrat@KipLaptop:~/.kolmafia/data$ grep "10060" TCRS_* | grep -e "awesome" -e "EPIC"
TCRS_Accordion_Thief_Marmot.txt:10060   enchanted tasty sausage 4   awesome Effect: "Thanksgot", Effect Duration: 40
TCRS_Accordion_Thief_Mongoose.txt:10060 toothsome sausage   3   awesome 
TCRS_Accordion_Thief_Packrat.txt:10060  super-sized fancy delicious sausage 5   awesome Effect: "Tightly-Wound Spine", Effect Duration: 40
TCRS_Accordion_Thief_Vole.txt:10060 delicious narrow sausage    3   awesome 
TCRS_Disco_Bandit_Vole.txt:10060    massive delicious sausage   6   awesome 
TCRS_Disco_Bandit_Wombat.txt:10060  toothsome sausage   3   awesome 
TCRS_Sauceror_Platypus.txt:10060    delicious sausage   3   awesome 
TCRS_Sauceror_Wombat.txt:10060  tasty sausage   3   awesome 
TCRS_Seal_Clubber_Platypus.txt:10060    super-sized yummy sausage   5   awesome 
jaspercb commented 4 years ago

In Mafia, with TCRS data loaded, $item[magical sausage].adventures should evaluate to 1.

Phate4569 commented 4 years ago

Ah, I did not realize I thought it was calculating. Good to know. Sorry.

Phate4569 commented 4 years ago

Sorry I had to check. I'm in TCRS SC/Oppossum:

> ash print($item[magical sausage].adventures);

20
Returned: void

> ash print($item[magical sausage]);

magical sausage
Returned: void

> ash print($item[magical sausage].fullness);

5
Returned: void
jaspercb commented 4 years ago

My bad! Will fix. I'll chuck it in expectedAdventuresFrom