Feature - Implemented extend coverage of autoBuyPriceLimit

roippi

Developer
Hypothetical: I have autoBuyPriceLimit set to 10000.

Situation A: I try to "acquire" a creation that requires one item costing 50k.
Situation B: I try to "acquire" a creation that requires 50 items costing 1k.

Current behavior: autoBuyPriceLimit kicks in and stops Situation A, but will allow Situation B to purchase its items.

Proposed behavior: autoBuyPriceLimit either stops Situation B from purchasing any items (personally preferred), or alternately stops Situation B from purchasing more than 10k worth of items.

Now that mafia will consider acquiring MUSE creations by making them from components, I feel this protection is necessary. If I type in "acquire brga", for example, mafia may decide to spend 1.6 million meat on bricko bricks (current mall prices) to make me a gargantuchicken.
 

roippi

Developer
Proposed patch attached. Not committing it right off the bat as even a marginal change to PurchaseRequest seems particularly vulnerable to the law of unintended consequences.

Code:
> acquire bricko pants

Searching for "BRICKO pants"...
Search complete.
Searching for "BRICKO brick"...
Search complete.
Verifying ingredients for BRICKO pants (1)...
Using cached search results for BRICKO brick...
Stopped purchasing BRICKO brick; purchase total: 2,509 exceeds autoBuyPriceLimit: 1,000.
Using cached search results for BRICKO brick...
Stopped purchasing BRICKO brick; purchase total: 2,509 exceeds autoBuyPriceLimit: 1,000.
You need 9 more BRICKO brick to continue.

> buy bricko pants

Searching for "BRICKO pants"...
Search complete.
Purchasing BRICKO pants (1 @ 2,799)...
You acquire an item: BRICKO pants
Purchases complete.
 

Attachments

  • autoBuyRequest.patch
    1.9 KB · Views: 35

slyz

Developer
I'm going to spend some time with the patch too. This way is much smarter than what I was thinking of (tallying the price of things in retrieve_item()).
 

slyz

Developer
First error that I don't usually have:
Code:
> ashq eat( 5, $item[ fishy fish lasagna ] );

Searching for "fishy fish lasagna"...
Search complete.
[color=red]Stopped purchasing fishy fish lasagna; purchase total: 41,148 exceeds autoBuyPriceLimit: 25,000.
You need 5 more fishy fish lasagna to continue.[/color]
I'm not sure enforcing autoBuyPriceLimit so strictly is good, but I think we need user input on this.
 

roippi

Developer
I'm not sure enforcing autoBuyPriceLimit so strictly is good, but I think we need user input on this.

Agreed on both parts.

I think that in the case you presented, eat() should behave like buy() in my bricko pants example above - it calls the overloaded version of retrieveItem with isAutomated=false, which bypasses the autoBuyPriceLimit check.
 

Theraze

Active member
Well, the way I'd see it as working 'proper' would be that each item is considered its own item, but it's still affected by aBPL. So eat fails if you try to eat two items worth over your autoBuyPriceLimit that you don't possess, but succeeds if they're in your inventory or the cost of one is less than your aBPL.

To say it differently,
retrieve_item(5, $item[fishy fish lasagna])
should succeed, as long as the price of one fishy fish lasagna is less than your aBPL. Also,
eat(1, $item[tube of cranberry Go-Goo])
should fail if your autoBuyPriceLimit is still the default of 20000 and the cheapest is over 21k and you don't have any available in inventory/storage/available locations.
 
Last edited:

Veracity

Developer
Staff member
Well, the way I'd see it as working 'proper' would be that each item is considered its own item, but it's still affected by aBPL. So eat fails if you try to eat two items worth over your autoBuyPriceLimit that you don't possess, but succeeds if they're in your inventory or the cost of one is less than your aBPL.
I agree with this, where the "cost" is the cheapest of the cost to buy outright or the (recursive) cost to craft from ingredients.
 

slyz

Developer
So, if we take the "eat 5 fishy fish lasagna" example, the cost of 1 fishy fish lasagna should respect the autoBuyPriceLimit, whether that cost comes from buying it directly from the mall, or buying all of its ingredients from the mall and then making it.

In that case, a more complicated implementation is in order. In the gory entrails of InventoryManager itself. Ugh.
 

Theraze

Active member
Exactly, which is the problem... alternatively, if the purchasing could reset when it's doing a new 'goal' item, that would work as well. But I didn't see anything in the patch that easily supported whether the item is the terminal goal or just a step along the way.
 

roippi

Developer
Yeah. Oh well, that's why I posted as a patch.

Time to put on my recursion-resistant boots and head on in.
 

roippi

Developer
Mmkay, different approach this time. This method should better handle those lovely recursive creation cases and extant code should handle cases where we decide to just buy the finished product.
 

Attachments

  • aBPLv2.patch
    2.8 KB · Views: 25

slyz

Developer
Here is a version of your patch that uses priceToMake() to get the average cost of making one item.

Code:
> set autoBuyPriceLimit = 900

autoBuyPriceLimit => 900

> set debugBuy = true

debugBuy => true

> acquire 5 olive lo mein

☯ olive (5) mall=350 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive (5) mall=350 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive stir-fry (5) mall=3000 make=1508
☯ dry noodles (5) onhand=4050
☯ olive (5) mall=350 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive stir-fry (5) mall=3000 make=1508
☯ dry noodles (5) onhand=4050
☯ olive lo mein (5) mall=5750 make=5596
☯ olive (5) mall=350 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive stir-fry (5) mall=3000 make=1508
☯ dry noodles (5) onhand=4050
[color=red]Price of components (1,119) for olive lo mein exceeds autoBuyPriceLimit (900)
You need 5 more olive lo mein to continue.[/color]

Here, the price of making an olive lo mein is valued at 1,119 meat, although I already have enough dry noodles in my inventory. That's because I have a "valueOfInventory" of 2.0. Mafia would have effectively spent 1,119 - 810 = 309 meat to make each lo mein.

When comparing the price to make to the user's autoBuyPriceLimit, I think we should value inventory as 0 meat. That means creating versions of priceToMake(), priceToAcquire() and itemValue() with a boolean mallPriceOnly parameter.
 

Attachments

  • aBPLv2_priceToMake.patch
    2.1 KB · Views: 20

slyz

Developer
And here is a version that only considers the average amount of meat that will be spend on each item when comparing to autoBuyPriceLimit:
Code:
> acquire 5 olive lo mein

☯ olive (5) mall=350 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive (5) mall=350 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive stir-fry (5) mall=3000 make=1508
☯ dry noodles (5) onhand=3745
☯ olive (5) mall=350 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive stir-fry (5) mall=3000 make=1508
☯ dry noodles (5) onhand=3745
☯ olive lo mein (5) mall=5750 make=5291
☯ olive (5) mall=350 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive stir-fry (5) mall=3000 make=1508
☯ dry noodles (5) onhand=0
[color=red]The amount of meat spend on components (309) for one olive lo mein exceeds autoBuyPriceLimit (300)
You need 5 more olive lo mein to continue.[/color]
In the last set of debugBuy output, dry noodles are considered as costing 0 (0 meat will be spend to acquire them), whereas before (when Mafia is trying to decide whether to buy or create), dry noodles are correctly valued at 1.0 * mallprice, as per my valueOfInventory preference.

After buying 3 olives (to reduce the average meat spend per item creation):
Code:
> buy 3 olive

Searching for "olive"...
Search complete.
Purchasing olive (3 @ 70)...
You acquire olive (3)
You spent 210 Meat
Purchases complete.

> acquire 5 olive lo mein

☯ olive (2) mall=245 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive stir-fry (5) mall=3000 make=1403
☯ dry noodles (5) onhand=3745
☯ olive (2) mall=245 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive stir-fry (5) mall=3000 make=1403
☯ dry noodles (5) onhand=3745
☯ olive lo mein (5) mall=5750 make=5186
☯ olive (2) mall=140 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive stir-fry (5) mall=3000 make=1298
☯ dry noodles (5) onhand=0
Verifying ingredients for olive lo mein (5)...
☯ olive (2) mall=245 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
☯ olive stir-fry (5) mall=3000 make=1403
☯ olive (2) mall=140 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
Verifying ingredients for olive stir-fry (5)...
Searching for "olive"...
Search complete.
Purchasing olive (2 @ 70)...
You acquire olive (2)
You spent 140 Meat
Purchases complete.
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ herbs (5) mall=320 make=2147483647
☯ spices (5) mall=835 make=2147483647
☯ secret blend of herbs and spices (5) mall=1120 make=1155
Searching for "secret blend of herbs and spices"...
Search complete.
Purchasing secret blend of herbs and spices (5 @ 224)...
You acquire secret blend of herbs and spices (5)
Purchases complete.
Creating olive stir-fry (5)...
You acquire olive stir-fry (5)
Successfully created olive stir-fry (5)
Creating olive lo mein (5)...
You acquire olive lo mein (5)
Successfully created olive lo mein (5)

Is this behavior satisfying? Could the error message be more explicit?

EDIT: I added a check for isAutomated too, so that the autoBuyPriceLimit check is bypassed for non-automated retrieveItem() calls. Although I don't think Mafia uses isAutomated = false anywhere at the moment.
 

Attachments

  • aBPLv3.patch
    4.4 KB · Views: 21
Last edited:

roippi

Developer
Cool, those functions are a lot more robust than what I had. Essentially same outcome, but better implementation.

When comparing the price to make to the user's autoBuyPriceLimit, I think we should value inventory as 0 meat.

Agreed, and I like the solution that you ended up at.

Is this behavior satisfying?

The only remaining issue I can see with this implementation is maybe some fringe cases where using the average meatSpend causes confusing behavior. Like, if I ask mafia to get two pumpkin pies and I only have one pumpkin on hand, it would see that the priceToMake of the first one is low, and the priceToMake of the second is high. (assuming pumpkin price of 10k) There's a window of aBPLs from 5k to 10k where it would reject buying a single pumpkin to make my pie, but be okay with acquiring one if I ask it to get two and already have one on hand. That's a little funky.

..Actually wait, I think the extant code in KoLMafia.java will still stop any single purchases that exceed the users aBPL. So that should be okay, I think. It's a little different behavior than when we stop the WHOLE acquisition process, as you can see:

Code:
> set autoBuyPriceLimit=6000

autoBuyPriceLimit => 6000

> closet put pumpkin

Placing items into closet...
Requests complete.

> acquire pumpkin pie

☯ Gnollish pie tin mall=100 make=2147483647
☯ flat dough mall=100 make=2147483647
Searching for "wad of dough"...
Search complete.
Searching for "flat dough"...
Search complete.
☯ pie crust mall=100 make=200
☯ pumpkin mall=9100 make=2147483647
Searching for "pumpkin pie"...
Search complete.
Searching for "pie crust"...
Search complete.
Searching for "Gnollish pie tin"...
Search complete.
☯ flat dough mall=100 make=100
☯ wad of dough mall=100 make=100
☯ pie crust mall=100 make=200
☯ pumpkin mall=9100 make=2147483647
☯ pumpkin pie mall=12770 make=9200
☯ Gnollish pie tin mall=100 make=2147483647
☯ flat dough mall=100 make=100
☯ wad of dough mall=100 make=100
☯ pie crust mall=100 make=200
☯ pumpkin mall=9100 make=2147483647
The average amount of meat spend on components (9,200) for one pumpkin pie exceeds autoBuyPriceLimit (6,000)
You need 1 more pumpkin pie to continue.

> closet take pumpkin

Removing items from closet...
You acquire an item: pumpkin
Requests complete.

> acquire 2 pumpkin pie

☯ Gnollish pie tin (2) mall=200 make=2147483647
☯ flat dough (2) mall=200 make=2147483647
☯ wad of dough (2) mall=200 make=200
☯ pie crust (2) mall=200 make=400
☯ pumpkin mall=16379 make=2147483647
☯ Gnollish pie tin (2) mall=200 make=2147483647
☯ flat dough (2) mall=200 make=2147483647
☯ wad of dough (2) mall=200 make=200
☯ flat dough (2) mall=200 make=200
☯ wad of dough (2) mall=200 make=200
☯ pie crust (2) mall=200 make=400
☯ pumpkin mall=16379 make=2147483647
☯ pumpkin pie (2) mall=25540 make=16579
☯ Gnollish pie tin (2) mall=200 make=2147483647
☯ flat dough (2) mall=200 make=200
☯ wad of dough (2) mall=200 make=200
☯ pie crust (2) mall=200 make=400
☯ pumpkin mall=9100 make=2147483647
Verifying ingredients for pumpkin pie (2)...
☯ Gnollish pie tin (2) mall=200 make=2147483647
☯ wad of dough (2) mall=200 make=2147483647
☯ flat dough (2) mall=200 make=200
☯ wad of dough (2) mall=200 make=200
☯ pie crust (2) mall=200 make=400
Searching for "pie crust"...
Search complete.
Purchasing pie crust (2 @ 100)...
You acquire pie crust (2)
Purchases complete.
Searching for "pumpkin"...
Search complete.
Stopped purchasing pumpkin @ 9,100.
Using cached search results for pumpkin...
Stopped purchasing pumpkin @ 9,100.
You need 1 more pumpkin to continue.

> inv pumpkin

pumpkin

Looks fine to me. Note that I sanitized the above by removing umpteen flat dough, wad of dough recursive checks, so if the debugBuy looks a little goofy, that's why.

Could the error message be more explicit?

I think it's good. Though I think proper grammar for "The average amount of meat spend on components" should either be "spent" or "to spend."

EDIT: I added a check for isAutomated too, so that the autoBuyPriceLimit check is bypassed for non-automated retrieveItem() calls. Although I don't think Mafia uses isAutomated = false anywhere at the moment.

BuyCommand uses the isAutomated=false version of makePurchases, but that's one step downstream of retrieveItem(). I'm not aware of any calls to retrieveItem itself with isAutomated=false. Still, good coding practices, and all that.
 

Bale

Minion
I just heard the sound of Theraze cackling madly as he begins to revise EatDrink to remove all his workarounds for this problem.
 

Theraze

Active member
Meh, I just left it as a "maybe someday we'll get a speculative version of retrieve_item so we can actually know how much it's going to cost" in my list of eventual goals. The problem with that is it makes mallbotting easier, so it's probably just as well, but... not sure if this fixes the bug where RI fails to get anything because the cheapest way to get multiple costs more than the cost of buying a single item from the mall, but... I'll find out when I have an aftercore sauceror, I'm sure. :)
 
Top