Feature - Implemented How about an "afterBattleScript"?

zarqon

Well-known member
I have come across several situations now where it would be much better if mafia adjusted goals when you acquire an item instead of only after adventuring somewhere:

1) Stasising monsters for elemental discs -- SS looks at is_goal(), so even after it gets the item it keeps stasising:

[2303] Seaside Megalopolis
Encounter: liquid metal robot
Round 0: zarqon wins initiative!
> Monster value: 0.0
> Profit per round: 0.0
> 1/8 monsters dropping goals.
> This monster drops all your remaining goals!
Round 1: zarqon uses the facsimile dictionary!
Round 2: zarqon uses the facsimile dictionary!
Round 3: zarqon uses the facsimile dictionary!
Round 4: liquid metal robot takes 67 damage.
Round 4: zarqon uses the facsimile dictionary!
Round 5: zarqon uses the facsimile dictionary!
Round 6: zarqon uses the facsimile dictionary!
Round 7: zarqon uses the facsimile dictionary!
Round 8: zarqon uses the facsimile dictionary!
Round 9: liquid metal robot takes 43 damage.
Round 9: zarqon uses the facsimile dictionary!
Round 10: zarqon uses the facsimile dictionary!
Round 11: zarqon uses the facsimile dictionary!
Round 12: zarqon uses the facsimile dictionary!
Round 13: zarqon uses the facsimile dictionary!
Round 14: zarqon attacks!
You acquire an item: essence of cold
Round 15: zarqon uses the facsimile dictionary!
Round 16: zarqon uses the facsimile dictionary!
Round 17: zarqon uses the facsimile dictionary!
Round 18: zarqon uses the facsimile dictionary!
Round 19: zarqon uses the facsimile dictionary!
Round 20: liquid metal robot takes 40 damage.
You lose 3 hit points
Round 20: zarqon uses the facsimile dictionary!
Round 21: zarqon uses the facsimile dictionary!
Round 22: zarqon uses the facsimile dictionary!
Round 23: zarqon uses the facsimile dictionary!
Round 24: liquid metal robot takes 41 damage.
You acquire an item: pool of liquid metal
You acquire an item: physiostim pill
You gain 10 muskewlairtees
You gain 12 mistikkaltees
You gain 24 mawksees

2) When a betweenBattleScript acquires goals by using container items, mafia will still adventure one more time before it realizes that goals are met.

3) Pickpocketing a goal item.

There may be more situations that arise. All of these situations could be avoided if mafia updated goals immediately whenever you acquire an item instead of during a post-adventure event.
 

jasonharper

Developer
#2 is a definite problem: I've looked into fixing it, but it didn't look very straightforward so I put it off (some of the needed details about goal state currently exist only as local variables of an entirely different function...)

However, I'm unable to duplicate #3 - if your goals are satisfied by a pickpocket, automation stops after that combat. Likewise, #1 appears to be due to some other problem in your code, as is_goal() will return false as soon as the goal is satisfied.
 

Bale

Minion
1) Stasising monsters for elemental discs -- SS looks at is_goal(), so even after it gets the item it keeps stasising:

I just took a look at SS and I think Jason is right about #1. You misremember how it works. The relevant code is this:

PHP:
   if (have_equipped($item[ruby rod]) && my_location() == $location[seaside megalopolis]) {
      foreach thingy in item_drops()
         if (contains_text(to_string(thingy),"essence of ") && available_amount(thingy) == 0) 
            return vprint("This "+nowfightingonstageone+" has a "+thingy+", making it your huckleberry.",10);
   }

you check to see if you possess a thingy, not if thingy is a goal. If there is a mafia related issue, it would be that the item is not added to inventory until the end of the combat...
 

zarqon

Well-known member
I think I'm losing my mind. Looked at the code and saw that I'd tried to save time by making is_huckleberry a variable, so it wouldn't get called every round. However, that meant that even after getting the item, the monster was still considered our huckleberry. Looks like fewyn needs to add a new prefix "Feature Request Erroneously Made About Problem In Own Script". Or, not.

My concern about #3 is that I'm not sure whether goals are considered satisfied when successfully stealing, or only at the end of combat. For instance, suppose I want to get a fumble formula when fighting Dr. Awkward, and if I don't pickpocket one I'll run away, and so forth until I get one. I pickpocket the item, and now it's the next round. Does is_goal(fumble formula) return true or false? (I realize my example is possible using either item_amount() or FTF's stolen variable, but I don't think it's unreasonable to want is_goal() to be reliable throughout combat.) I suppose further testing is in order, since I'm not sure which is the case.

So, let's pretend I only mentioned #2. Cool?
 

jasonharper

Developer
Your conditions list, as shown in the UI and tested by is_goal(), is updated the moment you acquire an item by ANY means.

The actual bug behind #2 has nothing to do with failure to update the conditions - the problem is that the conditions list is only being checked for having become empty at one specific point in the adventure sequence, which doesn't immediately catch all sources of items.
 

zarqon

Well-known member
Aha. I think fixing #2 would also resolve the similar extra-adventuring issue that occurs when you're bounty-hunting and a betweenBattleScript fights puttied monsters to get bounty items.
 

Theraze

Active member
Checking if goals are met on item use as well as adventuring end

I was trying to think about the best way to describe this, and probably failed. :) Anyways, here's the log.
Round 2: Theraze wins the fight!
You acquire an item: large box
You gain 9 Strongness
You gain 4 Magicalness
You gain 7 Chutzpah
Look! You found 1 large box (1,300μ)!

Using 1 large box...
You acquire an item: milky potion of blessing
You acquire an item: bubbly potion of inebriety
You acquire an item: smoky potion of confusion
You acquire an item: fizzy potion of ettin strength
You acquire an item: murky potion of detection
Finished using 1 large box.

Request 33 of 117 (Dungeon: Dungeons of Doom) in progress...

[2310] Dungeons of Doom
Encounter: The Return of the Dishwasher
You gain 21 Wizardliness

Conditions satisfied after 33 adventures.
My item goal was for a murky potion. Mafia is awesomely smart enough to know that small and large boxes can be used to create the murky potion, and uses them automatically. The FReq is that, if using the box (or picnic basket, or canopic jar, or whatever item type it is) creates the goal item, conditions should be satisfied then. There's no good reason for that extra adventure 33 in the log... well, besides making the coding easier. :) Anyways, this is something that would save me several adventures per run, and seems like it would make mafia more 'logical' to people who aren't thinking about things on the code side.
 

Veracity

Developer
Staff member
I was looking at Fred's Feature request to Add Grimace and Ronald Maps to item choice adventure drop-downs and thinking just how cool it would be if you could, say, set "5 synthetic dog-hair pill" as your condition, tell KoLmafia to adventure 20 times on Grimace, and end up with your desired result. I realized that even assuming we had that choice adventure set up to work with various goals, there were a few issues.

- KoLmafia tries to "create" items in order to satisfy goals. That's why you can set Talisman o'Nam as a condition and have adventuring stop once you get one. (Perhaps not the best example, since that interacts weirdly with our code to auto-create that item.) So, perhaps we could add a "concoction" which says you can create the dog-hair pill from the Grimace Map - except it also depends on having the choice set up correctly.
- A betweenBattleScript could set the choice goal appropriately and "use" the map - except, as we know, a "between" battle script is really a "before" battle script and is intended to get you ready to go adventuring. So, you could use the map and fulfill your goal - and then KoLmafia would adventure once more.

It struck me that this is a good use for an "afterBattleScript" - something that you could execute after having spent a turn somewhere. So, for my example, your script would be "set grimaceGoal=synthetic dog hair pill;use * Map to Safety Shelter Grimace Prime" or something. Similarly, if you wanted to get a "potion of detection" from the Dungeon of Doom, your afterBattleScript could be "use * small box;use * large box".

KoLmafia.executeAdventureOnce() would execute the afterBattleScript before calling KoLmafia.handleConditions() and would thus stop adventuring if the after battle script could satisfy goals.

That should work, since we call ResultProcessor.processResult() to process results from using items, and that calls GoalManager.updateProgress() to update goals.

This would also solve part of Picklish's report that adventure() from a between battle script changes combat filter. His issue was that he was adventuring in his betweenBattleScript. That's a shaky idea, for a number of reasons, but it is possible that he would have preferred using an afterBattleScript.

This also reminded me of various other goal-related bug reports/feature requests.

Zarqon wants us to Update goals when you acquire an item.

- If you acquire an item mid-battle (pickpocket, stasis), we don't update goals. That's because we don't call ResultProcessor.processResult(); we call ResultProcessor.gainItem() directly.

- "When a betweenBattleScript acquires goals by using container items, mafia will still adventure one more time before it realizes that goals are met." Didn't I say that above? Looks like he wants an afterBattleScript.

For the first case, ResultProcessor.processResult() currently calls ResultProcessor.gainItem() and then calls GoalManager.updateProgress(). We should make ResultProcessor.gainItem() call GoalManager.updateProgress() to update goals, which will solve zarqon's problem.

Something I've noticed: I set my goals to "5 agua de vida" and ask KoLmafia to adventure 20 times. Suppose it stops after 20 adventures, having found 3 aguas. The goals now say "+2 agua de vida". If I then "use" 3 aguas, the goals update to "+5 agua de vida" - which is useless. That's because using the agua goes through ResultProcessor.processResult() with a negative agua, so we subtract it from inventory and add it to the goal. We should not do that.

Bale wants to be able to Reduce conditions through CLI or add_item_condition(). In other words, we can now programmatically clear goals, add a goal or set a goal or (with hola's recent work) look at your current goals, but there's no easy way to remove a goal, other than clearing and re-adding everything except the one you want to remove. This is related to - if not a complete duplicate of - Theraze's Conditions - Set/Remove request.

So, to summarize, I propose the following:

- Create the concept of an "afterBattleScript". When automating, call this script after each adventure, before checking conditions. Perhaps also call it once BEFORE automating a sequence of adventures, since it can potentially trim the condition list and obviate adventuring completely.

- For the Relay Browser, add Yet Another Setting - relayRunsAfterBattleScript - and run it the way we used to restore HP from the Relay Browser - on a charpane refresh that indicates that an adventure has been used.

- Make ResultProcessor.gainItem() update goals - but only for item acquisition, not loss. This will fix zarqon's issues (items from stealing and stasising) and my issue (using agua increasing my goal). Make ResultProcessor.processResult() only update goals for non-items, since it calls the item-specific function which will now do that.

- Add ASH and/or CLI commands to remove or decrement a goal.

We should be able to close 3 feature requests, add spiffy new capabilities to scripts, and use afterBattlecript + goals to do hitherto difficult things.

Thoughts?
 

Veracity

Developer
Staff member
I just tested pickpocketing.

Validating adventure sequence...
Condition added: furry fur

About to adventure in Giant's Castle

Request 1 of 20 (Beanstalk: Giant's Castle) in progress...

[339444] Giant's Castle
Encounter: Furry Giant
Strategy: brianna [default]
Round 0: Brianna wins initiative!
Round 1: Brianna executes a macro!
Round 1: Brianna tries to steal an item!
You acquire an item: furry fur
Round 2: Brianna attacks!
Round 3: furry giant takes 1122 damage.
Round 3: Brianna wins the fight!
After Battle: Gort winks at you.
You gain 410 Meat
You gain 9 Strongness
You gain 7 Enchantedness
You gain 25 Chutzpah

Conditions satisfied after 1 adventures.
It satisfied the goal as soon as it acquired the item - mid-battle.

Zarqon's issue #2 - and Theraze's - are a result of the fact that a "betweenBattleScript" is really a "beforeBattleScript" - it is called when you have already decided to use an adventure, and its purpose is to get you ready to go to the desired location.

I just wrote a proposal for a "afterBattleScript" - and I will merge this request into that one.
 

Veracity

Developer
Staff member
Revision 10784 only updates goals when you gain items, not when you lose them. This fixes my "agua" bug. And zarqon never really had "issues" with items gained through stasis or pickpocketing. :p
 

Veracity

Developer
Staff member
Revision 10786 adds an "afterAdventureScript" which is executed after each automated adventure.

- I did "set afterAdventureScript=use * small box;use * large box"
- I set my goal to be "+1 potion of inebriety"
- I told KoLmafia to go to the Dungeon of Doom for 20 adventures.

After each adventure which yielded a small box or a large box, my script used the box. As soon as I got a potion of inebriety, KoLmafia stopped automating, with "Conditions satisfied after 5 adventures".

Now to hook it into the Relay Browser, preference controlled, and I will declare this done. I'm going to leave the "remove condition" stuff to the other Feature Request specifically on that topic.
 

Bale

Minion
- I did "set afterAdventureScript=use * small box;use * large box"
- I set my goal to be "+1 potion of inebriety"
- I told KoLmafia to go to the Dungeon of Doom for 20 adventures.

You are the brightly shining star in the constellation of KoLmafia.
 

Veracity

Developer
Staff member
Thanks! :)

Now, we just need somebody to write the Acme of All After Adventure Scripts, which will do something like:

while have goal( any bang potion ) && have item ( small box ) use 1 small box;
while have goal( any bang potion ) && have item ( large box ) use 1 large box;
while have goal( distention pill ) && have item ( grimace map ) set grimace choice appropriately and use 1 grimace map;

... and so on.
 

Veracity

Developer
Staff member
OK. Do we care if this script is called in the Relay Browser? I'm thinking "no", since I think its main use will be doing things to satisfy goals - and goals are only applicable for automated adventuring. You will be able to see what items you find in the relay browser and decide exactly what to do with them at the time, via the helpful "use" links, if you have those enabled.

I added the preference, but it's a no-op. I'm going to close this and if somebody comes up with a use for an after-adventure script after manual adventuring, they can open a new Feature Request for it.
 

Theraze

Active member
Well personally, I'm just going to see what happens when I turn zarqon's BBB script on for after battle as well. :D
 

Veracity

Developer
Staff member
Hopefully he will separate its functions into those appropriate for a "Best Before Battle Script" and those belonging in an "Awesome After Adventure Script".
 

Veracity

Developer
Staff member
Indeed.

Seriously, I looked at BBB and I see it do things like going semirare hunting, using dolphin whistles, and all sorts of other things that use adventures. Those are all things you might want to do in an After Adventure Script while automating, but NOT do in a Before Battle Script called while manually adventuring.

On the other hand, maybe not; the point of a BBB is to prepare you for adventuring in the (known) adventure you have just selected - either in the GUI, via clicking a link in the Relay Browser, or by calling an adventure() type of script command/function. It's part of moods and recovery and other Before Battle actions. Is it really appropriate for it to go off adventuring some place else entirely - which may lead to a fight - when you are buffed up ready to go to the original location?

And, if you go off and adventure in an After Adventure script, do we recover for you? Probably yes, since you will (possibly recursively) call adventure() or the equivalent to do so.
 

zarqon

Well-known member
Wow! This is STUPENDOUS. Thank you, shining star. Looks like I have some work to do!

And zarqon never really had "issues" with items gained through stasis or pickpocketing. :p

Quite so; the error was in my own code!

Now, we just need somebody to write the Acme of All After Adventure Scripts, which will do something like:

while have goal( any bang potion ) && have item ( small box ) use 1 small box;
while have goal( any bang potion ) && have item ( large box ) use 1 large box;
while have goal( distention pill ) && have item ( grimace map ) set grimace choice appropriately and use 1 grimace map;

... and so on.

BBB already does this for all direct container items, but not choiceadv-producing containers such as the Grimace Prime map. That's a good idea. :) Goal satisfaction is also why it fights "container monsters" such as putties, photocopies, and dolphins.

This fabulous change to automation will be able to solve most cases of wasted turns looking for goals, except for a few cases: if someone already has container items/monsters in their inventory prior to auto-adventuring, the previous behavior will still manifest and a turn will be wasted. To use a likely scenario, if I adventure to unlock the Dungeons of Doom, use the plus sign to identify my boxes and unlock the zone, and then adventure there for a bang potion, at least one turn will be spent regardless of whether I use the boxes before or after adventuring.

This is also a case for executing afterBattleScripts during manual adventuring, as it ensures that container goals can be met whether manually or automatically adventuring, thus possibly saving that unnecessary turn the next time the player adventures for goals.

Those are all things you might want to do in an After Adventure Script while automating, but NOT do in a Before Battle Script called while manually adventuring.

Hmmm. When I added using/fighting containers to BBB, my thinking was to satisfy goals as quickly as possible. If you have container items in your inventory and you're about to auto-adventure for goals, if you can satisfy those goals before adventuring, that would be desirable. On the other hand, it would feel very strange to click to manually adventure somewhere (which I'd call a commitment to adventure there) and have a beforeBattleScript take over and fight a puttied monster first (or series of them). Even if it didn't abort due to satisfied goals and still allowed me to play the turn, the wait would be confusing. If it did abort due to satisfied goals, it would be even more confusing unless a message was then delivered to the relay browser. (Feature idea perhaps? Abort messages during relay play which prevent a page from displaying could be displayed in the relay browser in place of the page.)

Is it really appropriate for it to go off adventuring some place else entirely - which may lead to a fight - when you are buffed up ready to go to the original location?

Not at all, except where goals may be satisfied by fighting a monster item. Wasting buffs is better than wasting buffs and a turn. With the exception of puttied fudge wasps (no longer particularly germane) and certain profitable dolphins (when no other goals are set), BBB only fights monster items when they contain goals. Other than that, it does not spend turns anywhere, so I'm not sure what else you might be reacting to. BBB does not semirare-hunt, it only helps you eat cookies at appropriate times.

So at the moment I'm seeing two options for the BBB split:

1) Leave goal-satisfaction in both before and after scripts. This has the positive effect of satisfying goals as quickly as possible, but with possibly confusing results during manual adventuring. Additionally, it will not avert the problem of the wasted turn when you start auto-adventuring with goal containers in inventory. However, an alert user might see the goals being met prior to adventuring and abort the script prior to wasting the turn.

2) Remove goal-satisfaction from the beforeBattle script. Despite the fact that goals may not be satisfied until after a wasted turn, it may be a better design choice to avoid confusion during manual play. Additionally, at present that turn would still be wasted regardless, so avoiding confusion and giving users faster response time during relay play may be the way to go.

I'll have to give that some thought. As always, quite open to hearing additional considerations!

Anyway, this is still many steps in the right direction and I'm thrilled! Thanks again Veracity for this vast improvement to mafia's automation abilities -- and for the idea about using choiceadv-containers! I'm excited to start splitting things up.
 
Top