PriceAdvisor: Maximize your profits

xKiv

Active member
Additional question/plea for help from someone who knows regular expressions. I have a little regex:
Code:
matcher integer = create_matcher("([\\d,]+)", str);
I nicked it from some post of Veracity's, totally unrelated to PA. It sees integers.

This works well for me, except that, well, when I use it to find integers so I can replace them with multiples, I end up with this: "mallsell 2 8-d camera". Oops. That's quite the camera!

This is not the regex's fault, of course. It sees an integer, just like it should. It's my fault: I should have looked for an integer with a space on each side.

However, I nicked the original because I'm no good at regexes. Can someone tell me what matcher will only see integers with a space on each side?

You want negative lookbehind/lookahead. I am not sure if ash just passes the regex to java, but if that's the case:

"(?<!\\s)([\\d,]+)(?!\\s)"
 
Last edited:

slyz

Developer
Veni, vidi, vici regex.

I also came to point out a little tool I adapted from a mredge73 script, to get advice from PriceAdvisor for everything in your inventory. Here.
 

Veracity

Developer
Staff member
Do you need a degree in computer science to understand regular expressions?
Rats! I have a degree in Medieval Studies. I am doomed!
 
Do you need a degree in computer science to understand regular expressions?

I would imagine the condition is sufficient (or ought to be sufficient, but apparently isn't in my case), but not necessary. Also, from the hints you've dropped about your job, it is clear that your practical experience in computer science far exceeds my own! (My own being effectively 0.)
 
All right. Let's talk version 1.6!

Three zlib vars were added, all defaulting to false:
priceAdvisor_obeyBuyLimit : if true, don't recommend anything with ingredients priced above your autoBuyPriceLimit (a Mafia setting)
priceAdvisor_conservative : if true, don't recommend anything with ingredients which are not buyable from NPCs or priced at mallmin.
priceAdvisor_CLIexecutable : if true, don't give any advice that isn't CLI-executable. A check for this currently wraps only two things:
- the "can't or doesn't sell" message, for things like useless powder
- the "trade your gloomies in to the Suspicious-Looking Guy" message
In both cases, the advice will be ("", 0.0) instead. The empty string is cli-executable with no problems. (If there's a way to trade your gloomies for oilies via CLI, I could use that replacement instead and you'd get that functionality back. Is there?)

Some name/interface changes:
- renamed the best_advice cache to best_cache
- renamed best_price() to best_advice() to better fit the naming conventions of the other helper functions
- changed smashed_advice() to smash_advice(), again for consistency
- changed the boolean argument of smash_advice() to be consider_more like everything else, not just consider_malus
- changed the method of infinite recursion / circular opportunity cost prevention to be more uniform

Some fixes:
Fixed best_cache to cache results for consider_more being true and false separately.
Fixed an issue where invalid sub-results were appearing in the final result; now they invalidate the final result, which does not appear.
Fixed an issue where final result == original item was not being caught if it occurred as the first "sell" statement of an action.
Fixed/enhanced replace_with_multiple() with xKiv's kindly-provided regex.
Fixed best_advice() to return a newly-constructed price_advice so nothing using its return value can mess with the best_cache.

Problems that remain in this version:
tldr; Maybe not quite cli-executable; cache is necessary to run at a reasonable speed but sometimes gives not-perfect advice

As I mentioned to dj_d, actions for things that report expected values will not be quite correct when cli_execute()'d. They say 1 of everything, when it might really be 2 of one thing and 0 of another. I don't really know what to do about this, since I don't know the results of actually performing the action. "*" is an option, but then it would act on everything in your inventory instead of just the results of the action.

There is an odd interaction between circular-opportunity-cost-prevention and caching. What I mean by circular opportunity cost is this:

Suppose A (sells for 100) and B (sells for 200) can be made into C (sells for 1000).
If you ask PA about A it considers:
Code:
- selling A : profit is 100
- making C and selling C :
  - this requires B
  - PA considers the opportunity cost of using B as an ingredient:
    - selling B : profit is 200
    - making C and selling C
      - this requires A.  To prevent infinite recursion, we consider A to 
        have its mallsell/autosell value here, which is just 100
      - so making and selling C has profit 900 [B]for B[/B]
    - since the largest profit for B is calculated at 900, that is the opportunity cost for B
  - so making and selling C has profit 100 [B]for A[/B]

This is clearly crazy. Because we were already thinking about using B to make C, the opportunity cost for using B in C when considering B should have been 0, not 900. Thus the opportunity cost of using B in C when considering A should have been max(200, 0) = 200 and the final profit from A, using it and buying B to make C, would have been 800 -- as any casual glance will tell you is correct.

PA does do this calculation correctly, preventing itself from thinking about making C a second time when it is already being considered. It says:
Code:
    - making C and selling C
      - already considering C; profit of considering it again is 0
    - since the largest profit from B is 200, that is its opportunity cost
  - so making and selling C has profit 800 [B]for A[/B]

This is all fine and good, except that sometimes the first time an item is considered (such as B in this case) is underneath some other considerations, which are correctly taken into account when reporting its best advice under those considerations. However, since this is the first time B has been considered, this is also the advice that gets cached as the best advice for B. As long as the same instance of PA is running, it will tell you that you ought to just sell B for 200. (When clearly you ought to buy an A, make C, and profit by 900.)

I can't turn the cache off; for anything even mildly complicated PA just takes far, far too long to complete. I've even tried a much more sophisticated caching method, designed to never cache the wrong thing:
Code:
if item in cache
  return best cached value that doesn't consider what's already being considered
else if cache mode on
  old_cc = currently_considering
  clear currently_considering // remove all context
  cache mode off // don't cache anything with the new context
  cache the result of price_advisor(item) -- every result, not just the best!
  cache mode on // return to normal
  currently_considering = old_cc  // return to context
else return price_advisor(item)[0] // without caching it

This very nicely ensures that each value that is cached has been evaluated as if it were at the top-level. Unfortunately, my testing has showed that preventing all the lower-level caching still makes PA far, far too slow.

I'll keep trying things and suggestions are welcomed, but if you've ever wondered why PA tells you things like this:
Code:
> pa bottle of rum
[B]bottle of rum[/B]
make 1 bottle of Lieutenant Freeman; mallsell 1 bottle of Lieutenant Freeman
etc.

> pa Lieutenant Freeman
[B]bottle of Lieutenant Freeman[/B]
acquire 1 kiwi; make 1 caipifruta; mallsell 1 caipifruita
ect.

instead of
Code:
> pa bottle of rum
[B]bottle of rum:[/B]
make bottle of Lieutenant Freeman; acquire kiwi; make caipifruta; mallsell caipifruta
etc.
now you know.

However, since the results of PA will never be worse than "just mallsell it", and will usually be better, I hope this affects its optimality rather than its usefulness.
 
Last edited:

dj_d

Member
Wow Aqua! OK, new item on the farm.ash todo list. This is super cool. Two little things:
* I'm concerned enough about unintended consequences that I'm thinking about having farm.ash programmatically set the conservative flag. Thougts?
* The "right" way to handle the 0/1/2/* problem is probably really ugly: use inline ash to capture the number of whatevers you have, do the action, check the number of whatevers you have now, and act on the results.


Also, another idea riffing off the above - goofy but possible - is that instead of making the output cli-executable, make it raw ash. That could, I think, be invoked via cli_execute ("ash "+foo);.
 

dj_d

Member
So we've got a Medieval Studies major in the CS field, and a CS major whose job is Latin Languages*. This place rocks.

*I am probably misremembering the thread about aqualectrix's (aqualectrices? no, that'd be two) name, in which someone who may not have actually be aqualectrix declared that they had some professional connection to latin, but it nonetheless amuses me to no end.
 
Oh, I have no professional connection to Latin. I just still consider myself a Latin scholar from my many years of taking Latin (and a bit of Ancient Greek) in high school and college.

I would definitely have farm.ash set the _conservative flag programmatically.

Can you explain a bit more about your proposed solution to the 0/1/2/* problem? The advice is generated from expected results data before the action is taken. Are you saying the advice generated should include code to figure out what to do as it's being executed? And how would I do that?
 

heeheehee

Developer
Staff member
*I am probably misremembering the thread about aqualectrix's (aqualectrices? no, that'd be two) name, in which someone who may not have actually be aqualectrix declared that they had some professional connection to latin, but it nonetheless amuses me to no end.

Yeah, that was the whole thread-jacking in which I accidentally referred to aqualectrix (aqualectrici?) as a he. She pointed out that I was probably not a Latin scholar, and hilarity ensued.

Anyway, to avoid another thread-jacking incident: can you clarify what exactly is meant by the 0/1/2/* problem?

Also, trading gloomies for oilies can be accomplished with "town_wrong.php?place=goofballs&sleazy=1". Tested it out on a multi, and it seems to work.

And finally: I'd say that profit_fx.ash will be completely ready for importing once I add functionality to automatically update data files. Should be just a few minutes (half an hour or longer if I get sidetracked, heh), if I know what I'm doing.
Edit: It should be done. I did, indeed, get sidetracked, but I also just decided to edit this bit into here.
 
Last edited:
0/1/2/* problem:

Say you're considering a knob goblin lunchbox. In its original non-numeric advice, PA said:
use knob goblin lunchbox; mallsell knob pasty; mallsell thermos full of knob coffee

It meant "use the lunchbox and mallsell however many pasties and thermoses you get". You might get one pasty and two thermoses (yay!) or two pasties and one thermos (boo!). It gave a price based on the expected result: 1.5 pasties and 1.5 thermoses.

This is still what it does, but now it puts a 1 in front of everything. If you cli-execute the advice, it will only mallsell 2/3 things. (Still true even with out the 1 in front of everything.)

In order to give correctly-executable advice, it needs to say, in an executable fashion, exactly what it means: use 1 knob goblin lunchbox; mallsell however many knob pasties you just got; mallsell however many thermoses full of knob coffee you just got

(The problem is even greater for things like smash results or fantasy chest results, where some of the recommended actions apply to things you might not even get!)
 

lostcalpolydude

Developer
Staff member
At least for some of this stuff, can you use negative quantities to say "all but X of an item"? If you have 5 knob pasties already, the command could say "mallsell -5 knob pasty" or something. I don't know if negative numbers work with all of the necessary commands (or even with mallsell), but I think it would solve the issue in at least some cases.
 

heeheehee

Developer
Staff member
At least for some of this stuff, can you use negative quantities to say "all but X of an item"? If you have 5 knob pasties already, the command could say "mallsell -5 knob pasty" or something. I don't know if negative numbers work with all of the necessary commands (or even with mallsell), but I think it would solve the issue in at least some cases.

Assuming that it's still directly CLI-executable, it should still work (with mallsell, at least -- just checked that. =D)
 
Ah, now that could work -- output "-" + item_amount(ing). I wonder if xKiv's regex recognizes negative numbers? I think I wouldn't want it to in this case.

I think Mafia supports negative quantities pretty consistently. Certainly it works with mallsell; I just tried it.
 

heeheehee

Developer
Staff member
Hmm... It doesn't seem like it'd do so. I think. (If I'm reading it right, it looks for a number bounded by spaces? I suck at regexps.)

I suppose your output could be displayed through "-"+available_amount(it). Or something like that. You might want to store current amounts in a temporary map, if you want to throw in an if statement to display the output iff you actually get some of the item.
 
Last edited:

dj_d

Member
That might work! I meant something like the much uglier:
"ash it it1 = item_amount($item[knob pasty]); use(1,$item[knob goblin lunchbox]); cli_execute("mallsell "+(item_amount($item[knob pasty])-it1)+" knob goblin pasty");...

etc. There's probably a cleaner way. :)
 
Top