ZLib -- Zarqon's useful function library

Theraze

Active member
But if you're comparing an exact string already, you don't need to re-normalize back, because presumably be_good(familiar.to_string()) should work based on the actual name of the familiar, not some bizarre subset. :)
 

Winterbay

Active member
But if you're comparing an exact string already, you don't need to re-normalize back, because presumably be_good(familiar.to_string()) should work based on the actual name of the familiar, not some bizarre subset. :)

That would require that the users always call the function with a normalised command, something which is not a given I would say. Especially for a function that takes a string as an argument.
 

Veracity

Developer
Staff member
That would require that the users always call the function with a normalised command, something which is not a given I would say. Especially for a function that takes a string as an argument.
I could hardly disagree more. It is not "the user" which is calling this function. It is "the program" - and I EXPECT programs to pass in "normalised" arguments.

In my opinion, the ONLY time KoLmafia should do fuzzy matching on a familiar, item, skill, effect, what have you is when it is looking up user input. There is no excuse for hard-coded data in a script to be anything other than the unambiguous name of the object.

If the user wants to type "volley" as a familiar, to_familiar() will fuzzy match that into $familiar[ blood faced volleyball ].

Now, it so happens that $familiar[ volley ] will ALSO currently match into that object - to my sorrow, since I have always opposed fuzzy matching in #[] constants - but, as soon as KoL introduces the "volley of boogers" familiar, your program has a compile error - which is exactly why I oppose such matching at compile time. Using non-exact constants in your ASH program is laziness on the part of the programmer. ASH caters to that laziness, but I am not proud of it.

If your program is calling the string version of is_trendy, it knows the kind of the object it is checking. There is no reason to call that version, given a familiar, item, or skill, since there are specialized versions for those. The intended use is for Clan Items and Campground items. For those, just as with items, skills, and familiars, there is absolutely no excuse for a program which is checking on the Pool Table, say, to pass in anything other than "Pool Table". If you are a lazy programmer and pass in "Pool", that will break as soon as there is a clan Swimming Pool. And it will break because you were lazy, not because KoLmafia didn't do magic fuzzy matching.
 

fronobulax

Developer
Staff member
In my opinion, the ONLY time KoLmafia should do fuzzy matching on a familiar, item, skill, effect, what have you is when it is looking up user input. There is no excuse for hard-coded data in a script to be anything other than the unambiguous name of the object.

Well said. Thank you.
 

zarqon

Well-known member
This function may very well be called on user input. But as my previous post showing how names convert to other types shows, fuzzy matching would make this function unreliable (e.g., if a user typed "ng", that would match a skill, an item, and a familiar, so which one should it check for trendiness). The function I posted tried to make a concession to users by calling to_item() only if direct comparisons to the other types didn't match, but even that may interfere with checking campground items. So I think we can reduce the entire Trendy bit to a single is_trendy(string) check, like so:

PHP:
boolean be_good(string it) {
   switch (my_path()) {
      case "Bees Hate You": return it.to_lower_case().index_of("b") == -1;
      case "Trendy": return is_trendy(it);
      case "Way of the Surprising Fist": return !($slots[weapon,offhand] contains it.to_item().to_slot());
   }
   return true;
}

Unless anyone has any convincing objections I'll make this change to be_good() in the next update.

Also, mad pr0pz0rz to Veracity for her eloquent post on the dangers of fuzzy matching. There are just a few specific cases where I do like fuzzy matching being there for $[] types: 1) things with markup or unusual characters in the name, and b) things with a single apostrophe in the name -- these blasted apostrophes screw up my code highlighter so I leave them out unless there are two such items on the same line to create a matched pair. These are low-risk shortcuts which I appreciate being able to take. I also use partial names occasionally when the name is just too dang long, to keep the code more readable, but this is slightly higher risk. To my thinking, these are the only excusable reasons for taking advantage of $[] fuzzy matching in a published script.
 

Veracity

Developer
Staff member
I also think fuzzy matching is useful in $[] constants in ash/ashq commands, since those are user input. But not for published scripts, as zarqon said.
 

StDoodle

Minion
For most of the cases of "screws up highlighting / special characters" there's an int option you can use, and you can easily comment what it refers to for clarity. This usually feels like the 'best' option to me.
 

zarqon

Well-known member
PHP:
foreach i in $items[this, that, these, those, xylinias pencil] {
   itemfunction(i);
   itemfunction2(i);
}

// --- vs. ----

foreach i in $ints[121, 232, 343, 454, 3113]  {  // this, that, these, those, xylinia's pencil
   itemfunction(to_item(i));
   itemfunction2(to_item(i));
}

Seems a good deal clearer to just delete the apostrophe.
 
Last edited:

StDoodle

Minion
PHP:
foreach i in $items[this, that, these, those, 3113] { // 3113 = xylinia's pencil
   itemfunction(i);
   itemfunction2(i);
}

:p

While this thread is active, can I revive the request to turn "1000" into "1,000" when using rnum()? ;)
 

Theraze

Active member
That would just be turning this:
Code:
   if (cr.length() > 4) for i from 1 to floor((cr.length()-1) / 3.0)
into this:
Code:
   if (cr.length() > 3) for i from 1 to floor((cr.length()-1) / 3.0)
right?
 

Winterbay

Active member
I could hardly disagree more. It is not "the user" which is calling this function. It is "the program" - and I EXPECT programs to pass in "normalised" arguments.

In my opinion, the ONLY time KoLmafia should do fuzzy matching on a familiar, item, skill, effect, what have you is when it is looking up user input. There is no excuse for hard-coded data in a script to be anything other than the unambiguous name of the object.

If the user wants to type "volley" as a familiar, to_familiar() will fuzzy match that into $familiar[ blood faced volleyball ].

Now, it so happens that $familiar[ volley ] will ALSO currently match into that object - to my sorrow, since I have always opposed fuzzy matching in #[] constants - but, as soon as KoL introduces the "volley of boogers" familiar, your program has a compile error - which is exactly why I oppose such matching at compile time. Using non-exact constants in your ASH program is laziness on the part of the programmer. ASH caters to that laziness, but I am not proud of it.

If your program is calling the string version of is_trendy, it knows the kind of the object it is checking. There is no reason to call that version, given a familiar, item, or skill, since there are specialized versions for those. The intended use is for Clan Items and Campground items. For those, just as with items, skills, and familiars, there is absolutely no excuse for a program which is checking on the Pool Table, say, to pass in anything other than "Pool Table". If you are a lazy programmer and pass in "Pool", that will break as soon as there is a clan Swimming Pool. And it will break because you were lazy, not because KoLmafia didn't do magic fuzzy matching.

You're arguments are all good, but they don't help with my own problem that an argument listed as an untyped "string" (and not familiar, item or other defined type) makes me feel that I should be able to pass whatever to it and either get a "real" repsponse or an error message back if that string wasn't what was expected of me to pass into the function.
But, as I said, that is a problem with my expectations of how things should work as very much a non-programmer.
 

slyz

Developer
I think Winterbay simply meant that declining be_good() into:
PHP:
boolean be_good( familiar it ) { return be_good( it.to_string() ); }
boolean be_good( item it ) { return be_good( it.to_string() ); }
...
would avoid scripter errors.

I'm off to rename my bloody-faced volleyball "volley of boogers" now.
 

zarqon

Well-known member
I agree that it would be useful to make is_trendy(string) print a message if the supplied string doesn't match anything. Much better than silently returning true -- someone could assume there was fuzzy matching to a specific thing when actually it was being treated as gibberish.
 
Last edited:

Veracity

Developer
Staff member
You're arguments are all good, but they don't help with my own problem that an argument listed as an untyped "string" (and not familiar, item or other defined type) makes me feel that I should be able to pass whatever to it and either get a "real" response or an error message back if that string wasn't what was expected of me to pass into the function.
But, as I said, that is a problem with my expectations of how things should work as very much a non-programmer.
If you write ASH scripts, you ARE a programmer.

I agree that it would be useful to make is_trendy(string) print a message if the supplied string doesn't match anything. Much better than silently returning true -- someone could assume there was fuzzy matching to a specific thing when actually it was being treated as gibberish.
Error messages again.

ASH is a programming language. When your ASH program calls a function, you get a (hopefully meaningful) return value back. If you pass in a bad argument and the function "prints an error message", what does your program do? Nothing. An "error message" is completely useless to an ASH program.

When I wrote is_trendy, I made a conscious, considered decision to return true for anything which typeii.php did not tag as untrendy. That file does not have a big list of every possible item, familiar, skill, and so on. It has a specific subset of such objects which are or will eventually become untrendy. There are many other objects which will never be listed in that file. Hell Ramen will never be unfashionable. Neither will a snorkel, or a mosquito, or Stream of Sauce. Therefore, is_trendy returns "true" for those items.

Suppose a new non-limited item appears tomorrow: dog pants. KoLmafia does not have it in its data base. If you ask if the general string "dog pants" is trendy, is_trendy will return true. It has no reason NOT to return true; the item does not appear in typeii.php as being Untrendy, so, if you happen to have the item, you are not restricted from using it.

Suppose you speculatively ask about "armadillo pants" - a bogus name. is_trendy returns "true", since typeii.php does not list it and, if you happen to have the item, skill, familiar - whatever it is! - the Trendy path will not prevent you from using it.

And yet, somehow, you want KoLmafia to detect that this random string is unknown and give an "error message"? Saying what? Something like "The string armadillo pants does not appear in typeii.php and is not recognizable as a known item, familiar, or skill, although it might be a Clan Item or a Campground Item which we don't know about yet"?

Would you prefer that the function be called is_not_trendy() and return true for exactly those objects which appear in typeii.php as restricted? Of course, if you do that and ask if "armadillo pants" are not trendy, you will be told "false" - which means, just as currently, that if you happen to have "armadillo pants", whatever they are, the Trendy path will not prevent you from using them. There is STILL nothing that is_not_trendy could do to detect that a random string will never match anything in that file.

The intended use for is_trendy is to apply it to the specific items which you have in inventory or storage - which are known to be items - or known familiars, or known skills or ... known whatever objects. You should never need to have an unknown string as an argument.

Please explain exactly how your PROGRAM would need to pass in an unknown string to is_trendy, rather than an item from inventory or a familiar from your terrarium, or some other known thing. Show me code.

Please explain how is_trendy COULD detect that an unknown string is not valid and never could be valid.

Summary: I don't see is_trendy() changing.
 

Winterbay

Active member
If you write ASH scripts, you ARE a programmer.

Well, yes in one sense of the word, but I'm not trained as a programmer. I've picked up bits and pieces here and there and can make an ASH-script. I would personally not define ASH-scripts as programs even though they share some characteristics. I could strech to say that I'm a scripter, but I can not (because I do not have the education or knowlegde to do so) write stand alone programs (such as Mafia), which is what my definition of a program is. But all this is semantics.

The way I've used is_trendy() mostly so far is by doing
Code:
ash is_trendy("something")
in order to check random things my mind comes up with wondering if it is trendy. For example I was confused that I could not cast Inigo the other day so I tried "is_trendy("inigo")" and of course got "true" back, so I went to the skill-specific version instead and got "false" (via the use of a to_skill). I could've used that from the beginning, but I did not expect that I had to do so because the function was (in my mind) untyped.
This is obviously not the way the function was intended to be used. I shall go and pu together an alias that does what I want it to do with the help of the way it is intended to work and leave this discussion.
 

Veracity

Developer
Staff member
The way I've used is_trendy() mostly so far is by doing
Code:
ash is_trendy("something")
in order to check random things my mind comes up with wondering if it is trendy.
As long as "something" is the correct name of an item, skill, familiar, clan item, or campground item, that works.

For example I was confused that I could not cast Inigo the other day so I tried "is_trendy("inigo")"
"inigo" is not the correct name of any such object. A knowledgable human can look at that and think of the skill, but a program would have to see if it was an item, see if it was a familiar, see if it was a skill, and so on.

You want to pass user input like "inigo" into is_trendy? Then you write a front-end function which will do the fuzzy matching for you.

is_trendy does not handle user input directly and do fuzzy matching on it. It is not appropriate for it to do so.
 

zarqon

Well-known member
Veracity, apologies; I may not have communicated clearly -- I wasn't suggesting that is_trendy()'s behavior change in any way from a program's point of view, merely that a helpful message would be printed to identify apparent misuses of the function (not an error; the function would still return true and operations would continue). The idea was that it would help scripters to use correct names for things, much like the "abc123 is not an integer; returning 123" message helps scripters better isolate their integers from page text. No scripter wants to see warning messages when he runs his script (script users don't care much for them either), so cluttering the CLI with something like "<inputtext> does not match any known item, familiar, skill, clan item, or campground item; returning true" when they specify a partial/bogus name would serve to 1) help scripters to either use correct names or normalize their parameters in $[], 2) help users fix problems due to having an outdated mafia installation more quickly, and 3) help mafia users who think like Winterbay did to better understand how the function is intended to work.

I can do it in the ZLib function we've been discussing if it would be needlessly bulky to implement or you're not convinced of its general usefulness.
 
Last edited:

Theraze

Active member
The problem with that, zarqon, is that currently is_trendy just checks if it's in the bad list. If it isn't, it assumes it's an okay item... and that's what you're requesting to have it display on. There's no way for is_trendy to tell if is_trendy("inigo") is wrong because there's no inigo or because it's allowed. Multiple examples:
> ash (is_trendy("shieldbutt") == is_trendy("some fake string"))

Seeing what's still trendy today...
Done. Are YOU a fashion plate?
Returned: true

> ash (is_trendy("shieldbutt".to_skill()) == is_trendy("some fake string"))

Returned: true

> ash (is_trendy("shieldbutt".to_skill()) == is_trendy("some fake string".to_skill()))

Returned: true

> ash "shieldbutt".to_skill()

Returned: Shieldbutt
level => 10
traincost => 10000
class => Turtle Tamer
libram => false
passive => false
buff => false
combat => true
permable => true

> ash (is_trendy("shieldbutt".to_skill()))

Returned: true
Basically, it will only return false if it is a match for the bad list. There's no checking to see why it's not on the bad list. And non-existent skills will say they're trendy just like existing skills, because they aren't on the disallowed list. Unless Jick has recently slipped in a skill named "some fake string" which would be rather bizarre. :)

The reason it has to be this way, if I'm understanding Veracity properly, is that some items such as the campground and clan items don't validate as 'objects' but are possibly trendy or not... so you can't throw out everything that doesn't match necessarily.
 
Last edited:

zarqon

Well-known member
Got it. I believe that falls under the "needlessly bulky to implement" category then.

Also hmmm, I noted a problem with using speculative stats in my_defstat(). If nothing has yet been speculated, speculative stats will return 0 until whatif is called once, even with no parameters. Is this a mafia bug or should I put a blank whatif at the top of any script which uses speculative values?
 
Top