Access to Internal Data

holatuwol

Developer
A lot of people seem to be writing ASH libraries which equate to access to data that KoLmafia manages anyway through the course of doing what it does. So, I decided I'd create this thread to address this -- why waste time writing giant libraries and maps for data KoLmafia already has?

If you have the suspicion that there's information that KoLmafia uses in order to do its internal data management, post the kind of information you think KoLmafia has access to, give a name for the ASH version of the method and give justification for why this is a good name for that method. My hope is that anyone can READ ASH scripts to find out what they're doing and I prefer highly "literate" function names.

That's about it -- new functions which KoLmafia does not have access to will need some sort of GUI equivalent to justify their existence (otherwise, an ASH library script is just as good). So, if you'd like KoLmafia to handle something internally, instead of having to import a library script (ie: you think the idea should be included in KoLmafia), you're more than welcome to produce an idea on how it can be used in the GUI and we'll consider it. But, even if we like it, you'll have to do the data entry, meaning you have to specify a format. :) That's about it!
 

holatuwol

Developer
While I see reasons for providing the information, which can be useful (as was the case when adding $stat[none]), I was asking for reasons why you believe it to be a good name.  This can be as simple as providing a code sample showing how it's easy to understand from context, without explanation or comments.  Therefore, while I'm sure the data is useful:

Verdict: All your suggestions have been rejected because you have failed to satisfy the request requirements.
 

macman104

Member
[quote author=holatuwol link=topic=250.msg1256#msg1256 date=1151647070]I was asking for reasons why you believe it to be a good name.[/quote]I'm a little confused by that statement. Does that mean he didn't explain why his function names are good ones? Just looking for clarification.
 
Hmm, access to internal data...I could build quite a list. I'll try to limit them though.

boolean have_musshroomplot() return true if the player has one, false if the don't.

Code:
if(have_mushroom_plot())
  {
  int PreFarmShrooms;
  int iterations;
  PreFarmShrooms = item_amount($item[knoll mushroom]);
  cli_execute("field harvest");
  if(item_amount($item[knoll mushroom]) > PreFarmShrooms)
    {
    iterations = 1;
    While(iterations != 17)
      {
      cli_execute("field plant " + int_to_string(iterations) + " " + MushroomToFarm);
      iterations = iterations + 1;
      }
    }
  }

effect skill_to_effect(skill my_skill) returns the effect given by casting the skill, none if the skill is not a self buff.
skill effect_to_skill(effect my_effect) skill_to_effects counterpart returns the skill needed to obtain an effect. None if the effect is not given by a skill.

adv_per_cast(skill my_skill) returns the number of adventures of the effect given by a 1 cast of a skill 0 or -1 if the skill passed does not give an effect.

int skill_cost(skill my_skill) returns the number of mp used for 1 cast a skill or:
int cost_to_cast(int times, skill myskill) would return the total cost to cast the given skill the given number of times

finally, the last request in this group is actually not a request for internal data, but more of a bypass of the way things are normally handled which will fit into the scheme of my other requests:
restore_mp(int restore_to) would ignore the fact that I have auto restore disabled, and restore mp to the amount specified or higher the best possible way in which kolmafia can see.
restore_hp(int restore_to) would do the same as above, but for hp

the following function would allow turning off auto restore so that I am not restoring after I self buff but instead will restore only before I self buff. Usefull with the saucephere buff, the star starfish, and when meat/mp restorers are at a premium.
Code:
void DoBuff(skill buff, int length)
{
int TotalTimesToCast;
int LengthNeeded;
int MaxTimesCast;
LengthNeeded = length - have_effect(Skill_To_Effect(buff));
if(LengthNeeded > 0)
  {
  TotalTimesToCast = (length / Adv_Per_Cast(buff)) + 1;
  MaxTimesCast = my_maxmp() / skillcost(buff) - 1;
  if(MaxTimesCast < TotalTimesToCast)
    {
    While(TotalTimesToCast > MaxTimesCast)
      {
      restore_mp(MaxTimesCast * skill_cost(buff));
      use_skill(MaxTimesCast, buff);
      TotalTimesToCast = TotalTimesToCast - MaxTimesCast;
      }
    }
  restore_mp(TotalTimesToCast * skill_cost(buff));
  use_skill(TotalTimesToCast, buff);
  }
}

next up: stat info

stat my_zodiac_stat() returns muscle if you are a muscle sign and so on. This can be determined in a script already with the following function:
Code:
Stat my_zodiac_stat()
  {
  if(my_zodiac() == $zodiac[wallaby] || my_zodiac() == $zodiac[vole] || my_zodiac() == $zodiac[mongoose]){return $Stat[Muscle];}
  if(my_zodiac() == $zodiac[Platypus] || my_zodiac() == $zodiac[Opossum] || my_zodiac() == $zodiac[Marmot]){return $Stat[Mysticality];}
  if(my_zodiac() == $zodiac[Wombat] || my_zodiac() == $zodiac[Blender] || my_zodiac() == $zodiac[Packrat]){return $Stat[Moxie];}
  return $Stat[none];
  }
but kolmafia processes internal functions much faster than functions written in a script.

Code:
if(my_zodiac_stat() == $stat[muscle])
{
buy(1, $item[spring]);
}
else
{
//start adventuring with objectives.
}

stat primary_stat() return the characters main stat.

Code:
if(my_primary_stat() == $stat[muscle])
  {
  eat(1. $item[knob sausage chow mein]);
  }

boolean have_outfit(string outfitname) returns true if you have the outfit, false if you don't.

Code:
if(!have_outfit("filthy hippy disguise"))
  {
  if(current_equipment($slot[hat]) != $item[filthy knitted dread sack] && item_amount($item[filthy knitted dread sack]) < 1){buy(1, $item[filthy knitted dread sack]);}
  if(current_equipment($slot[pants]) != $item[filthy corduroys] && item_amount($item[filthy corduroys]) < 1){buy(1, $item[filthy corduroys]);}
  }
cli_execute("outfit filthy hippy disguise");
 

holatuwol

Developer
macman104 said:
I'm a little confused by that statement.  Does that mean he didn't explain why his function names are good ones?  Just looking for clarification.
Correct.  One of the reasons I'm phrasing the request this way is I find that I read through built-in functions fine (with a little struggle for things like stats handling), but I have trouble figuring out what people are trying to do in many ASH scripts when I'm trying to reflect on what that function does.

While I certainly can't force readable names in everyone's ASH scripts, I'd like readable names in KoLmafia's built-in functions.  Also, since the easiest way to show how a function is useful is through a code example, and that's the suggested reasoning behind documenting why it's readable, it also helps me see the way people plan on using the built-in function, and my hope is that people tweak the name of the function to the point where it's easily readable by anyone (especially me).

This is a highly subjective process, and I'm prone to reject a lot of ideas outright, either because people aren't following the requirements (ie: they don't provide a reason for the name of the method), or because I don't find the name readable (which might be me being anal-retentive).  In the second case, if I see a code sample, or some justification for why it's a good name, it's really easy for me to come up with a new name that's easy to read.

As a result, I'm sure a few people think to themselves, "Wow, what a waste of time" as I'm prone to change names to my liking or reject things, even after good explanation of how they're used, if I see no explanation of the name.  Possibly even Tirian, who made ten proposals and had them all rejected in one cold-blooded line of red text.  However, I'm not intentionally being a tyrant, or a jerk, or any of those things that might come to mind: I'd simply like to have persons submitting requests to do some of the work Veracity and I do before adding new ASH functions and to encourage a different mentality about ASH function requests.

In hindsight, I feel as though I was coming off as overly harsh rejecting the very first request, so this morning, I thought about adding a few of the requested functions that were self-explanatory (you know, the kinds of functions where people might think, "Why should I explain why that's a good name?  The answer is DUH).  But, they've disappeared (I'm guessing deleted for revision, or out of annoyance with me being a tyrant?), and I only remember two (Update: three).


boolean in_hardcore(): Added
slot item_to_slot(item): Added
boolean have_equipped(item): Added


If you go as far as a code example, if it's genuinely how you'd use it (I'm hoping this is the case), you're not doing any extra work, since when I say, "Approved" your script is done, and when I say "Renamed" you just replace-all and you're set.  I wasn't joking around when I made this a sticky: if it's truly something that KoLmafia maintains internally, and provided it's something I haven't rejected before (getting the response text from a raw URL request), it will be added if you provide your code example OR if you give me a good reason for the name.

efilnikufecin said:
Hmm, access to internal data...I could build quite a list. I'll try to limit them though.
Don't worry too much about limiting it: if you can come up with all the code examples for all the internal functions you'd like access to, and you still find all of them necessary, then feel free to make your request.  Now for your suggestions (Update: So that's where the have_ prefixes came from ... the existing functions in the ASH use have_.  Okay, I've undone the renames):


boolean have_mushroom_plot(): Added
effect skill_to_effect(skill): Added
skill effect_to_skill(effect): Added
boolean restore_hp(int): Added
boolean restore_mp(int): Added
int skill_cost(skill): Renamed to int mp_cost(skill)
stat my_zodiac_stat(): Renamed to boolean in_muscle_sign(), boolean in_mysticality_sign(), boolean in_moxie_sign()
boolean have_outfit(string): Added
stat my_primary_stat(): Renamed to stat my_primestat()


boolean outfit(string): Added to supplement boolean have_outfit(string)


As a side note, I have also changed skill usage to automatically trigger restoration wherever needed, regardless of HP/MP restore settings.  Also, mp_cost does not check whether or not you have the skill; it simply lets you know what the cost of casting the skill would be if you had the skill.  Passives return a value of zero.


int adv_per_cast(skill): KoLmafia does not maintain this data.  The buffbot module recalculates it whenever it needs to for buffs, which are easy, since there are well-defined rules for when it's 5, 10 or 15, and you have the luxury of assuming a rock and roll legend for anyone running a buffbot, but other than that, KoLmafia has no idea what the adventure yield is for each skill.
 

macman104

Member
[quote author=holatuwol link=topic=250.msg1261#msg1261 date=1151698467]
Correct. One of the reasons I'm phrasing the request this way is I find that I read through built-in functions fine (with a little struggle for things like stats handling), but I have trouble figuring out what people are trying to do in many ASH scripts when I'm trying to reflect on what that function does.[/quote]Ok, sounds good. Thanks for the clarification.[quote author=holatuwol link=topic=250.msg1261#msg1261 date=1151698467]stat my_zodiac_stat(): Renamed to boolean in_muscle_sign(), boolean in_mysticality_sign(), boolean in_moxie_sign()[/quote]Could I suggest an alternative, one that requires only one function and returns a stat instead? A stat zodiac_to_stat($zodiac) which given a zodiac, would return either muscle, mysticality, or moxie, it also more closely reflects the script that it would replace
 

holatuwol

Developer
macman104 said:
holatuwol said:
stat my_zodiac_stat(): Renamed to boolean in_muscle_sign(), boolean in_mysticality_sign(), boolean in_moxie_sign()
Could I suggest an alternative, one that requires only one function and returns a stat instead?  A stat zodiac_to_stat($zodiac) which given a zodiac, would return either muscle, mysticality, or moxie, it also more closely reflects the script that it would replace

It seemed to me as though the only thing that was used from that method was to compare the result to either muscle, mysticality or moxie -- in of itself, it'd never be passed to a different method that would do anything other than, once again, test the result against muscle, mysticality or moxie.  So I renamed the method to match what it'd actually be used for.  Unless there's a planned use for it that I'm not seeing?


For comparison:

Code:
if ( my_zodiac_stat() == $stat[muscle] )
{
    buy( 1, $item[spring] );
}
else
{
    // start adventuring with objectives.
}

vs.

Code:
if ( in_muscle_sign() )
{
    buy( 1, $item[spring] );
}
else
{
    // start adventuring with objectives.
}
 

macman104

Member
[quote author=holatuwol link=topic=250.msg1264#msg1264 date=1151701422]It seemed to me as though the only thing that was used from that method was to compare the result to either muscle, mysticality or moxie -- in of itself, it'd never be passed to a different method that would do anything other than, once again, test the result against muscle, mysticality or moxie. So I renamed the method to match what it'd actually be used for. Unless there's a planned use for it that I'm not seeing?[/quote]Fair enough. /me scrounges for a good use that would convice holatuwol. Damn, I can't really come up with something. I guess, I'll just adjust, I just like the condensed one function thing, versus three different ones (albeit they're very similar). Your explanation makes perfect sense, so I've no qualms.
 

Nightmist

Member
[quote author=holatuwol link=topic=250.msg1261#msg1261 date=1151698467]

slot item_to_slot(item): Added
[/quote]
This pulls the data based on where the item sits in the inventory screen (im assuming it isnt reading each item desc since that would mean massive server hitting) or is there a database somewhere that has listed information (If there is im rather interested and could someone link me).

Other then that and me being curious the rest looks good ^^. Cant wait until 8.2 =D.
 

Tirian

Member
[quote author=holatuwol link=topic=250.msg1261#msg1261 date=1151698467]
In hindsight, I feel as though I was coming off as overly harsh rejecting the very first request, so this morning, I thought about adding a few of the requested functions that were self-explanatory (you know, the kinds of functions where people might think, "Why should I explain why that's a good name? The answer is DUH). But, they've disappeared (I'm guessing deleted for revision, or out of annoyance with me being a tyrant?), and I only remember two (Update: three).[/quote]

A combination of the two, and furthermore I didn't want anyone else to feel like they shouldn't independently duplicate my proposals if they thought that they were good ideas out of a sense either that they were "mine" or that they had been rejected. To be frank, I was frustrated that I spent an hour writing my proposals and you gave the appearance of spending much less time rejecting them (rather than asking for further input before consideration), but I assumed that we would both be of a clearer mind over the course of time.

I still have a copy of my list and I'm going to go through it more carefully and come up with some pseudocode for the unimplemented ideas. I don't think I've got a lot of notions about why my ideas for the names of the functions are outstanding, other than the fact that I am quite certain that they are the same name that you would have devised. :)
 
[quote author=holatuwol link=topic=250.msg1261#msg1261 date=1151698467]
If you go as far as a code example, if it's genuinely how you'd use it (I'm hoping this is the case), you're not doing any extra work, since when I say, "Approved" your script is done, and when I say "Renamed" you just replace-all and you're set.  I wasn't joking around when I made this a sticky: if it's truly something that KoLmafia maintains internally, and provided it's something I haven't rejected before (getting the response text from a raw URL request), it will be added if you provide your code example OR if you give me a good reason for the name.
[/quote]
I totally agree with the rejection of the request for the response text from a raw url request. We don't need any raw url based mall bots made from kolmafia.

All of the requests I made will replace a lot of the existing contents of my icypeak.ash script. This will reduce the memory used by my script, and reduce the overall size of the script which makes editing easier. Fact is I have to re-write the script because it contains a lot of work-arounds for previous fixed glitches in kolmafia or even kol itself.

[quote author=holatuwol link=topic=250.msg1261#msg1261 date=1151698467]
Don't worry too much about limiting it: if you can come up with all the code examples for all the internal functions you'd like access to, and you still find all of them necessary, then feel free to make your request.  Now for your suggestions (Update: So that's where the have_ prefixes came from ... the existing functions in the ASH use have_.  Okay, I've undone the renames):
[/quote]

I tried to parse through my script and find the functions which could be internal data. After finding all of them, I modified the names of the functions to match the general scripting commands already there. If I didn't see a comparable function the I tried to use a name that fit kolmafia's format. That is why the underscores were added to the names. For debugging I didn't use underscores in my function names so I could quickly distinguish between a built in function and one I wrote and forgot.

[quote author=holatuwol link=topic=250.msg1261#msg1261 date=1151698467]

boolean have_mushroom_plot(): Added
effect skill_to_effect(skill): Added
skill effect_to_skill(effect): Added
boolean restore_hp(int): Added
boolean restore_mp(int): Added
int skill_cost(skill): Renamed to int mp_cost(skill)
stat my_zodiac_stat(): Renamed to boolean in_muscle_sign(), boolean in_mysticality_sign(), boolean in_moxie_sign()
[/quote]

OK, pretty much right back to where it started. The original function was Boolean IsZodiacStat(stat Test) in my script for the same reason you gave 3 functions. I figured my_zodiac_stat() would be similar to my_hp and my_max_hp and their mp equivalents.

[quote author=holatuwol link=topic=250.msg1264#msg1264 date=1151701422]
It seemed to me as though the only thing that was used from that method was to compare the result to either muscle, mysticality or moxie -- in of itself, it'd never be passed to a different method that would do anything other than, once again, test the result against muscle, mysticality or moxie.  So I renamed the method to match what it'd actually be used for.  Unless there's a planned use for it that I'm not seeing?
[/quote]
I see one possibility...and it's a possibility only because ash does not support the feature yet, and may never support it:

Code:
case my_zodiac_stat() of
  :$stat[muscle]
      {
      //do something
      }
  :$stat[moxie]
      {
       //do something else
      }
  :$stat[mysticality]
      {
      //do something entirely different.
      }
I know the formatting is poor, but the idea is there, and if you think you will ever possibly add the functionality then making it compatible now would save work in the future.
[quote author=holatuwol link=topic=250.msg1261#msg1261 date=1151698467]
boolean have_outfit(string): Added
stat my_primary_stat(): Renamed to stat my_primestat()


boolean outfit(string): Added to supplement boolean have_outfit(string)


As a side note, I have also changed skill usage to automatically trigger restoration wherever needed, regardless of HP/MP restore settings.  Also, mp_cost does not check whether or not you have the skill; it simply lets you know what the cost of casting the skill would be if you had the skill.  Passives return a value of zero.
[/quote]

At this point I must say great! That's the majority of my requests with mild renames of the functions. It is not hard at all to adapt to the new names.

One question: does skill usage automattically trigger restore after using the skill as it does when auto restore is turned on, or just before?

[quote author=holatuwol link=topic=250.msg1261#msg1261 date=1151698467]

int adv_per_cast(skill): KoLmafia does not maintain this data.  The buffbot module recalculates it whenever it needs to for buffs, which are easy, since there are well-defined rules for when it's 5, 10 or 15, and you have the luxury of assuming a rock and roll legend for anyone running a buffbot, but other than that, KoLmafia has no idea what the adventure yield is for each skill.

[/quote]
I was unaware of that. I already have the function written that returns this data anyway so I am OK with it. The function isn't as massive as one might believe, it's just very hard to read because I condensed it as much as I could.

All in all I have to say thanks a million for your efforts. The changes are going to be great, and since icypeak.ash was running on the slow side on my machine, I will definitely be glad to take some of the data return functions out of it. The thread on this board for my current icy peak script is going to be moved to outdated scripts as soon as I do some editing.
 

holatuwol

Developer
Nightmist said:
... or is there a database somewhere that has listed information (If there is im rather interested and could someone link me).

The item data table (tradeitems.dat) stores how the item should be "used".  This ranges from using the item, using the item from the restores screen, using the item multiply, eating or drinking the item, transforming the item into a familiar, or equipping the item into an available slot.

Tirian said:
I don't think I've got a lot of notions about why my ideas for the names of the functions are outstanding, other than the fact that I am quite certain that they are the same name that you would have devised.

Don't worry too much about explanations of the names: the code samples are intended to replace that explanation, in the event that an explanation cannot be constructed.

Before I posted my rejection notice, I did recognize that each function had at least one or two sentences detailing how it would be used, so I knew that time had been invested in describing the use of the functions you wanted added, and a part of me thought, "Hey, he showed a substantial effort, I should let this slide."  At the same time, I felt it'd be a bad precedent to accept the functions as presented, as you hadn't done what I'd asked at all.

It took me awhile to come up with an idea to present the information I requested without resorting to full-blown legibility discussions (code samples), so while I didn't spend as much time as you did creating the proposal, it wasn't an immediate dismissal, as it might have appeared.  When I was replying, it was supposed to read as, "Probably yes, but follow instructions first," but I'm guessing that it read as, "No, the iron fist has spoken, go away."

Also, by providing code samples, I can ask questions like, "Is this easier to read, or is this easier to read?" based on the kinds of functions people are writing.  I'd just like to make it easier for me to discuss things, and rather than coming up with my own code examples (which might look nothing like how you guys wind up using them), using your code examples helps communication.

macman104 said:
I guess, I'll just adjust, I just like the condensed one function thing, versus three different ones (albeit they're very similar).  Your explanation makes perfect sense, so I've no qualms.

If I weren't going for readability, I'd have liked the condensed one function version as well, as it is the easiest way to think about things once you're used to imperative programming.  Simply call a function, cache its result, and test its value along the way.  But, I've also discovered that it's definitely not the natural way to read things.  So, unless stats are not being used strictly for equality tests, I've come to prefer allowance of multiple functions.

On that note, I might break on the stat day test if someone shows me a decent script with some legible function names, since KoLmafia does handle that data internally and the CLI already has that test available.  You can totally forget about holiday tests, though.

efilnikufecin said:
If I didn't see a comparable function the I tried to use a name that fit kolmafia's format. That is why the underscores were added to the names.

Oh no, I was looking at the "have" portion, actually.  I was wondering why it was that the functions weren't something like "has_" as it's more natural for me to say "has" than "have".  Then I realized that all of the ASH functions were have_ not has_.

efilnikufecin said:
One question: does skill usage automatically trigger restore after using the skill as it does when auto restore is turned on, or just before?

It used to always trigger after casting a skill (as well as before casting a skill), but this has not been true for some time, and nobody has reported any bugs regarding "loss in functionality" -- this means that whoever relied on it before has stopped relying on it now, or it's so infrequently required that nobody ever noticed.
 

Tirian

Member
Okay, you've already taken care of four of my proposals (in_hardcore, item_to_slot, have_outfit, and skill_to_effect -- I was only using has_equipped as an example of something that we could do with item_to_slot, but I sure don't mind it being added to ASH!), but while I was coming up with all of my code samples three new ideas came into my mind. Enjoy!

int pulls_remaining() - The number of pulls that I have remaining from Hagnk's today. I suppose that if I'm in softcore but not in ronin it would return a very large number and if I'm in hardcore but haven't rescured the king it would return 0.

Code:
void pull_for_feng_shui()
{
	if (pulls_remaining() < 3)
	{
		print("Sorry, you don't have enough pulls to get all the pieces");
		return;
	}
	take_storage(1, $item[decorative fountain]);
	take_storage(1, $item[Feng Shui for Big Dumb Idiots]);
	take_storage(1, $item[windchimes]);
	use(1, $item[Feng Shui for Big Dumb Idiots]);
}

Note that without the pre-check, I'd have wasted a pull or two that I might have used for something productive.

int ronin_remaining() - The number of adventures of ronin left. This seems like the opposite of pulls_remaining, returning -1 if I'm out of ronin and a very very large number if I'm in hardcore.

This sample is going to miss twenty different levels of subtlety, but let's assume for the moment that it's a fighter character who has already eaten and doesn't have Ode to Booze but would want to save as much drinking as possible for a post-ronin buffbot visit.

Code:
	[...]

	while (my_adventures() < ronin_remaining() && my_inebriation() + 4 <= drunkenness_limit())
	{
		drink(1, $item[fuzzbump]);
	}

	[...]

int drunkenness_limit() - returns either 15 or 20 depending on whether you have Liver of Steel. See the code sample above.

int still_lights() - It would be a nice part of a bedtime script to just make enough tonic waters to clear out the still. Maybe you're not catching this one at the moment.

Code:
	[...]
	buy(still_lights(), $item[soda water]);
	create(still_lights(), $item[tonic water]);
	[...]

boolean can_equip(item it) - Do I satisfy the stat requirements to equip this item?

This code sample is from the adventure-boosting portion of my bedtime script, specifically the part where you pick what weapon to wear.

Code:
boolean wear_if_can_and_have(item it, boolean worth_a_pull)
{
	if (!can_equip(it)) return false;
	if (item_amount(it) == 0)
	{
		if (!worth_a_pull || storage_amount(it) == 0 || pulls_left() == 0) return false;
		take_storage(1, it);
	}
	equip(it);
	return true;
}

boolean new_manage_arms()
{
	if (wear_if_can_and_have($item[time sword], true)
	{
		if (have_skill($skill[Double-Fisted Skull Smashing]))
			pull_to_wear($slot[off-hand], $item[time sword]);	
		return true;
	}

	item sword = $item[chrome sword];
	item xbow = $item[chrome crossbow];
	item staff = $item[chrome staff];

	if (wear_if_can_and_have(sword, false) return true;
	if (wear_if_can_and_have(staff, false) return true;
	if (wear_if_can_and_have(xbow, false) return true;
	if (wear_if_can_and_have(sword, true) return true;
	if (wear_if_can_and_have(staff, true) return true;
	if (wear_if_can_and_have(xbow, true) return true;

	return false;
}

At the moment, manage_arms() is much longer because I have to query for moxie and muscle for each weapon instead of being able to pass it to the helper function. (Note also the use of pulls_left() so that I wouldn't have to check after the pull to make sure that it succeeded.)

item familiar_equipment(familiar pet) - I suppose that there is at least one familiar with more than one piece of specific equipment, but knowing the general one and having the new ones being added as quickly as they are supported in KoLmafia would be great.

This might be a little trickier, so I'll show you part of the code as it exists at this moment and you'll see the dilemma. This is something that I would run just out of ronin that would put all of the familiars in their gear. I've got a map elsewhere that contains the data except that it falls out of date more regularly than your data does.

Code:
boolean dress_pet(familiar pet)
{
	item it = Mfam_equipment[pet];
	if (item_amount(it)==0) return false;
	if (!equip_familiar(pet)) return false;
	if (!equip(it)) return false;
	if (item_amount(it)>0) sell_item(item_amount(it), it);
	return true;
}

void main()
{
	familiar at_start = my_familiar();
	foreach pet in Mfam_equipment dress_pet(pet);
	equip_familiar(at_start);
}

The hitch is that I am using Mfam_equipment both for its associative qualities and as an iterator through the familiars. I suppose that exposing some data as an immutable map instead of a function might be more an offer than you had in mind, but you might be able to come up with a way around the problem.

boolean is_song(skill sk) and int song_count() - I'm glad that you implemented the skill to effect function. If part of your mood overhaul is a greater attention to AT buffs and their limitations, then this would be another useful thing to expose to scripts.

Here is part of my working code, in which this is implemented with a set (by which I mean a map in which the values are irrelevant) and another function.

Code:
boolean skill_upkeep(skill sk)
{
	if (!have_skill(sk)) return false;
	if (have_effect(Mskill_to_effect[sk]) > 0) return true;
	if (Ssong contains sk && active_songs() == 3) return false;
	use_skill(1, sk);
	return (have_effect(skill_to_effect(sk)) > 0);
}

void main()
{
[...]
	if (my_maxmp() >= 15) skill_upkeep($skill[empathy]);
	if (my_maxmp() >= 12) skill_upkeep($skill[leash of linguini]);
	if (my_maxmp() >= 40 && my_level() < 11) skill_upkeep($skill[aloysius' antiphon of aptitude]);
	if (my_maxmp() >= 7) skill_upkeep($skill[polka of plenty]);
[...]
}

boolean can_adventure(location spot) - I know that there are a few spots where you determine if a location is legal for adventuring before visiting. It would be similarly useful for us to not have to calculate if the Icy Peak or Valley was unlocked. Ideally, this functionality could eventually be strengthened to cover most of the locations and checking stat values and/or zodiac signs or whatever else would be necessary to pre-determine if an adventure would be legal. In the mean time, I would accept that this function would throw an error message and abort if it was queried about a location that you didn't have a test for.

I'm not sure exactly how pervasive the current functionality would be, but as I'm pretty sure you check the Valley at the moment, we could check to make sure that they don't accidentally try to get the abridged dictionary twice:

Code:
{
[...]
	if (can_adventure($location[valley beyond orc chasm]))
	{
		print("You have already finished the dictionary subquest!");
		return true;
	}
[...]
}
 
boolean have_familiar(familiar tocheck) returns true if you have the familiar either in your terrarium or equipped.

Code:
if(have_familiar($familiar[coffee pixie]))
  {
  equip_familiar($familiar[Coffee pixie]);
  }
  else
  {
  equip_familiar($familiar[leprechaun]);
  }
This would be an alternate to previously used code:
Code:
equip_familiar($familiar[leprechaun]);
cli_execute("familiar.php?action=newfam&newfam=22");
which did the exact same thing but if you have the coffee pixie then the leprechaun would be equipped followed by the Coffee Pixie.
The reason for the direct link is to avoid halting of the script when you don't have the familiar. I don't know if kolmafia still aborts at this moment, but I do know that it once did, and this was the workaround I came up with.

How does kolmafia currently handle the following?
Code:
equip_familiar($familiar[leprechaun]);
equip($item[lucky tam o shanter]);
I haven't tried it, but kolmafia's familiar trainer steals familiar equipment from other familiars as needed. Is this also the case with scripted commands to equip an item?
 
I go into these suggestions with my eyes wide open. In other words, I do not expect them to be welcomed with open arms, if they are welcomed at all. The reasoning behind that is because I know there is potential for abuse, even potential that I can not see. But I have tried to eliminate that abuseability of them as much as possible.

That being said:

Store managing helper:
Code:
boolean at_mall_minimum( [item to check])

example:

if( at_mall_minimum( $item[disco mask]))
{
   take_shop( shop_amount( $item[disco mask]), $item[disco mask]);
   sell_item( $item[disco mask]);
}

The intended functionality is to be able to check your store for all of those items sitting at minimum selling price. That way you can just take them out and autosell them, since the likelihood of them selling at that price is pretty slim. (and since not every item mins at 100 meat, it is difficult to scan them using the store manager to see which ones should be removed.)


On a related note... With the autoselling price change that is coming up... I do not know how simple it will be to add the requested functionality... It might just be better to wait and see how that turns out before going forward.


Mall purchasing helper:
Code:
boolean cheaper_to_farm( [item to check])

example:

if( cheaper_to_farm( $item[box]))
{
   while( item_amount( $item[box]) < 1)
   {
     advnture( 1, $location[fun house]);
   }
}
else
{
   buy( 1, $item[box]);
}

The thought behind this one is that mafia tracks the approximate drop percentage of an item and can rough guess the average number of adventures it would take to farm any given item. Even if we give an arbitrary amount to the "price" of an adventure farming... Say, 1000 meat. Mafia can guess if it would just be cheaper to farm an item than buy one in the mall.

Now, I realize that most of the time cheaper_to_farm() will return true... But it would give scripters a minimal way to compare the mall prices of a given item, to make sure they are getting something of a deal...

And to cover my bases, if an item that is manufactured is checked (like a nothing-in-the-box) it should always return false. Also, as a secondary thought, have the "price per adventure" user definable. Now that global variables are allowed, there can be an optional system used variable: float adventure_price. That the individual scripter can set at the beginning of their script to use with cheaper_to_farm().

Of course... Not being aware of how the drop percentage is stored, this might be more work that its return value....

But then I am just thinking out loud anyway.
 

Veracity

Developer
Staff member
[quote author=efilnikufecin link=topic=250.msg1288#msg1288 date=1151828755]
boolean have_familiar(familiar tocheck) returns true if you have the familiar either in your terrarium or equipped.[/quote]

OK.

I haven't tried it, but kolmafia's familiar trainer steals familiar equipment from other familiars as needed. Is this also the case with scripted commands to equip an item?

Yes. The familiar-item-stealing is built into EquipmentRequest, so it will kick in any place in KoLmafia where you try to equip a familiar item.

I'll demonstrate the new function and equipment stealing:

Code:
print( have_familiar( $familiar[feather boa constrictor] ) );
print( have_familiar( $familiar[leprechaun] ) );

familiar fam = my_familiar();
item it = current_equipment( $slot[familiar] );
print( "Current familiar = " + fam + " wearing " + it );

equip_familiar( $familiar[leprechaun] );
equip( it );
print( "Current familiar = " + my_familiar() + " wearing " + current_equipment( $slot[familiar] ) );

equip_familiar( fam );
equip( it );
print( "Current familiar = " + my_familiar() + " wearing " + current_equipment( $slot[familiar] ) );

yields:

> fam.ash
false
true
Current familiar = Cheshire Bat wearing annoying pitchfork
Putting Grinning Grrl the Cheshire Bat back into terrarium...
Taking Lucky Grrl the Leprechaun out of terrarium...
Request completed.
Stealing annoying pitchfork from Cheshire Bat...
Putting on annoying pitchfork...
Gear changed.
Request completed.
Current familiar = Leprechaun wearing annoying pitchfork
Putting Lucky Grrl the Leprechaun back into terrarium...
Taking Grinning Grrl the Cheshire Bat out of terrarium...
Request completed.
Stealing annoying pitchfork from Leprechaun...
Putting on annoying pitchfork...
Gear changed.
Request completed.
Current familiar = Cheshire Bat wearing annoying pitchfork
Script succeeded!
 
Thanks Veracity!

I have another. Well it's not data, but it's a feature that kolmafia has built into the store manager...or similar anyway. I like the "end of run sale" feature, but I always have items I do not want kolmafia to modify the price of in my store. I would however like to put items like dry noodles, reagents, and cocktailcrafting ingredients in the mall and have kolmafia figure the price of those items based on the same principles it uses to price items in the main interface.

Best name I can see would be:
undercut_item(item tosell)
My personal preference would be that it would take all of that item in my inventory and place it in the mall at the price it would have done so in the event that I used the put_shop command followed by the undercut cli command. The key here is that it would not change the price of any other item in my store including the 999,999,999 priced items.

Code:
use_skill($skill[Pastamastery]);
undercut_item($item[dry noodles]);

Then my [insert item here] would not end up sold before I actually want to do so. Leaving the item tucked away in the mall is a way of preventing mistakenly sending it in a kmail when I didn't want to do it.

another minor clarification: My preference would be that it didn't care what the price already was on the item if in my store already. The net effect would be re-pricing the items each time as needed. Also, in the event that the item isn't in inventory but in my store, if the function is called then i think the results should be re-pricing what is alredy there.
 

Tirian

Member
Take your pick. :)

string last_encounter() - the name of the monster or non-combat encounter name faced most recently. If the enounter listing is empty, then this can return null.

int encounter_listing(string encounter) - this would return the number of times that the listed monster name or non-combat enounter name was faced in this session.

If anyone has been living under a rock and wants to spade out the new zone for themselves, they shouldn't read this code sample.
Code:
void white_citadel()
{
	cli_execute("guild.php?place=paco");

	while (last_enounter() != "It's A Sign")
	{
		adventure(1, $location[whitey's grove]);
	}

	item glider = $item[hang glider];
	boolean have_a_glider = false;

	while (!have_a_glider || last_encounter() != "Summer Holiday")
	{
		adventure(1, $location[the road to the white citadel]);
		if (item_amount(glider) > 0) have_a_glider = true;
	}

	cli_execute(...);
	cli_execute("guild.php?place=paco");
}
 

holatuwol

Developer

int pulls_remaining(): Added
int drunkenness_limit(): Renamed to int inebriety_limit()
int still_lights(): Renamed to int stills_available()
boolean can_equip(item): Added
item familiar_equipment(familiar): Added



int ronin_remaining():  KoLmafia calculates this on the fly; it does not maintain this information.  However, there is a very similar function which may help ASH scripts calculate this as well, and that has been added instead.

boolean can_adventure(location): Since KoLmafia can only do this by going to the server, this doesn't count as internal data.  Therefore, this request has been rejected.

boolean is_song(skill): KoLmafia calculates this on the fly (it converts the skill to an ID and checks to see if the ID is between 6001 and 7000); it does not maintain this information.  If it does get transformed into internal information (ie: the calculation gets centralized somewhere), this might become available.

int song_count(): KoLmafia calculates this on the fly, though in a way much messier than the previous one (it converts each effect to a skill, then the skill to an ID, then if the ID is between 6001 and 7000, and adds up the result); it does not maintain this information.  If it does get transformed into internal information (ie: the calculation gets centralized somewhere), this might become available.

boolean at_mall_minimum(item): I'm not sure what the "at mall minimum" function is intended to do?  Is this function testing if the current price is twice autosell or at exactly 100 meat, or...?

boolean cheaper_to_farm(item): KoLmafia doesn't track where items drop; it merely tracks what items drop where.  The difference is subtle, but it means that this function is not "internal data" at this time.

void undercut_item(item):  Every time an item is added, KoLmafia's memory of all the other items' prices are wiped.  This is why undercutting occurs in bulk, rather than one at a time, as KoLmafia always goes to the server to refresh this information.  So, if I were to add this function, it'd be two server hits per undercut, and it'd hit the slowest of the servers ... as such, no.

string last_encounter(): KoLmafia doesn't store this data anywhere ... it simply registers the encounter and throws the information away.  So, there's no way to provide this information.

int encounter_listing(string): This requires a little bit of code adjustment, and while a few of the others involved this as well, this is one of those, "Help you automate brand new content => no" features.



int my_turncount(): Added
 
[quote author=holatuwol link=topic=250.msg1400#msg1400 date=1152336551]

boolean at_mall_minimum(item): I'm not sure what the "at mall minimum" function is intended to do? Is this function testing if the current price is twice autosell or at exactly 100 meat, or...?
[/quote]

Well, it would be twice autosell, or 100 meat. Whichever is higher. But with the looming autosell nerf, I am not certain 2x autosell will be the actual minimum. So do not feel bad if you do not feel like adding it.

[quote author=holatuwol link=topic=250.msg1400#msg1400 date=1152336551]

boolean cheaper_to_farm(item): KoLmafia doesn't track where items drop; it merely tracks what items drop where. The difference is subtle, but it means that this function is not "internal data" at this time.
[/quote]

Understood. I suspected that might be the case. But it was the best "non-abusable" way I could think of for checking purchase prices to make sure a scripter was not getting royally screwed on a buy command.


Thanks for the consideration.
 
Top