BatBrain -- a central nervous system for consult scripts

@Crowther: Yes, actually mafia thinks you lose both the beehive and the boning knife when you use them in combat, but I believe that's a separate bug (which one of us should probably report).

They are reusable except against the intended tower monster. If mafia marked them combat reusable and didn't remove them from inventory when used against that monster, I don't think the combat list not having it would update that in mafia's inventory list.
 
Ok, as of r58 I've altered BatBrain (as per the above test script) to load batfactors, collapse the ranges, and set the banished counter info only once per session! It's a noticeable speed increase. If you need to trigger a reload of batfactors, you can either 1) save an edit to BatBrain, or ii) delete the map_batfactors line from zversions.txt (or just delete zversions.txt).

Thanks for bringing that to my attention Veracity!

@lost: So letting mafia's knowledge be temporarily incorrect whenever these items are used against most monsters is the best way to handle it? I could accept that, though it would take some self-convincing.
 
My first several test scripts wouldn't work until I figured out that variables need to be actually declared within the static scope for it to work, which makes it a little awkward to have it use a function:
Consider this:

Code:
static int mynum;
static boolean weneedtoreload = true;

void loadfactors() { 
    if (!weneedtoreload) {
	print("already loaded" );
	return;
    }
    print("loading..."); 
    mynum = random(100); 
    print("loaded"); 
    weneedtoreload = false;
} 

print("num: "+mynum);

static {
    print("static loading" );
    loadfactors();
}

print("num: "+mynum);

print("non-static loading 1" );
loadfactors(); 
print("num: "+mynum);

weneedtoreload=true;
print("non-static loading 2" );
loadfactors(); 
print("num: "+mynum);
which results in this:

> static2.ash

num: 0
static loading
loading...
loaded
num: 44
non-static loading 1
already loaded
num: 44
non-static loading 2
loading...
loaded
num: 13

> static2.ash

num: 13
num: 13
non-static loading 1
already loaded
num: 13
non-static loading 2
loading...
loaded
num: 82
1) You can have multiple static sections.
2) You can declare functions, etc., between static sections and later static sections can simply use them; no predeclaration needed.
2) A static "section" can be a single variable declaration.

Perhaps some of the "limitations" you refer to are "how it works", and my little example will make that a bit clearer and make it seem less "limited". ;)
 
They are reusable except against the intended tower monster. If mafia marked them combat reusable and didn't remove them from inventory when used against that monster, I don't think the combat list not having it would update that in mafia's inventory list.
Perhaps we could mark them as reusable and then manually remove them when they are actually removed when used against the expected monster.
 
1) You can have multiple static sections.
2) You can declare functions, etc., between static sections and later static sections can simply use them; no predeclaration needed.
2) A static "section" can be a single variable declaration.

That's incredible. That means that static can be used to maintain memory between executions of a single program within a single session of KoLmafia!

Code:
static int test1;
static test1 = 100;

print("Old value: "+test1);
test1 = random(99);
print("New value: "+test1);

Code:
> test1

Old value: 100
New value: 37

> test1

Old value: 37
New value: 84

> test1

Old value: 84
New value: 28

> test1

Old value: 28
New value: 68

Huh! I thought that could only be done by setting properties.
 
That's incredible. That means that static can be used to maintain memory between executions of a single program within a single session of KoLmafia!
That is the whole point of it. We compile your program the first time you execute it and save it in an "interpreter" and reuse the same interpreter every time you run that program again in the same session - unless you change the source file; we probe the modification date each time you want to run it and re-compile if it has changed.

Actually, a "single session" is probably "everything between starting KoLmafia and exiting back to the shell", including "logging in and out and back in again" multiple times. So, if you have user-specific data, this may not be the mechanism you need; per-user properties are your friends. But for "data which is created by a complicated computation and is the same for everybody", this is the ticket.

Huh! I thought that could only be done by setting properties.
Well, keep in mind that if you change the program, KoLmafia will recompile it next time you use it and all statics will be re-executed. properties are kept on disk, and if you really want to make them last "during a single session" rather than "until rollover", for example, you have to add in your own recognition of what a "session" is. static variables will almost certainly be easier - and faster.

This is just another tool in your toolbox. I was surprised, back when I added this to ASH, that nobody seemed to have anything to say. I guess that's because nobody was paying attention - or, perhaps, I didn't force enough examples in their face to let them recognize how they could use it. :)
 
This is just another tool in your toolbox. I was surprised, back when I added this to ASH, that nobody seemed to have anything to say. I guess that's because nobody was paying attention - or, perhaps, I didn't force enough examples in their face to let them recognize how they could use it. :)

I have used this a few times, but I did not realize how flexible the feature actually is. I'm rethinking many things now that you spelled it out.


Actually, a "single session" is probably "everything between starting KoLmafia and exiting back to the shell", including "logging in and out and back in again" multiple times. So, if you have user-specific data, this may not be the mechanism you need; per-user properties are your friends. But for "data which is created by a complicated computation and is the same for everybody", this is the ticket.

Thank you for that clarification. The multi-character aspect does let out one use I was considering.
 
Last edited:
Looks like the recent changes to modifiers broke BatBrain, which now always thinks you have 0 HP. I don't know enough about this to know how to fix it.

> ash numeric_modifier("_spec","Buffed HP Maximum")

Returned: 0.0

> whatif quiet

> ash numeric_modifier("_spec","Buffed HP Maximum")

Returned: 0.0

My current maximum HP is 1,048, which my_hp() returns correctly. Suggestions anyone? How do I correctly access modifiers now? I could revert to using my_hp() instead of numeric modifier, but that would be a step away from rather than towards using BatBrain predictively outside combat, which has always been a design consideration.

EDIT: Just checked, and reverting to 15460 allows BatBrain to correctly assess your HP.

EDIT: @Veracity, thanks for explaining further; that cleared up some misunderstandings I'd arrived at from my experimentation. I've been really enjoying the possibilities this presents for speed improvements to all my scripts. Also, I wasn't using the word "limitations" in a pejorative sense; I apologize if it came across so.
 
Last edited:
Ok, the r59 Update brings BatBrain up to speed with the modifier changes.

It also brings us some more batfactors parsing optimizations. In addition to tweaking the order of things as per Veracity's post, I have eliminated two functions: normalize_dmgtype() and parse_factors(), and have moved all of their functionality into various places around the script, for an improvement in both speed and functionality.

Previously BatBrain was foreaching each factor in batfactors and search/replacing the keywords "prismatic", "sauce", "pasta", and "perfect" with a calculated-each-time list of elements. Since very few of the entries actually contain any of these keywords, that was mostly a big waste of time. Not only that, but actually editing factors is undesirable in the event that you change monsters mid-fight or use BatBrain predictively outside of combat against a series of monsters, since the damage elements for some of those keywords may change with the monsters. So:

  • The code for replacing "prismatic" in batfactors is now performed in the static section and will thus only be performed once per session. This is just an expansion of shorthand and is not dependent on the monster.
  • The code for calculating "pasta" now happens in set_monster() and is saved to a global variable. Thus it should be recalculated -- but only once, instead of every time -- whenever you change monsters. It is applied on the fly when building the specific Pastamancer skills (there are now only 4) containing that keyword, without actually altering factors. Thus, if you change monsters, the new "pasta" element(s) will be recalculated.
  • Likewise, the code for determining "sauce" is now only performed when building the one skill remaining in the game that uses that keyword (Saucegeyser).
  • The code for determining "perfect" has also been moved into set_monster(), and is applied in to_spread() without altering the source.
  • The code for removing underwater-only items when not underwater has also been moved to set_monster(), though I don't think it will stay there, since, again, you may change locations when predicting, and then having options missing from factors would be undesirable. But for all presently known uses of BatBrain, this will continue to serve for now.

All told, the total performance increase between BatBrain of three revisions ago vs. now is massive. A difference of roughly an entire second in runtime (thanks mainly to Veracity's awesome "static" scope). I feel extremely happy about that, and I'm also excited for the actions-loading revamp that's on the way next. Actions whose results are not dependent on any variables will be loaded statically and will thus not need to be calculated each time, saving us even more time! BatBrain will conquer its nemesis once and for all!

As always, enjoy!
 
Hi zarqon. Firstly, thanks for all the effort you've put in for this script!!

I've recently updated my mafia and all SVN projects and now I'm getting an error when running WHAM (verbosity 11):

Code:
Evaluating '1.3*(65+(0.4*4793.0)+20.0+0.0) sauce'...
Expression syntax errors for 'modifier_eval()':
Expected end, found sauce (zlib.ash, line 188)
You're on your own, partner.

Seems that modifier_eval doesn't like the formula for SauceGeyser. Batfactors has the following:
Code:
skill	4012	Saucegeyser	spelldmgpercent*(65+(0.4*buffedmys)+spelldmg+elembonus) sauce	-min(1,effect(54))*1.12*mpcost	aoe 3, mp max(1,min(1,effect(55))*0.29*mpcost)
 
Last edited:
Not sure if this is the correct thing to do or not... But I removed the
Code:
 sauce
from the SauceGeyser formula in batfactors and that fixed the syntax error. Then went on to complain about the pasta formulas (removed the " pasta" from 4 formulas and now my WHAM runs)...

Not sure if the above is correct in terms of calculating the formulas, but at least my WHAM is working...
 
I did as you described and I still get this error:

Code:
Expression syntax errors for 'modifier_eval()':
Expected     end, found 0 (zlib.ash, line 188)
You're on     your own, partner.

could you please post how do your lines in Batfactors look so I can see what I am doing wrong?
 
r60 solves the issues with "sauce" and "pasta".

That was a particularly tricksy bug to track down. Turns out, oddly enough, it had to do with capping dmg_dealt() at monster HP. We now use the uncapped version of that function for that check.
 
I don't know why this is happening, but I tried the new version in my main character and it worked perfectly, but then I logged into my alt and once again, the same error is popping up
 
I'm getting an error on pantspower now:

Code:
Expression syntax errors for 'modifier_eval()':
Can't understand pantspower (zlib.ash, line 188)
 
And now r61 tries a slightly better way of doing what we did in r60.

I never get any of these errors (aside from the recent pasta/sauce issue) so I'm debugging what to me are unfindable/nonexistent bugs. Hope this is an improvement.
 
Back
Top