BatBrain -- a central nervous system for consult scripts

Theraze

Active member
Definitely not working right for unknown... I have it set to 0, and it's still automatically fighting without an abort.
 

zarqon

Well-known member
Mafia doesn't even know about bees yet. As of now, $monster[queen bee] generates an error. Note that it thought your stat gain was worth 10 meat. That's because the monster's stats were all 0.

So for now, looks like a regular CSS might be the only way to get special handling for bees, since consult scripts can't even recognize them.

This did prompt me to add additional debugging prints showing monster name/stats to SS though, which will wend their way into the next update. It also makes me wonder whether or not the "unknown_ml" setting is working.

And for future reference, verbosity 9 is ideal for debugging. Verbosity 10 just adds all the modifier evaluations, which we don't need to see unless you're getting an "Evaluator syntax error".

EDIT: Wow, ninjas all over the place. It appears further research into unknown_ml is necessary. Bale, good point about the kitties -- will add your tweak.
 
Last edited:

Theraze

Active member
unknown_ml is not working, but it's going through the consult scripts with the bees. The automatically-adding-monsters-to-mafia's-internal-database thing that got added recently made unknown monsters work with consult scripts, but I think it broke the unknown_ml stat thing as well.

I'm tired and repeating myself. Time to sleep. :)

Edit: Here's what they return. You're expecting 0, I think what you need to look for is 1 instead... problem is that there are some legitimate '1' stat mobs.
> last_monster

Returned: bee swarm

> monster_attack

Returned: 1

> monster_defense

Returned: 1

> monster_hp

Returned: 0

The relevant part would be this:
Code:
   if (m == $monster[none] || monster_attack(m) == 0 || monster_attack(m) == monster_level_adjustment())
      adj.att = to_int(vars["unknown_ml"]);
   if (m == $monster[none] || monster_defense(m) == 0 || monster_defense(m) == monster_level_adjustment())
      adj.def = to_int(vars["unknown_ml"]);
   if (m == $monster[none] || monster_hp(m) == 0 || monster_hp(m) == monster_level_adjustment())
      adj.dmg[$element[none]] = to_int(vars["unknown_ml"]);
becoming this, or something like it:
Code:
   if (m == $monster[none] || (monster_attack(m) == 1 && monster_defense(m) == 1 && monster_hp(m) == 0) || monster_attack(m) == monster_level_adjustment())
      adj.att = to_int(vars["unknown_ml"]);
   if (m == $monster[none] || (monster_attack(m) == 1 && monster_defense(m) == 1 && monster_hp(m) == 0) || monster_defense(m) == monster_level_adjustment())
      adj.def = to_int(vars["unknown_ml"]);
   if (m == $monster[none] || (monster_attack(m) == 1 && monster_defense(m) == 1 && monster_hp(m) == 0) || monster_hp(m) == monster_level_adjustment())
      adj.dmg[$element[none]] = to_int(vars["unknown_ml"]);
 
Last edited:

Winterbay

Active member
Actually what the script expects is for the unspaded monster to have every stat compared to monster_level_adjustment(), but if you mcd is set to 1 in that case I wonder why that didn't affect the hp.
 

Winterbay

Active member
What is odd is the fact that your monster-override reports 1 for attack and defense and 0 for hp while mine reported 0 for all three values...
 

zarqon

Well-known member
I ran elaborate tests on this a while ago -- some "unknown"/unspaded monster stat values include +ML, others don't. However, I re-tested just now and found another problem.

Filtering out the monsters which have all three stats at 0, here are some relevant monster stats at ML +15:

Big Creepy Spider (ATT 16, DEF 15, HP 16)
Brutus, the Toga-Clad Lout (ATT 0, DEF 0, HP 295)
Completely Different Spider (ATT 16, DEF 15, HP 16)
Crate (ATT 16, DEF 15, HP 16)
Danglin' Chad (ATT 0, DEF 0, HP 315)
Don Crimbo (ATT 0, DEF 0, HP 1000015)
Drunken Half-Orc Hobo (ATT 16, DEF 15, HP 16)
Essence of Tofu (ATT 0, DEF 0, HP 80)
Fiendish Can of Asparagus (ATT 16, DEF 15, HP 16)
Fluffy Bunny (ATT 16, DEF 15, HP 16)
Frog (ATT 3, DEF 3, HP 3)
Gorgolok, the Demonic Hellseal (ATT 0, DEF 0, HP 415)
Hellseal guardian (ATT 0, DEF 0, HP 225)
Hellseal pup (ATT 0, DEF 0, HP 103)
Hung-over Half-Orc Hobo (ATT 16, DEF 15, HP 16)
Lumpy, the Demonic Sauceblob (ATT 0, DEF 0, HP 515)
Monty Basingstoke-Pratt, IV (ATT 0, DEF 0, HP 255)
Mother hellseal (ATT 0, DEF 0, HP 172)
Mother Slime (ATT 0, DEF 0, HP 3015)
n Bottles of Beer on a Golem (ATT 15, DEF 15, HP 15)
A n Stone Golem (ATT 15, DEF 15, HP 15)
A n-Dimensional Horror (ATT 15, DEF 15, HP 15)
A n-Headed Hydra (ATT 15, DEF 15, HP 15)
Newt (ATT 3, DEF 3, HP 3)
Possessed Can of Tomatoes (ATT 16, DEF 15, HP 16)
Protector Spectre (ATT 0, DEF 165, HP 131)
Rushing Bum (ATT 16, DEF 15, HP 16)
Salamander (ATT 3, DEF 3, HP 3)
Skate Board Member (ATT 0, DEF 0, HP 765)
Sleeping Knob Goblin Guard (ATT 16, DEF 15, HP 16)
Slow Talkin' Elliot (ATT 0, DEF 0, HP 215)
Sub-Assistant Knob Mad Scientist (ATT 16, DEF 16, HP 16)
The Beast with n Ears (ATT 15, DEF 15, HP 15)
The Beast with n Eyes (ATT 15, DEF 15, HP 15)
The Big Wisniewski (ATT 0, DEF 0, HP 2015)
The Demon Spirit of New Wave (ATT 0, DEF 0, HP 515)
The Ghost of Fernswarthy's n great-grandfather (ATT 15, DEF 15, HP 15)
The Man (ATT 0, DEF 0, HP 2015)
The Whole Kingdom (ATT 16, DEF 15, HP 10000015)
Undead Elbow Macaroni (ATT 16, DEF 16, HP 16)
Unearthed Monstrosity (ATT 0, DEF 0, HP 515)

It looks initially like we can consider a stat to be "unknown" if stat <= +ML. However there are a few monsters, such as spiders and bunnies, where the defense is one less, meaning it would be considered as unknown_ml (erroneous, since the unadjusted stat is actually 1). Is this due to defense being 90% of attack? I guess this is calculated after adding the ML adjustment -- which causes us troubles, since it's not intuitively reversible at +ML of 10 or greater. For instance, at ML +20, the bunny's defense stat ((1+20)*0.9 = 19) minus ML adjustment would be -1. So we have to add 10% of +ML back in to arrive at the actual defense stat. Yikes.

Also note that exception needs to be taken for CLEESH monsters; +ML is not added to their stats either (should it be?).

EDIT: Also noticed something unusual with basement monsters. With no +ML, their stats are all 1. However, with +ML, their stats equal the monster level adjustment. ?!?!!!?!

ANOTHER EDIT TO NOTE: The defense stat issue starts to manifest at ML +9, not 10, since it's added to the defense stat before being reduced.

And this is all somewhat beside the point of actually unknown monsters. What does mafia say their stats are in combat? Further, what does mafia return for last_monster()?
 
Last edited:

Winterbay

Active member
I'll let you knwo as soon as I encounter my first bee of the day. I'll add some print statements to my consult script to make sure I get those.

Edit:
So, I added the following code to my Consult script:
Code:
vprint("You are fighting a " + to_string(foe) + ". Mafia considers that this monster has an attack of " + monster_attack() + " or " + monster_attack(foe) + " when given a monster name.", 9);
vprint("Mafia further considers that this monster has a defense value of " + monster_defense() + " or " + monster_defense(foe) + " when given a monster name.", 9);
vprint("Mafia further further considers that this monster has a HP value of " + monster_hp() + " or " + monster_hp(foe) + " when given a monster name.", 9);
vprint("Your current ML-adjustment is: " + monster_level_adjustment() + ".", 9);

Which gave the following output versus a beebee queue (first monster of the day actually):
Code:
You are fighting a beebee queue. Mafia considers that this monster has an attack of 1 or 0 when given a monster name.
Mafia further considers that this monster has a defense value of 1 or 0 when given a monster name.
Mafia further further considers that this monster has a HP value of 0 or 0 when given a monster name.
Your current ML-adjustment is: 0.

This indicates that the difference between my test and theraze's test earlier might've been that I tested with monster_hp($monstername) and Theraze tested with monster_hp()...

Edit, edit:
Also
Code:
> ash last_monster()

Returned: beebee queue
 
Last edited:

Winterbay

Active member
Back on the topic of aborting after noodling. I think the problem may be (it has at least been the case in many of my fights I've noticed) that the abort for low health is calculated before teh enqueuing of the noodles and thus the script aborts after I've cast noodles since it first casts noodles and then calls the batround-sub which includes the abort.
So, despite me being able to 1 or 2 shot the monster with my spells it aborts since the abort value is set too high (often around my max hp).
 

Bale

Minion
Abort for low health is calculated at 1 hit of the monster being able to kill you. That has nothing to do with noodles. However I see the problem. If you are noodling, that abort is irrelevant for 2 turns. Perhaps there needs to be two versions of batround? With and without the abort so that batroundsafe can be called for the first two rounds if noodling... I'd say it is getting complicated, but honestly it got complicated a long time ago.
 

Winterbay

Active member
The problem is that after it has noodled the chances of the monster one-shotting me is rather small...
I changed that line in batround from
Code:
res.append("; abort \"BatBrain abort: poisoned\"; endif; if hpbelow "+ceil(m_regular()+1)+"; abort \"BatBrain abort: Danger, Will Robinson\"; endif; ");
to
Code:
res.append("; abort \"BatBrain abort: poisoned\"; endif; if hpbelow "+ceil(m_dpr(0,0)+1)+"; abort \"BatBrain abort: Danger, Will Robinson\"; endif; ");

Part of the problem here is that m_regular() does not take stunning into account so it will stay the same no matter how stunned the monster is, while m_dpr(0,0) does take the planned stunning effect into account.
A recent fight against a pirate shows the following:
Code:
[575] Barrrney's Barrr
Encounter: Yes, You're a Rock Starrr
Encounter:     tetchy pirate
Strategy: D:\Mafia\ccs\PM.ccs [default]
Round 0:     winterbay wins initiative!
You lose 12 hit points
1     MP costs 5.882353µ.
1 HP costs     1.7647059µ.
bottle of rum (20.0 @     +30.0): 35µ * 26.0% = 9.1
cocktail     napkin (10.0 @ +30.0): 25µ * 13.0% = 3.25
Value of stat     gain: 185µ
bottle of rum (20.0 @ +30.0): 35µ *     26.0% = 9.1
cocktail napkin (10.0 @ +30.0):     25µ * 13.0% = 3.25
Value of stat gain: 185µ
Monster     value: 253.35
You will die in 7 rounds.
Your attack will kill     the monster in 4 rounds.
Profit per round:                             Action                          Profit                          Damage                          Other                                      base; Rogue Program (0µ)                          34.94µ                          5.94 (0 µ/dmg)                          31.5% stun chance MP: 5.94                      
    Building options...
Options     built! (25 actions)
Building custom     actions...
Custom actions built! (1 actions)
Custom     action: use 5120 (no stun)
Executing macro:     scrollwhendone; sub batround; if haseffect 436 || haseffect 8 || haseffect     264 || haseffect 282 || haseffect 283 || haseffect 284; abort "BatBrain     abort: poisoned"; endif; if hpbelow 12; abort "BatBrain abort: Danger,     Will Robinson"; endif; endsub; use 5120; call batround; 
Round     1: winterbay executes a macro!
Pirate insults known: 1 (0.00%)
Round     1: winterbay uses the Massive Manual of Marauder Mockery!
You acquire     an effect: Embarrassed (duration: 1 Adventure)
Round 2: tetchy pirate     takes 2 damage.
Round 2: tetchy pirate takes 4 damage.
You lose 13     hit points
Building options...
Options     built! (25 actions)
Stasis action chosen:     attack
Stasis loop complete.
SmartStasisStun     => 3.0
Executing macro: scrollwhendone; sub     batround; if haseffect 436 || haseffect 8 || haseffect 264 || haseffect     282 || haseffect 283 || haseffect 284; abort "BatBrain abort: poisoned";     endif; if hpbelow 1; abort "BatBrain abort: Danger, Will Robinson"; endif;     endsub; skill 3004; call batround; 
Round 2: winterbay executes     a macro!
Round 2: winterbay casts ENTANGLING NOODLES!
Building     options...
Options built! (24 actions)
SmartStasis     complete.

Which is what I would expect when I cast noodles.
 

zarqon

Well-known member
It originally used m_dpr() but that's actually a pretty useless number there -- if the monster doesn't hit it does 0, and if it hits it does more than m_dpr(). A number in the middle doesn't really help you not die, which is the point of the abort.

Whether we use m_dpr() or m_regular(), we can get unintended behavior since that number is set in stone when batround is submitted. If you enqueue noodles and then a series of attacks, and the monster can deal you a lot of damage, m_dpr() would return 0 since the monster is stunned, so the abort (if hp < 1, abort!) would be useless for the entire combat. Using m_regular() avoids that problem but as Winterbay and others found, it causes unwanted aborts when you are actually safe due to stunning. This gets more complicated in that it's possible to enqueue multiple stunners of varying durations at any point in the combat. In other words, that number needs to be variable rather than constant, which is impossible in a BALLS subroutine.

There are two ways around this.

1) Don't include a low HP abort at all. Allow people to macro themselves to death.
2) Take that abort out of batround. Instead, include a separate abort after each queued action, and only if the estimated stun at that point in the combat is 0.

I'm leaning towards 2, but could be easily persuaded towards 1, due to the extreme difficulty of tracking stunning during combat (which BatBrain doesn't and may never do).

Handling for stunning is probably the least developed part of BatBrain at the moment, so it's good to have this discussion. I do feel that m_event() was a really great move in the right direction, though. Basically, a monster is just a passive damage effect which rewards you for removing it. :)
 

Bale

Minion
1) Don't include a low HP abort at all. Allow people to macro themselves to death.
2) Take that abort out of batround. Instead, include a separate abort after each queued action, and only if the estimated stun at that point in the combat is 0.

3) Allow user defined abort threshold by checking autoAbortThreshold.

You forgot option 3. I'd also agree that 2 would be ideal. If you cannot manage that then please check autoAbortThreshold so that the player can decide the HP level he wants to abort combat or leave out the HP abort if it is '-0.05' since option 3 is clearly superior to option 1.
 

zarqon

Well-known member
Sorry? That's not an option -- adding a setting does not solve the problem of aborting when stuns are in effect.
 

slyz

Developer
I'm not sure solution 2) would work. If I understand correctly, when you add an "abort <condition>" line in a KoL macro, it stays active for the rest of the macro. So if your macro contains several "abort hpbelow X" lines, they will all stay active for the duration of the macro.

It would be pretty easy to test though, I'll go ahead and do just that in a few hours.
 

Bale

Minion
Sorry? That's not an option -- adding a setting does not solve the problem of aborting when stuns are in effect.

That setting is already part of mafia. It is set on the HP/MP Usage tab and mafia's default combat macros will abort when HP drops below that percentage of HP.

It doesn't solve the problem of how to handle stuns. But if you are going to escape that problem by allowing the user to macro themselves to death, it would make sense to use that setting instead.


I'm not sure solution 2) would work. If I understand correctly, when you add an "abort <condition>" line in a KoL macro, it stays active for the rest of the macro. So if your macro contains several "abort hpbelow X" lines, they will all stay active for the duration of the macro.

Actually, you misunderstand the two different ways of handling aborts. A top level abort which contains its own condition will be valid for an entire macro. However, if you tuck an abort into an if-endif statement and do not use a cond for the abort, then it is valid only when the if cond is true.
 

zarqon

Well-known member
@Bale: Oh! It still doesn't solve the problem we're talking about but it is nice in that it would allow users to choose which of two problems they want to have in effect. So, it's a viable workaround if option 2 doesn't work, but if it's something the script can automate better, I'd rather not put the burden on the user.

I like script transparency with mafia settings, so I will include that check, either to replace option 1 or to supplement option 2.

Haha, I suddenly imagined someone watching an automated combat and frantically adjusting their autoAbortThreshold property on the fly to account for stuns.

If Bale is correct about aborts in conditions (which I suspect he is, due to the lack of complaints since moving most of BatBrain's aborts inside conditionals), I will proceed with this after each action:

if (m_hit_chance() > 0) macro += "; if hpbelow"+max((m_regular()+1),autoAbortThreshold*my_stat("hp"))+"; abort dangerrobwillinson"

After each action, if the monster has any chance of hitting you, it adds an abort if your HP is less than one hit from the monster, or your autoAbortThreshold HP amount, whichever is higher. This also solves a similar problem with delevel-and-plink strategies (the monster's damage number will decrease correctly), potentially avoiding other unwanted aborts.
 

Winterbay

Active member
A construct such as
Code:
skill x
if hpbelow y1
   abort "Low health"
endif
call batround
skill z
if hpbelow y2
   abort "Low health"
endif
call batround

Could work I guess and allow recalcualtion of variables between each thing.
 

Winterbay

Active member
On a different note. The high level bees appear to, apart from being resitant to multi level stuns, to have a spell resistance of 50%. I'm going to guess that it's possible that the medium level ones have a 25% resitance but I've not tested that so far. Adding:
Code:
case $monster[queen bee]:
case $monster[beebee king]:
   foreach el in $elements[]
      mres[el] = 0.5;
   mres[$element[none]] = 0.5;
   break;

To the monster resistance part appears to have stopped my consult script from underkilling the bees today.
 

fronobulax

Developer
Staff member
Dirty old lich is not spaded, thus it has incomplete monster data in mafia, thus SS/BB treats it as an unknown and returns control to the user because I have not set any variables telling it to do otherwise, correct?

If I don't want that I can either
  • Wait until it is spaded and data is added to mafia.
  • Make a special entry in my CCS for it.
  • Set variables so SS/BB has a clue what to do.

For the latter, what are the variables? unknown_ml is 170 although it is not clear to me what a better value should be or even if the current versions of SS/BB use it in the expected manner.

Thanks.
 
Top