SmartStasis -- a complex script for a simple CCS

Theraze

Active member
This is the line causing that...
Code:
   if (get_action($skill[entangling noodles]).stun > 0 && m_dpr(0,0)*2*meatperhp > mp_cost($skill[entangling noodles])*meatpermp)
      macro(get_action($skill[entangling noodles]));
Basically, if you still have entangling noodles available, and getting hit 2 rounds would cost more than the mp cost of noodles, cast it.
 

Theraze

Active member
SmartStasis is getting into an infinite poison loop, similar to above...
[2196] Black Forest
Encounter: black adder
Strategy: C:\Program Files (x86)\KoLMafia\ccs\default.ccs [default]
Round 0: Theraze wins initiative!
Factoring in Scarysauce: (6) damage, retal
Profit per round: ActionProfitDamageOtherbase; Star Starfish (0μ)35.51μ8.17 (0 μ/dmg)MP: 8.17
Monster: Black Adder, ATT: 154, DEF: 138, HP: 148, Value: 485.63
You will die in 3 rounds.
Your attack will kill the monster in 10 rounds.
Factoring in Scarysauce: (6) damage, retal
SpamAttack: Monster HP is 148.0.
SpamAttack: We are going to 2-shot with Wave of Sauce and Stream of Sauce.
Round 1: Theraze executes a macro!
Round 1: Theraze casts WAVE OF SAUCE!
Round 2: black adder takes 88 damage.
Round 2: Gronald floats behind your opponent, and begins to glow brightly. Starlight shines through your opponent, doing 22 damage, and pours into your body.
Round 2: black adder takes 22 damage.
You gain 22 Mana Points
Round 2: black adder takes 2 damage.
You lose 53 hit points
You acquire an effect: Really Quite Poisoned (duration: 5 Adventures)
You're dangerously poisoned! Will try to remove if possible.
Round 2: Theraze uses the anti-anti-antidote!
You lose an effect: Really Quite Poisoned
Round 3: black adder takes 4 damage.
You lose 52 hit points
SpamAttack: We are going to 1-shot with Stream of Sauce.
Round 3: Theraze executes a macro!
Round 3: Theraze casts STREAM OF SAUCE!
Round 4: black adder takes 29 damage.
You acquire an effect: Burning Hands (duration: 1 Adventure)
Round 4: black adder takes 5 damage.
You lose 49 hit points
You acquire an effect: Really Quite Poisoned (duration: 5 Adventures)
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
You're dangerously poisoned! Will try to remove if possible.
KoLmafia declares world peace.
You're on your own, partner.
At that point, the combat was over and I was dead.
 

Winterbay

Active member
That's batbrain though isn't it. SS never tries to remove poison, batbrain does that as part of act() when it gets called.
 

Theraze

Active member
Yeah, but BatBrain is called through SS, and SS is the one presumably that's still calling BatBrain incorrectly to cure poison on the round AFTER combat. :)

Guessing the fix will be some sort of "did I lose?" check. Maybe 0 health and beaten up should mean end of combat, just like WINWINWIN does? Not sure though if that goes for BB or SS.
 

Winterbay

Active member
Yeah, my spamattack (and possibly yours as well) has this check on the main loop:
Code:
until(contains_text(page, "WINWINWIN") || page == "" || my_hp() == 0 || initround >= maxround);
 

Theraze

Active member
Similar... mine currently has a hardcheck for 30 instead of the maxround, but I should probably transition that... :)
 

Bale

Minion
I'd like to make a feature request to make SmartStasis friendlier to other consult scripts.

My issue is that I'd like to save a server hit and some processing time. When SmartStasis ends with an entangling noodles, I'd like that to be enqueued instead of being its own server hit. To make this work, I'd like to be able to import SmartStasis to my own consult script. The guts of main() should be moved to SmartStasis(int initround, monster foe, string page, boolean enq), main() should consist of only

Code:
void main(int initround, monster foe, string page) {
   page = act(page);
   SmartStasis(initround, foe, page, false);
}

In SmartStasis(), at the end, noodles could be enqueued if enq == true or macroed if false.

zarqon, do you smile on this suggestion, have a better way to implement a solution, or dislike it altogether? This would not only save a server hit, it will also save some processing time since BatBrain will only has to generate all options once, instead of having to generate options a second time when imported into my consult script. You might also find this helpful some day in the distant future when you theoretically write BatMan.
 
Last edited:

Winterbay

Active member
It would also make my current modifications to SS unnecessary (I have a property that gets set in order to make sure that the consult script after SS knows that noodles have been cast and for how long it is probable to last, i.e it saves adj.stun).
 

Bale

Minion
Heh. It seems that Winterbay and I are trying to solve similar problems independently. I hadn't even considered saving adj.stun.
 

Bale

Minion
Okay. I see why saving adj.stun is a huge deal. I'm having problems with my finalAttack script thinking I'm going to die in two rounds since it doesn't know that SmartStasis already cast entangling noodles...

Perhaps it should be added to BatMan_happenings?
 

zarqon

Well-known member
@Bale: Order of operations is the reason I haven't already done this. SS's main() locks things in to a certain order, whereas a consult script designed to handle the entire combat may want to put some of SS's things after its own. Note that I add some of the vs-Cyrpt-miniboss items to custom[] in main() -- this is because they are also regular combat items and your script may want to use them differently, or skip them if you have better attack options. Additionally, you may not want to use all of your custom actions up front -- maybe you want to smack the monster down a bit first (i.e. when doing pirate insults).

Believe me, as the reason for BatBrain's existence, BatMan has been part of the design process since the beginning.

I'm curious why you didn't just import SS from the beginning. You just have to duplicate some/all of SS's main() -- in your preferred order -- in your own main(). In which case you can also make noodles enqueued rather than immediately macro'd.

Adding noodles to happenings is probably a good idea.
 

Bale

Minion
I'm curious why you didn't just import SS from the beginning. You just have to duplicate some/all of SS's main() -- in your preferred order -- in your own main(). In which case you can also make noodles enqueued rather than immediately macro'd.

Because I was hoping there would be native support and I didn't realize I had a real problem until just now. I've only just gotten serious about using the awesome power of predictive monster stats and sorting opts. First I had to debug the monster_stats("hp") bug which stopped me dead in my tracks. That was a pretty serious bug for anyone who wants to unlock the real power of BatBrain. For a while I thought I was just using BatBrain wrong until I tracked down the bug.

This is cool!

PHP:
boolean enqueueSafe() {
	skill classkill() {
		switch(my_class()) {
		case $class[Seal Clubber]: return $skill[Clobber];
		case $class[Turtle Tamer]: return $skill[Toss];
		case $class[Pastamancer]: return $skill[Spaghetti Spear];
		case $class[Sauceror]: return $skill[Salsaball];
		case $class[Disco Bandit]: return $skill[Suckerpunch];
		case $class[Accordion Thief]: return $skill[Sing];
		}
		return $skill[none];
	}
	advevent choice = get_action(classkill());
	if(kill_rounds(choice) > kill_rounds(get_action("attack")))
		choice = get_action("attack");
	if((kill_rounds(choice) < die_rounds() && kill_rounds(choice) < 10))
		return enqueue(choice, "");
	return false;
}

boolean enqueueDanger() {
	int q = count(queue);
	int shield = get_action($skill[shieldbutt]).id == ""? 99: kill_rounds(get_action($skill[shieldbutt]));
	if((shield < 3 || shield * 3 < die_rounds()) && shield + round < maxround - 1)
		enqueue(get_action($skill[shieldbutt]), to_string(shield));
	else while(monster_stat("hp") > 0) {
		sort opts by -to_profit(value);
		sort opts by -dmg_dealt(value.dmg);
		if(dmg_dealt(opts[0].dmg) < 1 || kill_rounds(opts[0]) >= die_rounds()) break;
		enqueue(opts[0]);
	}
	
	if(count(queue) == q)
		return false;
	enqueueSafe();  // Just in case the monster is at 3 HP.
	return true;
}

string failure() {
	return macro("abort \"Unable to figure out combat strategy\"", "");
}

if(!enqueueSafe() && !enqueueDanger())
	return failure();
return macro();

My next step is to construct a simple plinking strategy for moxie classes to use on monsters that are "reasonably" close to rendering harmless.
 
Last edited:

jwylot

Member
Stasising with a llama?

I'm doing a 100% llama run with an equipped moveable feast. CCS is set to make sure I finish on a critical hit for the v-mask stat bonus:
PHP:
[ default ]
consult SmartStasis.ash
skill shieldbutt
skill throw shield
skill shieldbutt
But what is happening is this:
PHP:
[194] Defiled Nook
Encounter: toothy sklelton
Round 0: panama joe wins initiative!
Round 1: panama joe executes a macro!
Round 1: panama joe casts SPRING RAINDROP ATTACK!
You gain 21 hit points
You gain 22 Muscularity Points
You lose 1 hit point
Round 2: panama joe executes a macro!
Round 2: panama joe casts THROW SHIELD!
Round 3: panama joe executes a macro!
Round 3: panama joe casts ENTANGLING NOODLES!
Round 4: panama joe executes a macro!
Round 4: panama joe uses the seal tooth!
Round 5: toothy sklelton takes 1 damage.
Round 5: panama joe uses the seal tooth!
Round 6: toothy sklelton takes 1 damage.
Round 6: panama joe uses the seal tooth!
Round 7: toothy sklelton takes 1 damage.
Round 7: panama joe uses the seal tooth!
Round 8: toothy sklelton takes 1 damage.
You lose 1 hit point
Round 8: panama joe executes a macro!
Round 8: panama joe uses the seal tooth!
Round 9: toothy sklelton takes 1 damage.
Round 9: Flogger tosses a gob of foul-smelling oyster stuffing at it, dealing 60 damage.
Round 9: toothy sklelton takes 60 damage.
You lose 1 hit point
Round 9: panama joe executes a macro!
Round 9: panama joe attacks!
Round 10: toothy sklelton takes 110 damage.
Round 10: Flogger spits on your opponent. Yech.
Round 10: toothy sklelton drops 7 defense.
Round 10: Flogger tosses a gob of foul-smelling oyster stuffing at it, dealing 52 damage.
Round 10: toothy sklelton takes 52 damage.
Round 10: panama joe wins the fight!
After Battle: Your Evilometer emits a single beep. The air in this part of the crypt smells slightly less evil...
You acquire an item: skeleton bone
After Battle: A smile crosses Flogger's serene countenance.
You gain 17 Beefiness
You gain 10 Enchantedness
You gain 8 Sarcasm
Seems it ignores my first Shieldbutt, throws the shield then waits for the llama to kill the monster. Is this a feature I am seeing for the first time?
 

roippi

Developer
That's not what is happening. In your above, smartstasis is running until round 9. Smartstasis already used throw shield in order to get crit noodles, so it was not available for the rest of your CCS to use.
 

Winterbay

Active member
That's not what is happening. In your above, smartstasis is running until round 9. Smartstasis already used throw shield in order to get crit noodles, so it was not available for the rest of your CCS to use.

Yep, which would've been more apparent with a verbosity of 9 so that the macro gets printed (and SS tells you when it exits).
 

zarqon

Well-known member
Smartstasis already used throw shield in order to get crit noodles

The current BB doesn't understand the implications of the shield's auto-crit at all. Critical success for skills is not even part of batfactors. Locally, I have Throw Shield tracking working, and it applies to your regular attack, but all other skills are still going to assume average damage/healing/stun.

SS probably just threw the shield first because on average that action has a profit of 0 -- and if you're fighting at a level appropriate for a reasonably speedy run, most of your other actions will probably have a profit of less than 0.

Support for Throw Shield in SS is something I'm currently mulling over. It seems simple -- if you have the shield, you should definitely take advantage of the auto-crit when it's free -- but it's actually more complicated. Do you want to extend noodles or get more attack? What about gear synergy? For example, someone may be farming using the OPS/hookspear duo (extract meat on critical hits), or getting more stats from a V mask. They would not want their shield "wasted" on extending noodles. It's still calculable, but it turns out to be a fairly complicated equation.
 

jwylot

Member
Thanks for the explanation zargon. I'm not sure that v-crisis should be shoe-horned into Batbrain but maybe it would be possible to have BB ignore the OPS altogether? That way, the user can set up a CCS which includes Smartstasis but tailor what happens to fit his needs.

I know nothing about the OPS/hookspear farming duo except that it is only really feasible out of Ronin. In Ronin, I would guess that anyone equipped with the OPS and a V-mask is looking to at least finish combat on a crit, if not actually using v-crisis but I understand it would be difficult to cater for everyone.
 

lostcalpolydude

Developer
Staff member
Usually when I use vcrisis it's to force an item to drop, most commonly a filthworm gland. Otherwise I'm trying to generate turns. End-of-fight stats are nice, but someone that wants stats should be wearing a juju mask instead (I don't think there's a run type where pulling both isn't ideal if you have both, and anyone that has access to a vivala mask should have access to the cheaper juju mask).

On the other hand, when I'm using vcrisis for filthworm glands, I have the GAP +crit buff going anyway, and that puts the critical hit rate at around 50%, so using Throw Shield for an attack there doesn't make much sense.
 

jwylot

Member
I really couldn't justify pulling two shields on my runs but each to his own. Even though I disagree, this isn't the place for such discussion :)
As zargon already said, we all use the shield/mask differently so some way of telling SStasis to keep it's hands off the OPS would be ideal. SStasis is insanely useful for doing the stuff it does well and I think it would be wrong to have it dictating the strategy we should adopt when it comes to using crits.
 
Top