BatBrain -- a central nervous system for consult scripts

zarqon

Well-known member
Were you checking to make sure your get_action() results weren't empty? It will return an empty event if the action is not in your available combat options.
 

Bale

Minion
I'm now invoking my powers as a Minion.
Please note that this thread has a new first post.


@Bale: well done; your example has convinced me to add the tweak you suggested right now. The alternative in your above example would be to add noodles to the queue and then spam the queue with dangeraction, then call macro(), which is slightly inelegant in that it ignores the capability of BALLS to spam actions on your behalf.

I'm also going to clear the queue in macro() after executing it, so you need not bother clearing it anymore.

Thank you. I was really trying to find a way to write this with the macro queue before I gave up and did it that way. Briefly I had a version that queued up noodles and dangeraction with macro(), then spammed dangeraction in case the first dangeraction. It was... inelegant.


As far as whether you're using BatBrain correctly, yes. However, you are making unnecessary checks. If get_action() returns an empty event, the skill is unavailable. Otherwise, the skill is available. This will do the same as you posted:

PHP:
advevent dangeraction() {
   foreach da in $strings[skill 7008, skill 2005, skill 4012, attack]
      if (get_action(da).id != "") return get_action(da);
   return new advevent;    // you should never reach this
}

Oh. Oh wow. So I can count on BatBrain to recognize if a skill (like shieldbutt) is presently available! That's sweet.
 
Last edited:

Theraze

Active member
Managed to get negative rounds-to-kill count using the spamattack posted (currently) in thread 18, which sometimes ends in a division by zero error.

Regarding how I got to that point, using a ranged weapon as a myst class to clean up the easy mobs, not a muscle weapon, which might be part of why your calculation line is getting off... since you don't have either the 3/4ths damage for ranged weapons being calculated, or the +ranged damage if weapon stat == moxie.

Not sure exactly what's causing it, but BatBrain gives my expected damage above 10, while spamattack suggests I'll do 2...
> ash import <batbrain> regular(1);

Returned: aggregate float [element]
none => 10.355

> ash import <zlib> monster foe = to_monster(get_property("lastMonster")); float total_damage = min(floor(my_buffedstat($stat[muscle])) - monster_defense(foe), 0) + min(0.1 * get_power(equipped_item($slot[weapon])),1) + numeric_modifier("Weapon Damage") * (1 + to_float(numeric_modifier("Weapon Damage Percent"))/100.0) + min(0.1 * get_power(equipped_item($slot[offhand])),1);

Returned: 2.0
Believe the problem MAY be the various min... that means that you can never go higher than 0 for your muscle bonus-damage, can never have a weapon power above 1, and your offhand item (if it happens to be a weapon) can never go above 1... yeah, looks like that's probably the issue.

So, here's a line that should work better.
Code:
   total_damage = (max(floor((my_buffedstat($stat[muscle])) * (weapon_type(equipped_item($slot[weapon])) == $stat[moxie] ? .75 : 1)) - monster_defense(foe), 0) + max(0.1 * get_power(equipped_item($slot[weapon])),1) + numeric_modifier("Weapon Damage") + (weapon_type(equipped_item($slot[weapon])) == $stat[moxie] ? numeric_modifier("Ranged Damage") : 0)) * (1 + to_float(numeric_modifier("Weapon Damage Percent"))/100.0) + (to_slot(equipped_item($slot[offhand])) == $slot[weapon] ? max(0.1 * get_power(equipped_item($slot[offhand])),1) : 0);
Comparison of after/before:
> ash import <zlib> monster foe = to_monster("caustic bull"); int total_damage = (max(floor((my_buffedstat($stat[muscle])) * (weapon_type(equipped_item($slot[weapon])) == $stat[moxie] ? .75 : 1)) - monster_defense(foe), 0) + max(0.1 * get_power(equipped_item($slot[weapon])),1) + numeric_modifier("Weapon Damage") + (weapon_type(equipped_item($slot[weapon])) == $stat[moxie] ? numeric_modifier("Ranged Damage") : 0)) * (1 + to_float(numeric_modifier("Weapon Damage Percent"))/100.0) + (to_slot(equipped_item($slot[offhand])) == $slot[weapon] ? max(0.1 * get_power(equipped_item($slot[offhand])),1) : 0);

Returned: 85

> ash import <zlib> monster foe = to_monster("caustic bull"); int total_damage = min(floor(my_buffedstat($stat[muscle])) - monster_defense(foe), 0) + min(0.1 * get_power(equipped_item($slot[weapon])),1) + numeric_modifier("Weapon Damage") * (1 + to_float(numeric_modifier("Weapon Damage Percent"))/100.0) + min(0.1 * get_power(equipped_item($slot[offhand])),1);

Returned: 2
This also accounts for whether or not the item in the second hand is a weapon, if you are using ranged damage so your muscle bonus is only 75%, and that when using ranged damage, you also get bonuses from the 'ranged damage' modifier. Also, by adding more parenthesis, weapon damage percent should modify the entire weapon damage stack, not just the 'weapon damage' modifier.
 

Winterbay

Active member
Managed to get negative rounds-to-kill count using the spamattack posted (currently) in thread 18, which sometimes ends in a division by zero error.

Regarding how I got to that point, using a ranged weapon as a myst class to clean up the easy mobs, not a muscle weapon, which might be part of why your calculation line is getting off... since you don't have either the 3/4ths damage for ranged weapons being calculated, or the +ranged damage if weapon stat == moxie.

Not sure exactly what's causing it, but BatBrain gives my expected damage above 10, while spamattack suggests I'll do 2...Believe the problem MAY be the various min... that means that you can never go higher than 0 for your muscle bonus-damage, can never have a weapon power above 1, and your offhand item (if it happens to be a weapon) can never go above 1... yeah, looks like that's probably the issue.

So, here's a line that should work better.
Code:
   total_damage = (max(floor((my_buffedstat($stat[muscle])) * (weapon_type(equipped_item($slot[weapon])) == $stat[moxie] ? .75 : 1)) - monster_defense(foe), 0) + max(0.1 * get_power(equipped_item($slot[weapon])),1) + numeric_modifier("Weapon Damage") + (weapon_type(equipped_item($slot[weapon])) == $stat[moxie] ? numeric_modifier("Ranged Damage") : 0)) * (1 + to_float(numeric_modifier("Weapon Damage Percent"))/100.0) + (to_slot(equipped_item($slot[offhand])) == $slot[weapon] ? max(0.1 * get_power(equipped_item($slot[offhand])),1) : 0);
Comparison of after/before:This also accounts for whether or not the item in the second hand is a weapon, if you are using ranged damage so your muscle bonus is only 75%, and that when using ranged damage, you also get bonuses from the 'ranged damage' modifier. Also, by adding more parenthesis, weapon damage percent should modify the entire weapon damage stack, not just the 'weapon damage' modifier.

Well, yes. It's mainly programmed for me and as such it uses muscle as attack stat :)
This version uses batbrain and does so in a way that avoids division by zero which shows up in there as well when the expected damage I can do with an attack is 0, it should also work with moxie weapons (or at least I think so since batbrain probably takes care of that...).

Edit: The old version also had a parenthesis error in where it put the floor and does not take elemental damage bonuses into account.
 

Winterbay

Active member
Ok, so double posting here, but since the last one is rather old...

Is there a way to set the result of "last_monster()" to anything but the last monster? I wonder because if possible that would let me use batbrain to predict what type of damage I can do against a certain monster without having to fight it first. My consult script does this to a certain extent by allowing the use of the (foe)-versions of the different monster functions to adapt my numbers based on a given monster, but since batbrain seems useful I'd like to be able to incorporate more of that into my script, preferably without losing that ability.
 

Bale

Minion
I think you need to make a request for zarqon to overload BatBrain's functions for predictive usage. Though, the way he has it set up he'll have to refactor a lot of the code to implement.
 

Theraze

Active member
Well, might be able to just have a declaration like
Code:
monster my_monster = last_monster();
and then replace all of the occurances of last_monster() with my_monster... then if you want to check on a different monster, just declare
Code:
my_monster = $monster[Something Else];
in your BatBrain execution/alias...
 

Bale

Minion
Currently my FinalAttack.ash looks like this:

Code:
import "BatBrain.ash";

advevent dangeraction() {
	// Check moxious maneuver, shieldbutt, saucegeyser, attack
	foreach da in $strings[skill 7008, skill 2005, skill 4012, attack]
		if(get_action(da).id != "") return get_action(da);
	return new advevent;    // you should never reach this
}

boolean can_splash() {
	if(!have_skill($skill[Wave of Sauce]) || !have_skill($skill[Saucegeyser])
	  || !(have_effect($effect[Jabañero Saucesphere]) >0 || have_effect($effect[Jalapeño Saucesphere]) >0))
		return false;
	if(dmg_dealt(get_action($skill[Wave of Sauce]).dmg) >= monster_stat("hp")) return false;
	int normal = numeric_modifier("spell damage");
	if(normal >= 25)
		return true;
	int cold = numeric_modifier("cold spell damage");
	int hot = numeric_modifier("hot spell damage");
	if(have_skill($skill[Immaculate Seasoning]) && cold != hot && max(cold, hot) + normal >= 25)
		return true;
	if(monster_element() == $element[cold] && cold + normal >= 25 
	  && (have_skill($skill[Immaculate Seasoning]) || have_equipped($item[Gazpacho's Glacial Grimoire])))
		return true;
	if(monster_element() == $element[hot] && hot + normal >= 25
	  && (have_skill($skill[Immaculate Seasoning]) || have_equipped($item[Codex of Capsaicin Conjuration]) 
	  || have_equipped($item[Ol' Scratch's manacles]) ||(have_equipped($item[Ol' Scratch's ash can]) && my_class() == $class[Sauceror])))
		return true;
	return false;
}

string splashSauce() {
	queue[count(queue)] = get_action($skill[Wave of Sauce]);
	queue[count(queue)] = get_action($skill[Saucegeyser]);
	return macro("mark spashStart", "goto SpashStart");
}

void main(int initround, monster foe, string page) {
	page = act(page);
	switch(foe) {
	case $monster[a.m.c. gremlin]:
		foreach da in $items[divine champagne popper, tattered scrap of paper]
			if(get_action(da).id != "") {
				page = get_action(da);
				return;
			}
		break;
	case $monster[clingy pirate]:
		if(get_action($item[cocktail napkin]).id != "") {
			page = macro(get_action($item[cocktail napkin]));
			return;
		}
		break;
	case $monster[mother hellseal]:
		if(get_action($skill[entangling noodles]).id != "")
			queue[count(queue)] = get_action($skill[entangling noodles]);
		queue[count(queue)] = get_action($skill[lunging thrust-smack]);
		page = macro("", "repeat");
		break;
	case $monster[naughty sorority nurse]:
	case $monster[Candied Yam Golem]:
	case $monster[Malevolent Tofurkey]:
	case $monster[Possessed Can of Cranberry Sauce]:
	case $monster[Stuffing Golem]:
	case $monster[Inebriated Tofurkey]:
	case $monster[Hammered Yam Golem]:
	case $monster[Plastered Can of Cranberry Sauce]:
	case $monster[Soused Stuffing Golem]:
	case $monster[El Novio Cadáver]:
	case $monster[El Padre Cadáver]:
	case $monster[La Novia Cadáver]:
	case $monster[La Persona Inocente Cadáver]:
	case $monster[angry bassist]:
	case $monster[blue-haired girl]:
	case $monster[evil ex-girlfriend]:
	case $monster[peeved roommate]:
	case $monster[random scenester]:
		if(can_splash())
			page = splashSauce();
		else {
			if(have_skill($skill[entangling noodles]))
				queue[count(queue)] = get_action($skill[entangling noodles]);
			queue[count(queue)] = dangeraction();
			page = macro("", "repeat");
		}
		return;
	// In Fernswarthy's Basement
	case $monster[the ghost of fernswarthy n great-grandfather]:
	case $monster[a n stone golem]:
	case $monster[the beast with n ears]:
	case $monster[the beast with n eyes]:
	case $monster[a n-headed hydra]:
	case $monster[n bottles of beer on a golem]:
	case $monster[a n-dimensional horror]:
	}

	switch(my_location()) {
	case $location[convention hall lobby]:
		queue[count(queue)] = get_action($skill[entangling noodles]);
		queue[count(queue)] = get_action($skill[wave of sauce]);
		queue[count(queue)] = get_action($skill[candyblast]);
		queue[count(queue)] = get_action($item[bottle of Gü-Gone]);
		page = macro("", "repeat");
		break;
	default:
		if(can_splash())
			page = splashSauce();
		else page = macro(get_action("attack"), "");
	}
}

Are there ways to use BatBrain to simplify can_splash()? (Also, obviously my convention hall section needs some logic. I simply copied the current contents of my CCS.)
 
Last edited:

kain

Member
I'm not sure you're taking into account +ML. I'm currently running +62ML and fighting in the library with an ice sickle and a pilgrim shield (buffed muscle of 58) and the script is trying to futilely attack
 

Winterbay

Active member
kain: Which script is this? If it's my spamattack-script then no it isn't, at least not for muscle and moxie classes where it's basically just an "attack; repeat;"-macro. For myst it should take ML into account since that is the only one that is at least somewhat advanced.
 

kain

Member
My fault there, I was playing around with the script that Bale posted directly above my post (post 31)
 

Winterbay

Active member
Right :)
I think that if you are in the library you will end up in this part:
Code:
	default:
		if(can_splash())
			page = splashSauce();
		else page = macro(get_action("attack"), "");

Unless I'm reading that part wrong I think it'll only attack if you do not have enough +spell damage to manage a splash (or do not have the skills to splash)
 

Bale

Minion
Yeah, that little consult script isn't trying to be clever. It's just a simple script for muscle or moxie classes who aren't in over their ML. I don't have a lot of incentive to be more clever since I know that I'll be blown away by BatMan when it is complete.

Perhaps I'll eventually add some checks for ability to hit the current monster, but this script is not a complete monster killing solution and I'm not trying to make that script.
 

Bale

Minion
Continuing to refine my understanding of BatBrain I was able to improve my detection of when to splash sauce spells for awesomeness.

PHP:
boolean can_splash() {
	if(my_class() != $class[sauceror] || !have_skill($skill[Wave of Sauce]) || !have_skill($skill[Saucegeyser])
	  || !(have_effect($effect[Jabañero Saucesphere]) >0 || have_effect($effect[Jalapeño Saucesphere]) >0)
	  || dmg_dealt(get_action($skill[Wave of Sauce]).dmg) >= monster_stat("hp"))
		return false;
	switch(normalize_dmgtype("sauce")) {
	case "hot": return numeric_modifier("spell damage") + numeric_modifier("hot spell damage") >= 25;
	case "cold": return numeric_modifier("spell damage") + numeric_modifier("cold spell damage") >= 25;
	}
	return numeric_modifier("spell damage") >= 25;
}

BatBrain's normalize_dmgtype() is very nice for figuring out what sort of damage a "sauce" or "pasta" spell will do.
 
Last edited:

Winterbay

Active member
BatBrain's normalize_dmgtype() is very nice for figuring out what sort of damage a "sauce" or "pasta" spell will do.

Ahh. I was wondering how it went from pasta or sauce to anything useful. I'll have to look into that :)
 

zarqon

Well-known member
That would work for last_monster(), but not for monster stats, a much more significant part of combat. These change during combat and need to be accessed using, for instance, monster_hp() rather than monster_hp(monster). However, this is part of the reason for BatBrain to use a wrapper function for monster stats. It would be fairly easy to use versions with parameters if last_monster() != foe.

Making BatBrain speculative outside of combat is something I'm hoping to do, but first I want to make it work in combat. It already has the flexibility to speculate in combat, although this needs to be improved with regard to player stats. Once it can do that well, I think we can start to trust its predictions.

Also, Bale: haven't done a thing with splashback yet -- will probably gank some of that when I do, thanks.
 
Top