PriceAdvisor: Maximize your profits

xKiv

Active member
I wonder if xKiv's regex recognizes negative numbers?

"(?<!\\s)([\\d,]+)(?!\\s)" will not match a negative number, unless there is a space between the minus sign and the humber itself, because ...
... wait, I think I got it wrong ... I put in negative look*s, because I wanted it to work at the start/end of the string too, but I forgot to negate the character class!
This regex will actually only match a number that *doesn't* have a space on any end.
Correct version would be ...
"(?<!\\S)([\\d,]+)(?!\\S)"
(replace lowercase s with uppercase S)
Now, this will find a (maximal) string of digits (and the character ",") such that it isn't preceded by a non-space character and isn't followed by a non-space character (which is much the same as "is preceded by a space or a start of string AND is followed by a space or a start of string" .. which could have probably been more succintly expressed as "(^|\\s)([\\d,]+)($|\\s)" but that would change the capture groups), so it won't match anything directly preceded (or containing) a "-" character.
For the possibility of that, you would want something like
"(?<!\\S)(-?[\\d,]+)(?!\\S)"
 
Ah. So. I don't need to recognize the comma, and I don't want to recognize the negative sign (I don't want to multiply negative numbers). So what I want is:

"(?<!\\S)([\\d]+)(?!\\S)"

Yes?

(Thanks for your help with this, xKiv, I really appreciate it.)
 

zarqon

Well-known member
Thoughts on Container Items

Do you have any way of accessing the chance that you will get an item from a container item? Or, also useful, how many items will result from using a container? Seems like you should multiply the price_advice() result of items inside containers by the chance that they will result. That would certainly improve the accuracy of any of your action trees that involve using containers.

Example: getting advice for a wad of slimy rags claims that using it and then smashing all four items (you actually only get one of the four) is far and away the most profitable use of the item, when actually you will only get 25% of PA's projected value -- a significant difference.

I'm not sure how you are getting the data about containers -- I perused your script briefly but it wasn't immediately apparent -- but wanted to suggest that as something to improve.

I also have an ulterior motive. If you find a good way to access that info, I'd definitely want to use it in FTF/SS/BBB. I'd like to write a wrapper for is_goal(item) that takes containers into consideration. And I'd also like to consider containers for has_goals(monster), which FTF uses to auto-olfact and BBB uses to fight putty monsters/dolphins.
 
Zarqon: it's a map, living on map manager -- use_for_items.txt. (Some conditional items, like dead mimic / Hippy MPE / Frat FGF live in code since, well, they're conditional: see section starting about line 87, "Add "use" items with conditional results")

Relevant section of use_for_items.txt:
Code:
wad of slimy rags	crown-shaped beanie	0.25
wad of slimy rags	hopping socks	0.25
wad of slimy rags	letterman's jacket	0.25
wad of slimy rags	poodle skirt	0.25

Relevant section of PriceAdvisor.ash, around line 533:
Code:
		// Using, including using with requirements
		if (meat_use contains it || item_use contains it)
		{
			price_advice use;
			use.action = "use 1 " + it;
			use.price = meat_use[it];
			item required_item;
			boolean bad_result = false;
			
			foreach it2 in item_use[it]
			{ 
				temp = best_advice(it2, !(currently_considering contains it2));
				
				// If sub-result is invalid, result is invalid
				if (temp.action == "")
				{ 
					bad_result = true;
				  break;
				}

				if ( item_use[it][it2] < 0 ) // item is the required item
				{ required_item = it2; }
				else
				{ use.action = use.action + "; " + temp.action; }
				use.price = use.price + [B]item_use[it][it2] * temp.price[/B];
			}
			
			if (required_item != $item[none])
				use.action = "acquire 1 " + required_item + "; " + use.action;
				
			if (!has_circular_end_product(use.action) && !bad_result)
				advice[count(advice)] = use;
		}

So you see I have anticipated you! It's just that the advice is misleading -- what it wants to say is "whichever one of these you end up with, here's what you should do with it..." but it can't really know, so the action string gives advice for all possible results. The price, however, is correctly adjusted by expected value. (Well, as long as the map is correct -- I've taken the data from the wiki and can hardly complain about inexact spading if I'm not willing to do the job myself!)

Edit: Huh, I think I spot a bug. Required items (keys) were a later addition, and it looks like the pricing doesn't account for the fact that item_use[it][it2] is negative in that case... I'll push an update for that once xKiv gets back to me about the regex, so I don't push two tiny ones so close together.
 
Last edited:

zarqon

Well-known member
Aha! Thanks for the tip -- I was misled by the printed output in precisely the manner you suggest.

Checked out use_for_items... it looks perfect! I believe I will be loading it for has_goals() and is_goal(). Thanks for doing the hard part for me. :) I must say I'm quite pleased by the increasing level of function/data sharing between scripts.

Speaking of, here's a heads-up: when the next ZLib update comes along (probably in about a week or so), you'll be able to (in fact, you'll need to) delete a bunch of lines from your script (all the load_current_map declarations and functions), since load_current_map() will soon be in ZLib, with once-daily Map Manager checking. I could probably bump that update sooner if you'd like to include that in your update as well.
 
I'm looking forward to your update, but there's no need to push it forward unless you really want to. I'm fine with my 1.61 being a quick-tiny-bugfixes update -- there's plenty still in the pipeline for PA that load_current_map can be changed and rolled in at pretty much any time.

One slightly non-standard thing you may want to watch out for in use_for_items is the occasional "-1" entry, which is not a result of use but a requirement for use (i.e. keys). This can be used either to require the key item or to filter out the whole keyed container from consideration.

(Meat is not accounted for by use_for_items; containers which yield both occur in both use_for_items and use_for_meat. But I don't think you'll be worrying about meat for goals, so that's probably pretty irrelevant to you.)
 

xKiv

Active member
Ah. So. I don't need to recognize the comma, and I don't want to recognize the negative sign (I don't want to multiply negative numbers). So what I want is:

"(?<!\\S)([\\d]+)(?!\\S)"

Yes?

(Thanks for your help with this, xKiv, I really appreciate it.)

As far as I can tell (tested with perl), this does what I think you want it to do.
(and I thought you wanted to parse even numbers with "," in them - thousands separator or whatnot - or I wouldn't have left it in there)
 

zarqon

Well-known member
My point was that my update will actually break your script, so you may want to do it all at once. At least, I had to edit your script (with my already-changed ZLib) to get it to run.
 
Was it all that sneaky pre-declaring of functions?

Anyway, I've done my little fixes as 1.61. (Thanks for getting back to me, xKiv -- I think the comma was in there because I stole the regex from some post of Veracity's. She probably did want to recognize commas, but I don't need to, and now that I know what it actually means I know I can take it out!)

Zarqon, I really appreciated your offer but I didn't want to mess with your release schedule -- even though it will break PA I think the fix will be quick on my end. I'm thankful for the warning!
 
I think the easiest thing for you to do would be to use the alias provided in the first post. Copy and paste this text into your CLI:
Code:
alias pa => ash import <PriceAdvisor.ash> print(price_advisor($items[%%], true))
Then, whenever you want to ask PriceAdvisor about an item (or even more than one), you can type "pa" and then the item name in the CLI. For example, if you want to know about springs, cogs, and sprockets, you would type this:
Code:
pa spring, cog, sprocket

PriceAdvisor is not really designed to be run from the script menu -- I apologize for the confusion.

Edit: Also, congratulations! You are the 100th person to register PriceAdvisor.
 
Last edited:
Version 1.62:
- uses the cli-executable string suggested by heeheehee to trade a gloomy mushroom for an oily golden mushroom if in cli-executable mode
- updated to work with the awesomeness of load_current_map() from ZLib 14. Many thanks to Zarqon for pushing for the aggregate type that allows this, and to Veracity for implementing it. I love chopping those two almost-identical functions definitions out of my code! (And yet more thanks to Zarqon for giving me a heads-up earlier that this update would be so awesome it would force PA to update too.)
 

heeheehee

Developer
Staff member
Kinda wanted to point out that in your initial post, "best_price" should probably be "best_advice". Which I noticed since I finally started actually using this script. xD
 

heeheehee

Developer
Staff member
/me points at "Both best_price() and price_advisor() are overloaded to take multiple items:"

Anyway, if you ever do decide to import profit_fx.ash, I guess this bit of code would work (mostly done in your* style)
Code:
price_advice use_effect_advice(item it, boolean consider_more, location place)
{
	price_advice advice;
	advice.action = "use 1 " + it;
	advice.price = 0.0
	
	if (relfx contains to_lower_case(it))
	{ return new price_advice("", 0.0); }

	return meat_gain(place, string it);
}

I suppose that I could rewrite parts of profit_fx to spit out a map with float qty [item it] as well as flat meat. This'd probably help in getting it to use better pricing for items. I guess this'll be the last** update to profit_fx.ash.
Edit: Argh, just realized that this would not work with my fix for the multiple-drops issue. I could try a different approach with a different type of map, though...

*Meaning, it's just a slight modification of part of PA.
**Even though I said that the current version would be the last one weeks ago. Lesson learned? I should probably start using "penultimate". Heh.
 
Last edited:

heeheehee

Developer
Staff member
Yup, that seems to be it.

Updated profit_fx.ash to now note items and meat. Yay! So here's the modified bit of code that you'd just inject into PriceAdvisor.ash (after importing profit_fx):

Code:
float tempvar = 0;

string all_but_current(string str) // Function basically ripped off of replace_with_multiple.
{
	matcher integer = create_matcher("(?<!\\S)([\\d]+)(?!\\S)", str);
	string replaced = "";
	float itStart = 0;
	print(str);
	float itEnd = index_of(str,";");
	
	string temp = substring(str, 0, itEnd);
	
	while(find(integer))
	{
		int mstart = index_of(str, group(integer), itStart);
		temp = replace_string(temp, group(integer), "");
		
		item itm = to_item(substring(str, mstart, itEnd));
		replaced = replaced + substring(str, itStart, mstart)+"-"+to_string(available_amount(itm))+" "+to_string(itm)+";";
		itStart = itEnd + 1;
		itEnd = index_of(str, ";",(itStart));
		if(itEnd!=-1) temp = substring(str, itStart, itEnd);
		integer.reset(temp);
	}
	print(replaced);
	return replaced;
}

price_advice main(item it, boolean consider_more, location place)
{
	float parsing = meat_gain(place, it); // This is just to get profit_fx to execute and set all the variables.
	price_advice advice;
	advice.action = "use 1 " + it + ";adventure " + relfx[to_lower_case(it)].duration + " " + place+";";
	advice.price = tot_meetz;

	if (!(relfx contains to_lower_case(it)))
	{ return new price_advice("", 0.0); }
	price_advice single_advice;

	// Find expected value
	foreach key in drops { 	// Each key is an item. ct is a nested array that helps differentiate between 
				// different, but otherwise identical drops.
		foreach i,m in drop[key].ct {	// i is count, m is monster. First is necessary in zones like 8-Bit
						// Realm, second is to keep the results differentiable to humans. And more.
			tempvar = tempvar + drop[key].ct[i,m];	// tempvar is estimated additional items that drop.
		}
		single_advice = best_advice(key, consider_more);
		if (single_advice.action == "")
		{ single_advice.price = 0; single_advice.action = ";";}
		else single_advice.action = single_advice.action + ";";

		single_advice.action = all_but_current(single_advice.action);

		advice.action = advice.action + single_advice.action;
		advice.price = advice.price + to_int(relfx[to_lower_case(it)].duration) * single_advice.price * tempvar;
		tempvar = 0; // Clear tempvar.
	}
	return advice;
}

At least, I think that should work. Holler at me if it doesn't. [[Also, I feel like I'm threadjacking again. Sorry!]]

Edit: It will work, but the price advice is all screwy. Fixing...
[[Fixing edit: Ugh, it's getting so messy. And currently, making stuff doesn't really work.]]

The creating stuff problem popped up in the str all_but_current(string str) function that I created, which uses the regex to essentially rewrite a chain of CLI-executable commands to do that for everything but what's in your inventory. I had to do that, mainly because my estimate can't be 100% correct, and either way, you can't mallsell 1.827 twinkly wads. =D

Some number of edits previously: Does the regex that's in PA will need resetting? So like
Code:
	while(find(integer))
	{
		multiplicand = to_int(group(integer, 1));
		replaced = replace_string(replaced, group(integer, 1), to_string(multiplier * multiplicand));
		integer.reset(replaced); // Quick fix!
	}
? 'Cause I think I came across a problem involving this. At some point. ((Of course, that was when messing with a bunch of actions lumped together.)

Some number of edits later: "acquire 1 MSG, 1 dry noodles" isn't proper CLI-output. Just a heads up.
 
Last edited:

heeheehee

Developer
Staff member
I think the easiest thing for you to do would be to use the alias provided in the first post. Copy and paste this text into your CLI:
Code:
alias pa => ash import <PriceAdvisor.ash> print(price_advisor($items[%%], true))
Then, whenever you want to ask PriceAdvisor about an item (or even more than one), you can type "pa" and then the item name in the CLI. For example, if you want to know about springs, cogs, and sprockets, you would type this:
Code:
pa spring, cog, sprocket

PriceAdvisor is not really designed to be run from the script menu -- I apologize for the confusion.

Regarding PA, at least. My threadjacking isn't by any means complete yet. And I'll probably just end up PMing it to aqualectrix (aqualectrici) when I'm done with all my fixes. So yeah, no more threadjacking for me!
 

mredge73

Member
I haven't read all of the posts in the forum but I am trying to make use of this script by slowly integrating it into my item handling scripts.

The biggest problem that I have is that if I have 1000 items that need to be autosold for example. Currently by passing the action into the cli_execute() I would have to do it 1000 times causing 1000+ server hits opposed to autoselling 1000 items in one server hit. It can get even larger if there are multiple steps.

So as a feature request, can you add in a way to specify the quantity of items to be executed at one time:
price_advice [int] price_advisor(item it,int howmany, boolean consider_more);

or

To make better use of this script to execute advice automatically I need a way to split the advice up into individual commands where each action can be executed individually. The best way to do this would be to mod the record and then build a new function to make use of it.

for example:
record price_advice2
{
string[int] action; // mallsell, smash, autosell, etc...
item[int] victim; // item to do the action to
float[int] price; // net meat gain from doing action to 1 victim
};
 
Last edited:
Top