From Kolmafia
Jump to navigation Jump to search

About BatBrain

BatBrain is a function library intended to greatly simplify writing a consult script. A general introduction, changelog, download instructions, and forum discussion can be found in the BatBrain thread, but this page is the best place for scripters interested in writing a consult script with BatBrain to start. NOTE: BatBrain is not built into KoLmafia, it is a separate ASH script which you must download and import to make these functions available.

What It Does

yadda yadda


BatBrain handles all damage done (both to you and to the monster) as spreads. In fact, it defines a data type called "spread":

typedef float[element] spread;

This means that all damage is a map of floats keyed by element, with positive values for damage and negative values for healing. We could have just used float[element] everywhere, but it strikes us as cleaner to use the single word "spread". You'll see this datatype occur fairly frequently if you peruse BatBrain and if you're writing a combat script, you may have to deal with one. Fortunately, there are some functions for dealing with spreads:


spread to_spread(string dmg )

spread to_spread(string dmg ,float factor )

  • dmg is the damage string, in the same format used by batfactors.
  • factor is optional; supply this if you want to factor the supplied damage by an amount other than 1.0

This function creates a spread from a string. For example, an attack which deals 10 hot damage would deal to_spread("10 hot") damage. The format for dmg is flexible and described in greater detail on the batfactors page. An optional factor parameter is available as shorthand for factor(to_spread()).


spread merge(spread first ,spread second )

  • first is the first spread to merge
  • second is the second spread

Basic arithmetic, a + b. It returns a spread combining the damage in first and second.


spread factor(spread f ,float fact )

  • f is the spread to factor
  • fact is the factor

More basic arithmetic, this time multiplication. This function returns spread f factored by fact.


string to_html(spread src )

  • src is the spread to convert to HTML

This function returns an HTML string showing a spread the way KoL does, with elemental damage parenthesized and appropriately color-coded.


float dmg_dealt(spread action )

  • action is the damage spread for a given source of monster damage

This function returns the actual damage a given spread would deal to your current opponent, accounting for the monster's resistances, vulnerabilities, and damage cap where applicable. For example, when fighting Groar, any cold damage present in action would be reduced to 1, and any damage of his vulnerable elements would be doubled. Secondly, any damage over Groar's soft damage cap would be reduced appropriately, and finally the damage would be summed together into a single float and returned.


float dmg_taken(spread pain )

  • pain is the damage spread for a given source of player damage

This function returns the actual damage a given spread would deal to the player, accounting for the player's elemental resistances and vulnerabilities.


Now that we've discussed spreads, we can really get down to it. Absolutely everything that happens, every single "adventure event" that takes place, is treated as an advevent. Your familiar performs an advevent every round, the monster performs an advevent, each action you have available is an advevent, and sometimes even your current gear or running effects contribute an advevent or two. Your current combat environment could be described with an advevent, and in fact any adjustments made to your current environment when enqueueing actions are tracked in an advevent. Get the picture? Advevents make BatBrain's world turn. And here's what they look like:

record advevent {          // record of changes due to an event
   string id;              // macro-ready; attack/jiggle/skill s/use i
   string cid;             // unique combat identifier: lastCombatStarted
   spread dmg;             // raw dmg dealt, before resists/vulns
   spread pdmg;            // raw dmg taken, before resists/vulns
   float att;              // monster attack adjustment
   float def;              // monster defense adjustment
   float stun;             // stun chance (expressed as average rounds stunned)
   float mp;               // mp gained/lost
   float meat;             // meat gained/lost
   float profit;           // profit cache to avoid recalculating
   int rounds;             // rounds consumed
   substats stats;         // substats gained/lost
   boolean endscombat;     // this action ends combat
   string custom;          // if not empty, this action should not be used in normal automation -- value is action category (banish, attract, yellow, copy, runaway, etc)
   string note;            // user-friendly name or special note

Note that each advevent contains two spreads, one for damage to the monster and one for damage to the player. The rest is mostly self-explanatory, but a few things require clarification.

First, the profit field will contain nothing (0) until to_profit() is called on the advevent in question, at which point the result is cached in this field for further reference, since to_profit() has a fair amount of calculations and is thus much more expensive (in processing terms) than simply checking a.profit. Keep that in mind if you're calling to_profit() a lot; chances are you could speed up your script by changing a few of those to check the profit cache instead.

Second, substats is another datatype defined by BatBrain, and it's simply a float[stat] map.

The custom field will be empty for most actions, so scripts ought to be checking a.custom == "" before using an action in regular automation. For special actions, the custom field will contain the type of custom action (and sometimes additional information). Some custom types currently being used are: attract, banish, copy, yellow, and runaway.

Finally, the note field is not really used by BatBrain, but is used by BatMan RE to display extra information about certain actions (such as notices of estimates due to unspaded numbers, or ongoing damage not predicted by BatBrain, etc).

Here are some functions to help you deal with advevents should the need arise:


advevent to_event(string id ,spread dmg ,spread pdmg ,string special ,int howmanyrounds )

advevent to_event(string id ,spread dmg ,spread pdmg ,string special )

advevent to_event(string id ,string special ,int howmanyrounds )

advevent to_event(string id ,string special )

  • id is the ID of the event. BALLS-friendly name (will be used in macros)
  • dmg is for damage/healing to the monster.
  • pdmg is for damage/healing to the player.
  • special is for specifying other qualities of the action, such as deleveling, stunning, meat gained/lost, etc.
  • howmanyrounds is optional, default 0. It specifies the number of rounds this action takes. Will be 0 for all but actual player actions.

Builds and returns an advevent, used ubiquitously by BatBrain internally but also useful to a scripter for creating custom actions that are not present in opts[]. You may omit both spreads if the event being constructed deals no damage to either monster or player. The format for the special field is generally a comma-delimited list of "keyword value", although some keywords lack values. The format is described in greater detail on the batfactors page -- quite a few keywords are available. Note that if you have an elemental form, all damage specified in dmg will be converted to that element by this function.


advevent merge(advevent first ,advevent second )

  • first is the first advevent to merge
  • second is the second advevent

As with the spread version, this function combines two advevents into a single advevent by individually combining each field. It isn't always just simple addition, however. First of all, the id field will be merged using a semicolon and a space, so if you were merging "jiggle" with "attack", the new combined id would be "jiggle; attack". This allows merging events to retain a BALLS-friendly ID. Second, if both first and second are single item actions and you have Funkslinging, the events will be merged into a single funkslinging action "use X,Y". The rounds field likewise will be added together unless it's being auto-funked, in which case 1 + 1 = 1. Last, the stun field is not simply added together, because if you perform two actions, each with a 50% stun chance, that does not guarantee a 100% stun chance; that's a 75% stun chance, because you have a 50% chance of stunning 50% of the time when the first action fails to stun. Merging takes this into account.


advevent factor(advevent f ,float fact )

  • f is the advevent to factor
  • fact is the factor

This function multiplies an advevent by fact, and is much more straightforward than merge(). All numeric fields are simply multiplied, with one exception: the rounds field is not multiplied since that would be undesirable. BatBrain uses this function internally to apply the hitchance of an event to the event.


float to_profit(advevent haps ,boolean nodelevel )

  • haps is the advevent to evaluate
  • nodelevel is optional, default false. If supplied as true, will ignore profit from deleveling.

Returns the profit of performing the action haps at the current point in the combat. It looks ahead one round (including the monster's event, your familiar's event, and any equipment and effects) and determines what you gained and lost, and converts all of that to a single number, which it returns. This is a very useful function for combining with other criteria and sorting opts[]. HP and MP gain/loss are evaluated based on your _meatpermp and _meatperhp properties, which are set by Bale's Universal Recovery Script. If you are not using this script, basic calculations are made using the price of common restoratives. HP/MP regeneration is also factored into the profit appropriately. By default, this function includes profit from deleveling (i.e. reducing the monster's attack by X means it will deal Y less damage, effectively earning you Y HP). However, this may be undesirable if you intend to stun beforehand, so an optional nodelevel flag was included.

Useful Global Variables

BatBrain has a not-small number of global variables which may simplify writing your script. Most of these variables are automatically populated as soon as your script runs act(). Most of these values are set in BatBrain's set_monster() function.


monster m;                 // the monster currently being fought
record monster_data {
   int variable;           // the +ML currently being run; if the same, the monster will be reloaded from cache
   boolean nopotato;       // potatos are useless here
   boolean nofamiliar;     // your familiar is useless here
   spread aura;            // passive damage dealt to you every round
   spread retal;           // damage dealt to you if you hit with a melee attack
   spread res;             // resistances to each element, expressed as -1.0 (vulnerability) to 1.0 (immunity)
   spread pen;             // the monster's penetration ($element[none] for DA penetration)
   int drpen;              // DR penetration.  The doctor is IN!
   int howmany;            // group monster size (default 1)
   int multiattack;        // number of attacks per round (default 1)
   int maxround;           // combat lasts till this round (default 30)
   int damagecap;          // damage in amounts beyond this will be reduced...
   float capexp;           // this exponent
   float autohit;          // chance of automatically hitting, regardless of moxie
   float automiss;         // chance of automatically missing
   float nostagger;        // chance of shrugging staggers.  Usually 1.0 if anything
   float nostun;           // chance of shrugging multi-round stuns
   float noitems;          // chance of ignoring combat items
   float noskills;         // chance of ignoring skills
   float delevelres;       // percent resistance to deleveling
   float spellres;         // percent resistance to spells
   float dodge;            // chance of dodging melee attacks
   string onlyhurtby;      // can only be damaged by X.  possible: aoe, club, pottery, healing
   string[string] dmgkey;  // normalized damage type(s) vs. this monster (sauce, pasta, perfect)
   string note;            // special note about this monster
monster_data mdata;        // m's monster_data
float mvalcache;           // the value of the monster, including substats and meat/item drops


spread pres         // your resistances/vulnerabilities
string page         // the text of the most recent fight.php page load
int round           // the current/simulated round
int maxround        // the maximum rounds the combat can extend
float beatenup      // the cost of getting beaten up
float meatperhp     // the value of 1 HP
float meatpermp     // the value of 1 MP


int[item] stolen          // all items you have acquired during this combat
advevent adj              // all adjustments to the current combat (used when queueing)
boolean[monster, int, string, int] happenings;  // events that have happened in this fight.  Indices: m, lastCombatStarted, action id, round => playeraction


advevent[int] opts        // all known combat options currently available to you
advevent[int] custom      // custom actions which SS has determined apply to this combat
advevent[int] queue       // queued actions (affects round number and uses adj to track adjustments)
int[string] blacklist     // blacklisted actions to be ignored
string batround_insert    // empty by default; calling scripts may insert additional macro code to batround (macro called each round between actions), formatted as "commands; "

Other Useful Functions


boolean finished()

This function simply returns true if your combat is over. Useful as a condition in your combat script's master while() loop.


float monster_stat(string which )

  • which can be one of: att, def, hp, drpen, howmany, damagecap, capexp, autohit, automiss, nostun, noskills, noitems, nostagger, delevelres, spellres, dodge, or itemres

This function wraps ASH's monster_attack(), monster_defense(), and monster_hp() functions. Please use this function instead of the ASH functions, since the value may differ if you have enqueued actions causing BatBrain to predictively alter the monster's stats. This function is also used to return any of the special monster attributes listed above (these attributes are specified in batfactors).


float my_stat(string which )

  • which can be "hp","mp", "Muscle", "Mysticality", or "Moxie"

This function wraps ASH's my_hp(), my_mp(), and my_buffedstat() functions. Anytime you're checking any of these five stats, please use this function rather than the ASH functions, for the same reason as above.


boolean happened(string occurrence )

boolean happened(skill occurrence )

boolean happened(item occurrence )

boolean happened(advevent occurrence )

  • occurrence is the advevent ID you want to check

Use this function to check if an action has already happened during this combat. For example, if you have already used a seal tooth on a previous round, or you have enqueued a seal tooth, happened("use 2") will return true. In addition to canonical action ID's (BALLS syntax), BatBrain also gives you a few other handles for special events: crit, shieldcrit, smusted, stolen, icecapstun, hipster_stats, sealwail, and lackstool.