Need help with a Hardcore recoveryScript

Bale

Minion
Hi. I've never posted here before, but as I understand the rules I need to provide some of the code I'm working with to support my question. I'm working on a recoveryScript. This is designed for hardcore or ronin -- an environment with limited resources and no access outside of inventory. It is also designed not to waste anything, so it will try to make most efficient use of limited resources. It will also try not to waste either hp or mp healing off items that give both, like Phonics downs or Ancient Magi-Wipes.

I'm pretty new to scripting for kolMafia. This is only my second script and I don't have any other examples of this type to borrow from, so please be gentle if I did something stupid. Anyway, here are my questions:

1. Is there some way to identify if the black market is open? All I could think of was to (contains_text(visit_url("woods.php"),"store.php?whichstore=l"), but it seems wasteful to have to hit the server every time I need the answer.

2. Can I check if the Doc Galaktik quest was completed without hitting the server? Is there a command for using Doc Galaktik's mp restorative or should I use a visit_url?

3. Is there some way to query mafia for the effectiveness of various restoratives since the method I'm using in my example seems terribly inefficient if there's a way built in.


Here's a snippet of the code I'm working on. There's more, but this is enough to give you the idea. It's still in an early phase. Note the ridiculously cumbersome map that I'm using to store restorative data. I hate that, but I can't think of a better method.

Code:
record restorative_info {
	int minmp;
	int maxmp;
	int minhp;
	int maxhp;
};

restorative_info [item] heal;
heal[ $item[plump juicy grub] ].minmp = 0;
heal[ $item[plump juicy grub] ].maxmp = 0;
heal[ $item[plump juicy grub] ].minhp = 999999999;
heal[ $item[plump juicy grub] ].maxhp = 999999999;
heal[ $item[Delicious shimmering moth] ].minmp = 30;
heal[ $item[Delicious shimmering moth] ].maxmp = 40;
heal[ $item[Delicious shimmering moth] ].minhp = 0;
heal[ $item[Delicious shimmering moth] ].maxhp = 0;

heal[ $item[Tonic water] ].minmp = 30;
heal[ $item[Tonic water] ].maxmp = 50;
heal[ $item[Tonic water] ].minhp = 0;
heal[ $item[Tonic water] ].maxhp = 0;
heal[ $item[magical mystery juice] ].minmp = floor(my_level() * 1.5 + 4);
heal[ $item[magical mystery juice] ].maxmp = floor(my_level() * 1.5 + 6);
heal[ $item[magical mystery juice] ].minhp = 0;
heal[ $item[magical mystery juice] ].maxhp = 0;
heal[ $item[Knob Goblin seltzer] ].minmp = 8;
heal[ $item[Knob Goblin seltzer] ].maxmp = 12;
heal[ $item[Knob Goblin seltzer] ].minhp = 0;
heal[ $item[Knob Goblin seltzer] ].maxhp = 0;
heal[ $item[black cherry soda] ].minmp = 8;
heal[ $item[black cherry soda] ].maxmp = 11;
heal[ $item[black cherry soda] ].minhp = 0;
heal[ $item[black cherry soda] ].maxhp = 0;

// This will tell me how many of a given restorative I need to restore a minimum quantity of mp without over-restoring hp.
int mp_quant(int minimum, item restorative){
	int mp_needed = minimum - my_mp();
	if mp_needed < 1
		return 0;
	float roughquantity = mp_needed / heal[restorative].maxmp;
	int quantity = floor(roughquantity);
	if (ceil(roughquantity) * heal[restorative].maxmp > my_maxmp())
		if (heal[restorative].maxmp == 999999999)
			return 1;
	else 
		quantity = ceil (roughquantity)
	while ( quantity * (heal[restorative].maxhp + heal[restorative].minhp) / 2 + my_hp() > my_maxhp() )
		quantity = quantity - 1;
	if quantity > item_amount(restorative)
		quantity = item_amount(restorative);
	return quantity;
}

// This will tell me how many of a given restorative I need to restore a minimum quantity of hp without over-restoring mp.
int hp_quant(int minimum, item restorative){
	int hp_needed = minimum - my_hp();
	if hp_needed < 1
		return 0;
	float roughquantity = hp_needed / heal[restorative].maxhp;
	int quantity = floor(roughquantity);
	if (ceil(roughquantity) * heal[restorative].maxhp > my_maxhp())
		if (heal[restorative].maxhp == 999999999)
			return 1;
	else 
		quantity = ceil (roughquantity)
	while ( quantity * (heal[restorative].maxmp + heal[restorative].minmp) / 2 + my_mp() > my_maxmp() )
		quantity = quantity - 1;
	if quantity > item_amount(restorative)
		quantity = item_amount(restorative);
	return quantity;
}

// This will chose the best restorative to purchase if I don't have enough in my inventory. I'll probably decide to use Doc Galaktics if it returns soda water.
item best_mp_purchase () {
	if ( my_primestat() === $stat[ mysticality ] || ( my_class() === $class[ accordion thief ] && my_level() >= 9 ))
	    return $item[magical mystery juice];
   	if (have_outfit("knob goblin elite guard uniform") && item_amount("Cobb's Knob lab key") > 0)
  		return $item[knob seltzer];
   	if (my_level() >= 11)
   		return $item[black cherry soda];
   	return $item[soda water];
}

Reference: http://kolmafia.us/index.php/topic,1982.0.html
 

jasonharper

Developer
Re: Hardcore recoveryScript

I'm afraid I've got no good news for you - ASH currently has no access to the info on restorative effectiveness or quest completion state. Even if it did, restoratives only have an average specified, not separate min/max values like you want.

visit_url() would be the best way to buy Galaktik MP restoration. The alternative would be to set the list of allowed restoratives to just that item, and exiting the script, but that would leave things in a bad state once you stop using the script.
 

Veracity

Developer
Staff member
Re: Hardcore recoveryScript

[quote author=jasonharper link=topic=1994.msg9608#msg9608 date=1226150316]
visit_url() would be the best way to buy Galaktik MP restoration.
[/quote]
I could have sworn that we had a CLI command:

galaktik hp [amount]
galaktik mp [amount]

which connected to the GalaktikRequest, but it doesn't seem to be there.

I'd rather (re)implement that than force people to use visit_url() for things that KoLmafia otherwise supports well itself.

Edit: The "galaktik" command was removed in revision 3863, when support for partial restores was added elsewhere in KoLmafia; prior to that, Doc Galaktik was an all-or-nothing method of HP/MP recovery, and the command therefore did not accept a parameter.

I re-added the command and implemented an option parameter to specify how much HP/MP to restore:

galaktik hp -- restore all hp
galaktik hp 10 -- restore 10 HP
galaktik hp -10 -- restore all except 10 HP

... and similarly for mp
 

jasonharper

Developer
Re: Hardcore recoveryScript

Veracity: I'm not sure why you're checking currentLine, the way you have it "galaktik" won't work if it isn't the first command on the line.

Bale: I've put in some ASH functions for quest status: galaktik_cures_discounted(), white_citadel_available(),
black_market_available(), and hippy_store_available(). I'm not sure that they can be trusted immediately after ascending - I've had troubles with mafia thinking it could still buy black cherry soda, so I don't think the quest status is getting refreshed properly. At least having a way to determine the status will make it easier to fix that...
 

Veracity

Developer
Staff member
Re: Hardcore recoveryScript

[quote author=jasonharper link=topic=1994.msg9614#msg9614 date=1226163500]
Veracity: I'm not sure why you're checking currentLine.[/quote]
It was left over from the previous version, whose code I cut & pasted and modified to get the new command. I'll remove the unnecessary vestige...
 

Bale

Minion
Re: Hardcore recoveryScript

[quote author=jasonharper link=topic=1994.msg9608#msg9608 date=1226150316]
I'm afraid I've got no good news for you - ASH currently has no access to the info on restorative effectiveness or quest completion state. Even if it did, restoratives only have an average specified, not separate min/max values like you want.[/quote]

Just a list of average values would make me happy. It's less exacting than what I was doing, but if I could access it with the recoveryScript I'd be quite happy to settle for average values. PLEASE!

I think pretty much everyone who ever tries to write a recoveryScript would benefit from the ability to access mafia's values for restoratives.

[quote author=Veracity link=topic=1994.msg9613#msg9613 date=1226155937]I re-added the command and implemented an option parameter to specify how much HP/MP to restore:

galaktik hp -- restore all hp
galaktik hp 10 -- restore 10 HP
galaktik hp -10 -- restore all except 10 HP

... and similarly for mp
[/quote]

Awesome! I'll just have to use cli_execute("galaktik mp "+ to_string(minimum)); or the like.

[quote author=jasonharper link=topic=1994.msg9614#msg9614 date=1226163500]Bale: I've put in some ASH functions for quest status: galaktik_cures_discounted(), white_citadel_available(),
black_market_available(), and hippy_store_available(). I'm not sure that they can be trusted immediately after ascending - I've had troubles with mafia thinking it could still buy black cherry soda, so I don't think the quest status is getting refreshed properly. At least having a way to determine the status will make it easier to fix that...
[/quote]

Thank you again! I'm really happy that my attempt to write this script has pointed out commands useful for recovery scripting.
 

Bale

Minion
Re: Hardcore recoveryScript

I've still got a lot of work to do, but at least this little snippet looks nice.

Code:
// This will chose the best restorative to purchase if I don't have enough in my inventory. 
// I'll probably decide to use Doc Galaktic if it returns soda water.
item best_mp_purchase () {
	if ( my_primestat() === $stat[ mysticality ] || ( my_class() === $class[ accordion thief ] && my_level() >= 9 ))
	    return $item[magical mystery juice];
   	if (have_outfit("knob goblin elite guard uniform") && item_amount("Cobb's Knob lab key") > 0)
  		return $item[knob seltzer];
   	if (black_market_available())
   		return $item[black cherry soda];
	if (white_citadel_available())
		return $item[Regular Cloaca Cola];
   	return $item[soda water];
}
 

jasonharper

Developer
Re: Hardcore recoveryScript

[quote author=Bale link=topic=1994.msg9622#msg9622 date=1226187294]
Just a list of average values would make me happy. It's less exacting than what I was doing, but if I could access it with the recoveryScript I'd be quite happy to settle for average values.[/quote]
Here's the problem: if those values are made accessible, the interface for doing so would be carved in stone. The whole point of having recoveryScripts is that the existing mechanism isn't meeting everyone's needs. The ultimate goal is either to fix it, or toss it all out and replace it with something that works better - an option that would be greatly complicated by any need to maintain old data formats.

So, having an explicit list of recovery values in your code is exactly the right thing for you to do, and having separate min/max values is actually a quite good idea. Note that this doesn't mean that you have to build the map every time your recoveryScript runs: you might want to do that in a separate run-once script, and save it with map_to_file() for your main script to load with file_to_map().

It may save you some time to start with the average recovery values from the source code; if nothing else, it's a fairly complete list of restoration sources. The lists are near the top of these files:
HP: http://kolmafia.svn.sourceforge.net/viewvc/kolmafia/src/net/sourceforge/kolmafia/HPRestoreItemList.java?revision=6436&view=markup
MP: http://kolmafia.svn.sourceforge.net/viewvc/kolmafia/src/net/sourceforge/kolmafia/MPRestoreItemList.java?revision=6436&view=markup
The second number in each entry, if present, is the price for NPC buyables; the boolean at the end of the MP entries indicates whether it's combat-usable, which isn't relevant for a recoveryScript.
 

Bale

Minion
Re: Hardcore recoveryScript

My first reaction to that was to insist that using a function like restorative[$item [ x ] ] that returned an average value would only be a choice for the scripter. Just an option. After all, anyone could still create a more exacting solution if they want to, so you should just enable the choice.

Then I listened more closely to what you're saying. You don't really like that solution and you realize that I and others will jump at the ability to take that shortcut. After all, you created the option of a recovery script specifically because you didn't like the simplistic logic of kolMafia's defaults.

Well, you've removed the crutches in order to force me to walk on my own feet, so I'll continue. I'll have to check up on how map_to_file() and file_to_map() work. I do like your idea to have the data in a separate file like that. Maybe someday you'll include my restorative map file in one of kolMafia's datafiles along with a set of functions for retrieving the min and max values. ;)

Please continue to offer any other suggestions you have as I continue to post about my progress. I'll post my mapfile once I'm done with it, just in case anyone else would want to use it in their own recovery scripts.
 

Bale

Minion
Re: Hardcore recoveryScript

Just realized that I was missing a few important functions.

Is there any way to query the type of dwelling in my campsite? Also, how can I find out how many free rests I have left for the day? Or how many I've used, either way.

Also, how can I find out the dungeons of doom potion that heals? Will CLI_execute("!"); return a string that I can search? Is that actually the best way to do it?
 

jasonharper

Developer
Re: Hardcore recoveryScript

Mafia currently doesn't know what kind of housing you have - tracking that is on my to-do list, don't know when I'll get to it.

Preference "timesRested" gives the number of known rests used, free and otherwise; you'll need to use have_skill() on the disco nap skills to figure out how many would be free.

I THINK you can use to_item("potion of healing") to get the DoD healing potion; and that the result will be equal to $item[none] if it hasn't been identified yet. Let me know if that doesn't work, there are more certain (but more complicated) ways of finding that out.
 

Bale

Minion
Re: Hardcore recoveryScript

[quote author=jasonharper link=topic=1994.msg9645#msg9645 date=1226335018]
Mafia currently doesn't know what kind of housing you have - tracking that is on my to-do list, don't know when I'll get to it.

Preference "timesRested" gives the number of known rests used, free and otherwise; you'll need to use have_skill() on the disco nap skills to figure out how many would be free.[/quote]timesRested is good. I can easily check skills to find out if I've got spares. I don't really need another function for that.

Can you think of a work around for identifying housing? Should I use (contains_text(visit_url("campground.php"),"rest4_free.gif") or the like? That'd at least also tell me if the resting costs an action at the same time. I just don't like hitting up the server.

Could I just assume you'll start tracking housing? I could just write my script using housing() which returns an integer corresponding to the number in the image of the current dwelling. (Currently, housing goes from rest0.gif for bare ground to rest7.gif for a hobo fortress.) If you're likely to add that any time in the next weeks or two, then I wouldn't mind using the function in advance of implementation. I'm taking my time writing the script so it doesn't need to work yet.

I THINK you can use to_item("potion of healing") to get the DoD healing potion; and that the result will be equal to $item[none] if it hasn't been identified yet. Let me know if that doesn't work, there are more certain (but more complicated) ways of finding that out.
[/quote]

Thanks! That works perfectly! print("Healing potion: " + to_string(to_item("potion of healing"))) gives me the expected answer, both with a character that has identified it and with a character that has not.

Now, here's a bonus. I've completed my restorative mapping so I'll attach it. I was a little dubious about including oyster eggs because they consume spleen, but I added them anyway and I'll just have to be careful with their use.
 

Attachments

  • create_restore_map.ash
    10.5 KB · Views: 57

jasonharper

Developer
Re: Hardcore recoveryScript

Hmmm, I hadn't even thought about how the housing data would be made available yet. It can't be as simple as a single function housing() returning a number from 0 to 7, as there are also the effects of bedding and other furnishings that need to be taken into account, but it would be fairly safe to assume that the 0..7 number could be derived from the available data somehow.

If you decide to go ahead and parse the campground yourself while waiting on that, don't forget that the image name can also be in the form "rest4tp.gif" or "rest4tp_free.gif" if you've been toilet papered.
 

Bale

Minion
Re: Hardcore recoveryScript

[quote author=jasonharper link=topic=1994.msg9668#msg9668 date=1226361655]
Hmmm, I hadn't even thought about how the housing data would be made available yet. It can't be as simple as a single function housing() returning a number from 0 to 7, as there are also the effects of bedding and other furnishings that need to be taken into account, but it would be fairly safe to assume that the 0..7 number could be derived from the available data somehow.

If you decide to go ahead and parse the campground yourself while waiting on that, don't forget that the image name can also be in the form "rest4tp.gif" or "rest4tp_free.gif" if you've been toilet papered.
[/quote]

Thanks. Hadn't thought of toilet papering.

I guess campground() could return a string which we'd parse for information. The string would probably be best as a comma separated list.

housing number = to_int( substring( campground(), 1, 1 ) )

Other items would be found by:
contains( campground(), "bean bag chair")
contains( campground(), "Really good Feng Shui")
contains( campground(), "Pagoda")
 

jasonharper

Developer
Re: Hardcore recoveryScript

Ok, first draft of campground equipment parsing is in r6502. It's not quite usable yet - in particular, the data isn't updated if you install new housing during a session - but it should get you started.

Here's a little sample script that prints out the campground items, and shows the expected HP/MP gains from resting:
Code:
int[item] camp = get_campground();
foreach key in camp {
	print(key + "=" + camp[key]);
}
print("");
print("Minimum HP restored:");
print((numeric_modifier("Base Resting HP")-1) * (numeric_modifier("Resting HP Percent")+100) / 100 + numeric_modifier("Bonus Resting HP"));
print("Maximum HP restored:");
print((numeric_modifier("Base Resting HP")+1) * (numeric_modifier("Resting HP Percent")+100) / 100 + numeric_modifier("Bonus Resting HP"));
print("Exact MP restored:");
print(numeric_modifier("Base Resting MP") * (numeric_modifier("Resting MP Percent")+100) / 100 + numeric_modifier("Bonus Resting MP"));
The HP/MP values should take everything into account - feng shui, pagoda, white picket fences, moon phases, and even the comfy blanket - so you probably won't need to look at the individual campground items at all, unless your script is going to do automatic housing upgrades prior to resting.
 

Bale

Minion
Re: Hardcore recoveryScript

[quote author=jasonharper link=topic=1994.msg9726#msg9726 date=1226658426]
Ok, first draft of campground equipment parsing is in r6502. It's not quite usable yet - in particular, the data isn't updated if you install new housing during a session - but it should get you started.
[/quote]

Wow. Thanks. I can't wait for the next daily build to be posted so I can test out that script and see the output.

You've managed to make my script even more complicated than it's already shaping up to be. I'm coming along pretty well incidently, though it isn't quite up to testing yet since its logic gets a bit complicated in places. There's just two spots where I'm still drawing a blank. Still... its shaping up to be something that will manage my healing with the same logic I'd want to use if I micromanaged absolutely everything. Testing this is gonna be a bitch.

Now a question: What does the function numeric_modifier() do? How does the data get to numeric_modifier("Base Resting HP") from camp[key]?
 

jasonharper

Developer
Re: Hardcore recoveryScript

[quote author=Bale link=topic=1994.msg9727#msg9727 date=1226660575]Now a question: What does the function numeric_modifier() do? How does the data get to numeric_modifier("Base Resting HP") from camp[key]?
[/quote]
numeric_modifier accesses fields of your current modifiers, which are the sum of the individual modifier values (from modifiers.txt) for all of your current equipment. It's the same mechanism that lets mafia calculate a total item drop bonus, given that you have perhaps a weapon, an accessory, and a familiar that each have an item drop bonus. r6502 just adds modifiers from campground equipment to the total, and adds six new available fields to describe resting effects. Here is the current list of fields you can inquire about (but note that many of these have specific ASH functions for retrieving them, which would generally be better to use):

"Familiar Weight", "Monster Level", "Combat Rate", "Initiative", "Experience", "Item Drop", "Meat Drop", "Damage Absorption", "Damage Reduction", "Cold Resistance", "Hot Resistance", "Sleaze Resistance", "Spooky Resistance", "Stench Resistance", "Mana Cost", "Moxie", "Moxie Percent", "Muscle", "Muscle Percent", "Mysticality", "Mysticality Percent", "Maximum HP", "Maximum HP Percent", "Maximum MP", "Maximum MP Percent", "Melee Damage", "Ranged Damage", "Spell Damage", "Spell Damage Percent", "Cold Damage", "Hot Damage", "Sleaze Damage", "Spooky Damage", "Stench Damage", "Cold Spell Damage", "Hot Spell Damage", "Sleaze Spell Damage", "Spooky Spell Damage", "Stench Spell Damage", "Critical", "Fumble", "HP Regen Min", "HP Regen Max", "MP Regen Min", "MP Regen Max", "Adventures", "Familiar Weight Percent", "Melee Damage Percent", "Ranged Damage Percent", "Stackable Mana Cost", "Hobo Power", "Base Resting HP", "Resting HP Percent", "Bonus Resting HP", "Base Resting MP", "Resting MP Percent", "Bonus Resting MP"

There is also a 2-parameter version of numeric_modifier, that lets you inquire about a modifier value from a specific item/effect/outfit/etc., whether or not you have it equipped at the moment. For example, numeric_modifier($item[Hobo fortress blueprints], "Base Resting MP") would return 85.
 

Bale

Minion
Re: Hardcore recoveryScript

Wow. Thank you. And thanks for enumerating all the parameters that can be used with it.

So, that function would enable me to write a script that could enumerate every item that is involved in mafia's calculation of my item drop bonus and how much each item contributes towards the total... Interesting. Maybe someone else will write that script.
 

Bale

Minion
Re: Hardcore recoveryScript

After nearly two weeks I've finally reached the bugtesting phase. That's the good news. Unfortunately the bad news is that either I've found a serious bug in my script or else I've found a serious bug in "set recoveryScript = " and I'm not sure how this bug could be in my script.

I've just started testing my script and I find that it will always try to restore if my hp/mp is less than the restoration target, not the restoration trigger. For instance, with these settings:

restorationsettings.png


It tries to restore every time my health is less than 25% or my mana is less than 10%. That should mean mafia is calling my restoration script, even when it shouldn't be. I'm putting off further testing of my script until you can confrim that it is being called properly.
 

jasonharper

Developer
Re: Hardcore recoveryScript

The recoveryScript gets called whether or not any recovery needs to be done; indeed, the script itself DEFINES whether or not recovery needs to be done. It's not required to implement the same threshold/target levels as the built-in recovery mechanism, it could choose to vary those levels based on your stats, the area you're adventuring in, etc.
 
Top