Feature Commands to check for base/directed ML monster stats

nworbetan

Member
I've been parsing these base attack and defense values out of monsters.txt in an ash script for a while. When I first realized that I really did need the base, unmodified monster defense, I looked into adding a getBaseDefense() function to MonsterData.java and exposing it via an ash function in RuntimeLibrary.java. It looked easier and more straightforward to do it in java, but since I didn't actually follow through I could have overlooked something. Would it be worth my time and effort to take a shot at this?
 

Theraze

Active member
The problem-issue basically is scaling monsters... since some use an unmodified ML, some use a modified ML, some have a ML cap, and some completely ignore ML... doing it easily is more difficult than it used to be when I wrote up a patch. Also easier... just takes overloading more of mafia's backends.
 

nworbetan

Member
Okay, so the first important thing I overlooked is the fact that you attached a patch on page one. I had read all of page two, and your first post on page one, and thought "Yay, someone else cares about this thing I care about!" and then posted.

So now that I've read a little bit more, adding the functions to give easy access to monsters base stats still looks easy enough that I can do it. Adding new functions that speculate "what would this scaling monster's stats be if I added X ml" does seem a little more complicated. The monster scaling expressions in monsters.txt are all correct though, right? It looks doable.

So since I have a somewhat less than 100% acceptance rate when I post a patch, and writing this (and especially testing for accuracy afterwards) will take a couple hours... If no devs feel like spending their own time developing this, would my hypothetical patch have a greater than 0% chance of at least being considered?
 

Theraze

Active member
Base stats are possible currently on everything, including scaling monsters. The 'normal' commands should give results based on current ML levels.

The question is whether or not we want to have commands to be able to predict stats if we have different ML levels...
 

roippi

Developer
would my hypothetical patch have a greater than 0% chance of at least being considered?

There is a nonzero chance, yes. Problem is that there haven't been too many of us devs lately stepping up to review/commit patches, and vetting is a time consuming and mostly thankless job.

It definitely helps if your patch is well-commented.
 

nworbetan

Member
Base stats are possible currently on everything, including scaling monsters. The 'normal' commands should give results based on current ML levels.

The question is whether or not we want to have commands to be able to predict stats if we have different ML levels...

If there's a way to get the unmodified (i.e. not multiplied by .9) defense out of KoLmafia without parsing it directly from monsters.txt, I'll be both grateful and impressed if you can tell me what it is. I've looked, I've tried, I came up empty handed.

As to the "do we want..." question, I kind of assumed you had a specific use in mind when you suggested those functions in the first place? Off the top of my head, it seems like it could make scripting things like clans of cave bars easier.

There is a nonzero chance, yes.

Sweet, I'll do what I can then.
 

slyz

Developer
If there's a way to get the unmodified (i.e. not multiplied by .9) defense out of KoLmafia without parsing it directly from monsters.txt, I'll be both grateful and impressed if you can tell me what it is.
Probably something like this, for monsters who don't have an ML cap:
PHP:
int defense = ceil( ( monster_defense( $monster[ dairy goat ] ) - numeric_modifier( "Monster Level" ) ) / 0.9);
 

Theraze

Active member
Originally I'd thought it could help scripts like Adventure Advisor, Harvest, and to a lesser extent, BCA.

But why would you want the "not 90%" defense? KoL doesn't use it... why do we care? Unless we're trying to show that the wiki got it wrong again...
 

fronobulax

Developer
Staff member
So since I have a somewhat less than 100% acceptance rate when I post a patch,

If it's any consolation i think just about every dev has committed something that was subsequently reverted or modified because the original was deemed Not A Good Thing.

The good news is my new job includes writing code. The bad news is I no longer have a single minded compulsion to tweak mafia because it is my only chance to write code. Thus I don't always seek out patches other people have done and decide whether I should commit them or not. However I do react to PMs. So, if you or anyone else, has a patch that is languishing and want some attention send me a PM that includes a link to the thread with the patch. I will either review and commit, tell you why I won't or post something in the thread. My general rule is that I have to understand the problem the patch solves or the feature it adds, I have to feel that unintended side effects are unlikely and I have to sense from the comments in the thread that more than one person is interested and none of the people whose opinions matter to me object.
 
If it's any consolation i think just about every dev has committed something that was subsequently reverted or modified because the original was deemed Not A Good Thing.

The good news is my new job includes writing code. The bad news is I no longer have a single minded compulsion to tweak mafia because it is my only chance to write code. Thus I don't always seek out patches other people have done and decide whether I should commit them or not. However I do react to PMs. So, if you or anyone else, has a patch that is languishing and want some attention send me a PM that includes a link to the thread with the patch. I will either review and commit, tell you why I won't or post something in the thread. My general rule is that I have to understand the problem the patch solves or the feature it adds, I have to feel that unintended side effects are unlikely and I have to sense from the comments in the thread that more than one person is interested and none of the people whose opinions matter to me object.

:) I will be sure to take you up on that offer...
 

nworbetan

Member
Probably something like this, for monsters who don't have an ML cap:
PHP:
int defense = ceil( ( monster_defense( $monster[ dairy goat ] ) - numeric_modifier( "Monster Level" ) ) / 0.9);
Because defense is an int, dividing by .9 regularly ends up being a number that's 1 less than the original defense. This same effect is the only source of uncertainty when deducing a monster's defense based on "how much damage did I just punch it for". Yes, for real.
Code:
ash for x from 100 to 111 { int y = x * .9; print_html(x + " => " + y  + " => " + ceil(y / .9)); }

But why would you want the "not 90%" defense? KoL doesn't use it... why do we care? Unless we're trying to show that the wiki got it wrong again...

It's not used anywhere nearly as frequently as the 90% value, but KoL does use the 100% defense value at least once per fight. A monster's +-min(5, 5%) variance is calculated on the 100% defense value, and then it's multplied by .9, and then all the other math happens and uses the 90% value. The one use I've gotten out of it is predicting with 100% accuracy that I'm about to hit a monster for at least x but no more than y damage. Niche, sure, but not entirely useless.

p.s. The unmodified_defense() and $monster[].unmodified_defense coding went just as quickly and smoothly as I thought it would. Here's a patch for that. I don't really know what comments to make about it to make it "well-commented" though. I didn't do anything interesting or creative enough to warrant an explanation, imo, it's pretty much carbon copies of existing code.

I need to learn more about Java to finish the speculative_attack($monster[], int) functions though (specifically: the right way to get a String value out of a MonsterExpression Object). It turns out that although the two concepts are thematically very similar, their code doesn't depend on each other at all, so a separate patch for each isn't a problem.
 

Attachments

  • unmodified_defense.patch
    3.5 KB · Views: 20

slyz

Developer
That happens because your x * 0.9 is stored as an int, not because of the ceil( y/ 0.9 ).

A better comparison to the line I wrote would be:
Code:
> ash for x from 100 to 111 { float y = x * .9; print_html(x + " => " + y + " => " + ceil(y / .9)); }

100 => 90.0 => 100
101 => 90.9 => 101
102 => 91.8 => 102
103 => 92.7 => 103
104 => 93.60000000000001 => 104
105 => 94.5 => 105
106 => 95.4 => 106
107 => 96.3 => 107
108 => 97.2 => 108
109 => 98.10000000000001 => 109
110 => 99.0 => 110
111 => 99.9 => 111
Returned: void

EDIT: oh duh, I see what you meant now. Mafia itself gives you y as an int.
 
Last edited:

Darzil

Developer
It's a kludge, but on 95% of non-scaling monsters, Att = Def (before the 90% reduction), and if different they are normally several points different. Maybe in your calculation you can assume that if Def / 0.9 is within 1 point of Att, that Base Def = Att.
 

xKiv

Active member
That happens because your x * 0.9 is stored with finite precision]
FTFY. 0.9 can't be represented exactly as a float/double, and x * 0.9 will almost definitely end up being slightly wrong, and (x * 0.9) / 0.9 *might* round to the "correct" answer, but isn't guaranteed to do that.
(and then getting the x*0.9 approximately as an int makes it even worse, yes)
 

nworbetan

Member
Mafia itself gives you y as an int.

As far as I've seen (while debugging this script), KoL uses the int value floor(<the Def values in monsters.txt, +-min(5, 5%)> * .9) for defense, just like mafia. It looks to me like the rounding leaves some telltale quirks in the damage distribution, but I haven't spent enough turns in one zone to "prove" that hypothesis to any degree of statistical significance yet. I used ceil(y / .9) because that's the best you can do to try to recover the original after it's been through the lossy floor(x * .9) procedure.

I'm actually wondering now if I didn't go far enough in that unmodified_defense.patch, because how can I call the defense "unmodified" if it still includes +ML and beeosity()? I like the unmodified_defense() behavior in this patch much much better than that first one. This one returns exactly what it gets from monsters.txt, no more and no less, and it doesn't touch the standard MonsterData.getDefense() at all.

The speculativeStat() functions on the other hand... I think at this point it's safe to say I'm in over my head. Don't get me wrong, speculativeHP() seems to work perfectly. It can iterate all the way through "ash foreach m in $monsters[] print_html(speculative_monster_hp(m, 1234))" without a single problem. The really really strange thing is that speculativeAttack() is the exact same function, and fails intermittently for reasons that I can't even imagine.

If anyone wants to help troubleshoot where these null pointers are coming from, or just wants to laugh at how terrible these unfinished speculation functions are, feel free. :)
 

Attachments

  • these_speculative_functions_are_awful.patch
    7.5 KB · Views: 19
Top