BatBrain -- a central nervous system for consult scripts

Running BatBrain 1.2 in the latest KoLmafia (r9390), I get the following error every battle:

Code:
class java.lang.ClassCastException: net.sourceforge.kolmafia.textui.parsetree.Value cannot be cast to net.sourceforge.kolmafia.textui.parsetree.CompositeValue
java.lang.ClassCastException: net.sourceforge.kolmafia.textui.parsetree.Value cannot be cast to net.sourceforge.kolmafia.textui.parsetree.CompositeValue
	at net.sourceforge.kolmafia.textui.parsetree.CompositeReference.getSlice(CompositeReference.java:107)
	at net.sourceforge.kolmafia.textui.parsetree.CompositeReference.getValue(CompositeReference.java:178)
	at net.sourceforge.kolmafia.textui.parsetree.CompositeReference.execute(CompositeReference.java:91)
	at net.sourceforge.kolmafia.textui.parsetree.Operator.applyTo(Operator.java:391)
	at net.sourceforge.kolmafia.textui.parsetree.Expression.execute(Expression.java:221)
	at net.sourceforge.kolmafia.textui.parsetree.Expression.execute(Expression.java:150)
	at net.sourceforge.kolmafia.textui.parsetree.Operator.applyTo(Operator.java:591)
	at net.sourceforge.kolmafia.textui.parsetree.Expression.execute(Expression.java:221)
	at net.sourceforge.kolmafia.textui.parsetree.Assignment.execute(Assignment.java:101)
	at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:451)
	at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:129)
	at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:166)
	at net.sourceforge.kolmafia.textui.parsetree.Assignment.execute(Assignment.java:101)
	at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:451)
	at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:129)
	at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:166)
	at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:451)
	at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:129)
	at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:166)
	at net.sourceforge.kolmafia.textui.parsetree.FunctionReturn.execute(FunctionReturn.java:98)
	at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:451)
	at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:129)
	at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:166)
	at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:451)
	at net.sourceforge.kolmafia.textui.parsetree.Else.execute(Else.java:63)
	at net.sourceforge.kolmafia.textui.parsetree.If.execute(If.java:81)
	at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:451)
	at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:129)
	at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:166)
	at net.sourceforge.kolmafia.textui.parsetree.Assignment.execute(Assignment.java:101)
	at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:451)
	at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:129)
	at net.sourceforge.kolmafia.textui.Interpreter.executeScope(Interpreter.java:282)
	at net.sourceforge.kolmafia.textui.Interpreter.execute(Interpreter.java:213)
	at net.sourceforge.kolmafia.textui.Interpreter.execute(Interpreter.java:206)
	at net.sourceforge.kolmafia.request.FightRequest.nextRound(FightRequest.java:613)
	at net.sourceforge.kolmafia.request.FightRequest.runOnce(FightRequest.java:1257)
	at net.sourceforge.kolmafia.request.FightRequest.run(FightRequest.java:1281)
	at net.sourceforge.kolmafia.request.GenericRequest.handleServerRedirect(GenericRequest.java:1682)
	at net.sourceforge.kolmafia.request.GenericRequest.retrieveServerReply(GenericRequest.java:1519)
	at net.sourceforge.kolmafia.request.GenericRequest.execute(GenericRequest.java:1231)
	at net.sourceforge.kolmafia.request.GenericRequest.run(GenericRequest.java:1119)
	at net.sourceforge.kolmafia.request.AdventureRequest.run(AdventureRequest.java:220)
	at net.sourceforge.kolmafia.RequestThread.postRequest(RequestThread.java:73)
	at net.sourceforge.kolmafia.KoLAdventure.run(KoLAdventure.java:987)
	at net.sourceforge.kolmafia.RequestThread.postRequest(RequestThread.java:103)
	at net.sourceforge.kolmafia.KoLmafia.executeAdventureOnce(KoLmafia.java:1443)
	at net.sourceforge.kolmafia.KoLmafia.executeRequestOnce(KoLmafia.java:1461)
	at net.sourceforge.kolmafia.KoLmafia.executeRequest(KoLmafia.java:1351)
	at net.sourceforge.kolmafia.KoLmafia.makeRequest(KoLmafia.java:1233)
	at net.sourceforge.kolmafia.textui.command.AdventureCommand.run(AdventureCommand.java:103)
	at net.sourceforge.kolmafia.KoLmafiaCLI.executeCommand(KoLmafiaCLI.java:538)
	at net.sourceforge.kolmafia.KoLmafiaCLI.executeLine(KoLmafiaCLI.java:412)
	at net.sourceforge.kolmafia.swingui.CommandDisplayFrame$CommandQueueHandler.handleQueue(CommandDisplayFrame.java:202)
	at net.sourceforge.kolmafia.swingui.CommandDisplayFrame$CommandQueueHandler.run(CommandDisplayFrame.java:183)

It appears the problem is in line 873 of BatBrain:
Code:
advevent a = merge(adjustment,a);

Since a is only just being defined, the second parameter to merge appears to be passed as 0 in ASH, and the KoLmafia Java exception happens in BatBrain's merge function at line 109, when it tries to defererence sec.id.

I'm not entirely sure why the correct merge function (the one that takes advevent arguments) gets called at all, and whether the not-yet-defined "a" gets somehow semi-cast to an advevent in order to satisfy the function definition, but then this gets forgotten when KoLmafia tries to get the "id" field; however, a short-term workaround in BatBrain appears to be to change line 873 to:

Code:
advevent a; a = merge(adjustment,a);

The same construct appears to be necessary at line 583.
 
I saw the same error and changing
Code:
advevent a = merge(adjustment,a);
to
Code:
advevent a;
a = merge(adjustment,a);
in the two places noted above worked for me. (Thanks zeroToNine).

I would be very interested in Veracity's comments since it could be related to how the script is parsed.
 
I'm used to languages where passing by reference must be specified explicitly ...
You don't use Java, eh?

I saw the same error and changing
Code:
advevent a = merge(adjustment,a);
to
Code:
advevent a;
a = merge(adjustment,a);
in the two places noted above worked for me. (Thanks zeroToNine).

I would be very interested in Veracity's comments since it could be related to how the script is parsed.
Interesting. Apparently we add "a" to the symbol table for the scope before interpreting the initializer. I don't recall ever seeing code which used the (uninitialized) variable in its own initializer before.

I'll have to think about this.

Edit: I suspect that

Code:
advevent a = merge( adjustment, new advevent );
would work. I have no idea what "merge" is. Something which replaces the "adjustment" field of an advevent? Why not just initialize the advevent with the appropriate adjustment?

Code:
 advevent a = new advevent(,,adjustment);
... as appropriate, depending on which field adjustment goes into.
 
Last edited:
Code:
advevent a = merge( adjustment, new advevent );
would work. I have no idea what "merge" is. Something which replaces the "adjustment" field of an advevent? Why not just initialize the advevent with the appropriate adjustment?

Merge adds two advevents to each other so instead of doing
Code:
advevent a, b, c;
c.id = a.id + b.id;
c.next = a.next + b.next
you do
Code:
advevent a, b, c;
c = merge(a,b);
.
 
So, he's trying to use merge as a way to copy an advevent. If id and next are the two fields of an advevent, either of the following would work:
Code:
advevent a = merge( adjustment, new advevent);
advevent a = new advevent( adjustment.id, adjustment.next );
The first will do an extra memory allocation to make an empty advevent.

Revision 9391 fixes the ASH parser to disallow using an uninitialized variable in its own initializer expression.
 
While we're on the trail of interesting bugs. I am currently getting a -2.27% chance to hit monsters since the return value of hitchance() looks like this:
Code:
return minmax((6.0 + attack - max(monster_stat("def"),0.0))/10.5,0.0,1.0) - fumble_chance();  // change minimum to critchance
I think it would be better to do
Code:
return minmax((6.0 + attack - max(monster_stat("def"),0.0))/10.5 - fumble_chance(),0.0,1.0);  // change minimum to critchance

Or am I wrong?
 
shouldn't that have *(1-fumble_chance()) instead?

In fact, I think it should be
Code:
float straight_atk_chance = minmax(6.0 + attack - max(monster_stat("def"),0.0))/10.5, 0.0, 1,0);
return crit_chance + (1-crit_chance-fumble_chance())*straight_atk_chance;

(once you have crit_chance figured out).
 
Last edited:
I saw the same error and changing
Code:
advevent a = merge(adjustment,a);
to
Code:
advevent a;
a = merge(adjustment,a);
in the two places noted above worked for me. (Thanks zeroToNine).

I would be very interested in Veracity's comments since it could be related to how the script is parsed.

I only found one instance of
Code:
advevent a = merge(adjustment,a);
in batbrain.
 
I only found one instance of
Code:
advevent a = merge(adjustment,a);
in batbrain.

I had three instances of
Code:
advevent <something> = function(something, something other)

I did a quick search through the code for advevent and changed the ones I found so that they defined the variable first.
 
Ha, originally did have it as merge(eventtobecopied, new advevent), then opted to use the empty variable instead just because it's shorter and I thought it might be a smidge faster (not allocating another variable).

Code:
 advevent a = new advevent(,,adjustment);
... as appropriate, depending on which field adjustment goes into.

Now that is a useful bit of information. Didn't have any idea until that post how to declare a new record and populate its fields in one fell swoop. Thanks.

Using merge(a, new record) is definitely not as optimal as new record(a.field1, a.field2, a.field3), since it needs two extra records (there's another one in the function to receive the summed fields of both records). The whole copying records thing is a little bit hackish anyway.

However, two fields in advevents are maps -- float[element] and int[stat], respectively. This would make the construct statement rather messy, since I'm pretty sure new advevent(a.id, a.mapvariable) would still have the same problem of copying pointers rather than maps, no? I assume there is an equivalent constructor statement for maps? If so, it's still possible but it would be a very long command. Using merge(), though hackish, is easier on the eyes. :)

I'll get this fixed and posted within a few minutes.

You don't use Java, eh?

Nope. A few CS classes I took in uni used Java, but I didn't delve much into it beyond the class excercises. My programming experience, which is nearly all as a hobby, is mainly in PHP and Delphi (object-oriented Pascal). I have written a few web apps some years back that some organizations are still using, however.
 
I have a bug report. I honestly don't know which script is responsible but after today's earlier adventures with SS getting blamed for BB's error I'm putting it here. :confused:
This is with the latest SS, BB posted this afternoon and using Bale's FinalAttack right after SS in my CCS. (I need Robin Williams to read that line)

This one only happens on certain monsters. Everything seems to go well, there's just a "You're on your own partner" break in the middle.
Monsters that trigger this bug:
Modern Zombie
dirty old lihc
beebee queue
bee swarm

EDIT: Possibly relevant bit: I had a haiku katana equipped for all combats. Each time this error occurred, Spring Raindrop was not cast. As the log shows, the first action was noodles. On other monsters, Spring was always cast before noodling or anything else.

Verbosity 10 logs:
Start of combat:
Code:
[320] Defiled Alcove
Encounter: modern zmobie
Strategy: C:\Users\Eszetela\Documents\My Dropbox\KoLmafia\.kolmafia\ccs\Final.ccs [default]
Round 0: edfox loses initiative!
You lose 24 hit points
1 MP costs 17.0μ.
1 HP costs 5.1μ.
Value of stat gain: 15μ
Value of stat gain: 15μ
Monster value: 15
You will die in 3067 rounds.
Your attack will kill the monster in 1 rounds.
Profit per round: ActionProfitDamageOtherbase; Pet Cheezling (0μ)0μ--
Building options...
Options built! (14 actions)
Building custom actions...
1/4 monsters drop goals here.
Custom actions built! (0 actions)
Stasis action chosen: skill 3004
This monster is not your huckleberry.
Stasis loop complete.
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 185;  abort "BatBrain abort: Danger, Will Robinson"; endif; endsub; skill  3004; call batround; 
Round 1: edfox executes a macro!
Round 1: edfox casts ENTANGLING NOODLES!
Building options...
Options built! (13 actions)
SmartStasis complete.
You're on your own, partner.
Click here to continue in the relay browser.
Continuing the same combat via relay with the 'script' button:
Code:
1 MP costs 17.0μ.
1 HP costs 5.1μ.
Value of stat gain: 15μ
Building options...
Options built! (13 actions)
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 2;  abort "BatBrain abort: Danger, Will Robinson"; endif; endsub; sub  batsub1; skill 2005; call batround; endsub; call batsub1; repeat ; sub  batsub2; attack; call batround; endsub; call batsub2; repeat ; 
Round 2: edfox executes a macro!
Round 2: edfox casts SHIELDBUTT!
Round 3: modern zmobie takes 233 damage.
Round 3: modern zmobie takes 30 damage.
Round 3: modern zmobie drops 4 attack power.
Round 3: edfox wins the fight!
After Battle: Your Evilometer emits five quick beeps.
After Battle: Cheez Wizz covers one of its jalapeño "eyes" with a  pseudopod, in a sort of rough facsimile of a wink, and burbles  cheerfully at you
You gain 117 Meat
You gain 12 Strongness
You gain a Muscle point!
You gain 5 Mysteriousness
You gain 3 Cheek
Building options...
Options built! (5 actions)
More info. Continuing the attack on a bee swarm failed with a different error that Bale has been referring to and the Danger Will Robinson bit appeared in the relay browser. Although now I can't find the post he made about negative hit chances... so perhaps I'm referring to something in my head. ANYWAY! Perhaps this is the causes off all these stops.
Code:
spellKill: Monster HP is 351.0
You have -4.5454545% chance to hit the monster
Macro called on empty queue!
spellKill: Monster HP is 351.0
You have -4.5454545% chance to hit the monster
Macro called on empty queue!
You're on your own, partner.
 
Last edited:
Which spells do you have available for use? It may be that you no skills that can kill the monster in 5 turns, or you could have too little MP left, another thing that can generate that sort of error since afaia spellKill, just as my version of it, doesn't include restorers in the macro.
 
I'm running as a SC and don't have many offensive spells permed. Eliminating buffs and other noncombat things from the available skills pane:

Clobber (0 mp)
Shieldbutt (5 mp)
Spectral Snapper (20 mp)
Entangling Noodles (3 mp)
Lasagna Bandages (6 mp)

I'm pretty sure it's not an MP issue. I'm using Universal Recovery set at recover at 20% up to 50% and with a pool of 62 that should never not be enough to shieldbutt a few times.

EDIT:

I may have just figured it out, or at least found something relevant. I was using a haiku katana the whole time while this error occurred. On bugged combats where I got the stop SS did not cast Spring Raindrop. On every other combat it casts Spring as the very first action, even before noodles (I would rather noodle first spring second but that's a quibble). I went back through my logs and this appears to be 100%. All combats with the monsters that cause the stopper had no Spring Raindrop.
With Universal Recovery watching MP every round I should still have plenty even without Spring but it's an interesting coincidence.
 
Last edited:
The only time Bale's FinalAttack calls spellKill is if shieldbutt is for some reason unavailable. Looking at your permed spells none of those are taken into account by spellKill so it is no surprise that script exits with an empty macro queue at least.

I've no idea why using Spring Raindrop or not could have to do with Shieldbutt not being available as an option...
 
This probably has to do with un-stunnable monsters, since BatBrain as yet doesn't know which monsters are immune to multi-round stuns. In all of these cases, was Noodles the last action before SS was finished? I suspect it was, which meant when SS finished mafia saw a KoL error message and aborted.

That's something easily added if I could just find a list of these monsters, and it might fix the issue. So far I haven't been able to track down such a list, however. The Wiki apparently doesn't care about this; the pages on stunning, bosses, and special monsters have nothing about immunity to stun.
 
All Hobo Bosses are also definitely immune.

This is just a preliminary list, but we're starting to get somewhere. :)
 
Here is an odd one I noticed with my Dandy Lion as a fam and having a having a whip equipped. I have a juju mask equipped.

My .ccs looks like this:
[ default ]
consult SmartStasis.ash
skill wrath of the trickster god
skill wrath of the lightning god
skill wrath of the volcano god
skill cannelloni cannon


When the whip activates the Dandy Lion, the combat throws me out to a manual intervention.

[118] Dark Heart of the Woods
Encounter: Fallen Archfiend

Strategy: D:\Games\KoL\Current Builds\Joe_Mama\ccs\Juju.ccs [default]
Round 0: Joe_Mama loses initiative!
You lose 6 hit points
Round 1: fallen archfiend takes 4 damage.
You lose 21 hit points
You will die in 2 rounds.
Your attack will kill the monster in 4 rounds.
Round 1: Joe_Mama executes a macro!
Round 1: Joe_Mama casts ENTANGLING NOODLES!
Round 2: Spuds MacKenzie uses his vines to give you an invigorating head massage. It's not the same as giving you a foot massage, but it's in the same ballpark.
You gain 6 hit points
You gain 3 Mana Points
Round 2: Like the majority of whipholders, you crack your whip. Cleo Leo is startled by the noise and goes feral, clawing your opponent for 19 damage. All fear the kitten with a whip!
Round 2: fallen archfiend takes 19 damage.
You're on your own, partner.
Click here to continue in the relay browser.


Am I invoking the script incorrectly or maybe my .ccs is a little brain-damaged or...???

The basic logic flow is:
Cast noodles if needed
Cast one of the 3 juju skills if active
Cast Canelloni Cannon if the monster hasn't been vaporized

Thanks!
 
Last edited:
I would suggest you try that again at a higher verbosity so that the submitted macro can be seen and analysed for odditites. I've also been experiencing odd breaks after casting of noodles, but I think that is mainly due to unstunnable monsters, but I'm not sure...
 
Back
Top