Feature ASH location_accessible function.

Veracity

Developer
Staff member
Added in r26623. I'm not married to the name, and I definitely want to augment it, but lets talk about what it does, what it could do, and what it should do.

KoLmafia will "automate" adventures for you - via the GUI, via the "adventure" command, via the adventure() and adv1() ASH functions. Valid locations are in adventures.txt and are internally represented by the KoLAdventure object. Many are adventure.php - and the adventure ID is the "snarfblat". But, there is also mining, various bosses, Sorceress Tower crowds, and so on. Adventures are grouped into zones from zonelist.txt

Before it attempts to go to a location, it does a lot of validation.

1) validate1 - is the location available to you at all?

You may have to be a particular level, have a suitable zodiac sign, be at a particular place in a quest, have unlocked access to a zone, and so on. There may be other conditions that need to prevail, but we don't check them here.

If this returns false, there is nothing you can do to go to this zone right now.

1.5) Checks for combat strategy, antidotes if poisonous monsters, and so on.

2) Do recovery and call betweenBattleScript, if any.

If you need to wear an outfit, have a particular familiar, have a particular status effect or need to do anything else to actually make the zone accessible that a script can do without adventuring, do it here. That could include buying or pulling an item.

3) validate2 - are ALL the conditions needed to go to the location active now?

i.e. has the between_battle script done everything it needed to do, if the area had any special requirements.

----------------------

I added location_available(loc) which returns true or false based on step 1 - validate1.

Note that validate1 will return true for a Charter zone or Wormwood, say, because your betweenBattleScript COULD use a day pass or a bottle of absinthe - even buying or pulling.

I am thinking of adding a couple of steps.

1a) If you are under Standard restrictions, all sorts of zones are off limits. You will not have access to a Machine Elf or Snowglobe to go to the Deep Machine tunnels. Your elemental charters are not active and day passes are unavailable.

1b) If you are in Hardcore not in Standard, you (might, depending on path) have your familiars. You might have items dropped by familiars or equipment. So, out-of-standard areas MIGHT be accessible to you.

Otherwise - not in Standard, not in Hardcore - you might have familiars and items and you might be able to buy and/or pull an access item, so everything is fine.

1a and 1b will go into validate1 and hence into location_accessible().

Thoughts? Suggestions? Bugs?
 
Thank you Veracity! This is great, exposing this to scripters will both help them and help us when they point out areas that it is missing.

I think that the most use for scripts is going to be from validate2 (and validate1? I haven't read to see if you need to run both. Also maybe we should rename those to something else!). In the context of running alongside a betweenBattleScript that's great: validate1 sense checks then validate2 confirms.

Being used during scripting is a little different - it's less helpful to know that you *could* access an area but can't right now, because the script is then going to have to how to check every location regardless of the location_accessible output. For example: you get a Guzzlr quest to adventure in a certain zone. It would be great to run:

Can we adventure here? If yes, great. If not, do we know how to unlock this area? If yes, great. If not, let's skip it.

With this approach we're instead going to run:

Can we adventure here? If not (jump above). If yes, ok but can we actually adventure here? If yes, great. If not (and jump to the above)

Which is a little more complicated in my opinion - we're essentially running validate2! The key thing here is: even if you do want to know an area where you could theoretically adventure, you'd still need to write this extra code to then do that thing.

Also: I wrote a quick script to check this against my lasting local version of canAdv

Code:
ash import "canAdv.ash"; foreach l in $locations[] if (can_adv(l) != location_accessible(l)) { print(`mafia thinks {l} is {location_accessible(l) ? "" : "in"}accessible but canadv disagrees`); }

Which I figure is likely to find a lot of gaps since we default to true. It finds what looks like 40-50, many of which could be dealt with by excluding zones from past world events.
 
Also: I wrote a quick script to check this against my lasting local version of canAdv

Code:
ash import "canAdv.ash"; foreach l in $locations[] if (can_adv(l) != location_accessible(l)) { print(`mafia thinks {l} is {location_accessible(l) ? "" : "in"}accessible but canadv disagrees`); }

Which I figure is likely to find a lot of gaps since we default to true. It finds what looks like 40-50, many of which could be dealt with by excluding zones from past world events.

I suspect it will return a lot more false positives on accounts that have empty accessible inventory.
And even validate2 will then disagree with canadv, because canadv asks "do we have the items that need to be used/equipped to go there (and optionally actually use+equip then)", but (if I understand it correctly) validate2 asks "have all the necessary items already been used/equipped".
 
I misrepresented validate2. It has a lot of code to put on outfits and such. It counts on the betweenBattleScript to buy stuff if necessary.

As I said, validate1 and validate 2 are used internally by KoLmafia when automating adventuring. Between them is your betweenBattleScript, which can acquire items, and so on, and your recoveryScript which will heal you up. Your betweenBattleScript CAN put on outfits and use items, but a lot of that is in validate2, if it chooses not to do so.

As I mentioned, your betweenBattleScript can pull or buy items, if you don't have enough.

Suppose we had this:

boolean location_accessible(boolean check_only);

If check_only is true, it will check that you can get to the area right now, possibly using items and putting on outfits that you have in inventory or an "available" source given item acquisition settings: inventory, closet, storage (if out of Ronin); it will not consider spending Meat. If this says "true", you can get there, but you are responsible for doing the prep - possibly via your betweenBattleScript

If check_only is false and you have the "stuff" needed, it will then actually suit you up: use sonars, put on haram girl outfit and use Knob Goblin perfume, use an already available day pass, and so on. This is the stuff that is in validate2. If you then automate after doing this, validate1 and validate2 will be no-ops. Your betweenBattleScript (and recoveryScript) will still fire and do whatever else it wants to do.
 
By the way - this came up because the "journey" command needs validate1 in order to report on whether you actually get to a particular zone to collect skills or not. validate1 was missing logic for six of those ones, so I fixed it this morning.

That is a use case for NOT automatically suiting up to go somewhere.

Interestingly, I just noticed that we have no checks for Barrrney's Barrr (or the other Pirate areas) which have equipment requirements. Perhaps we should put those in a "Pirate" zone, rather than simply Island, which is available only if you have access to the island, have Swashbuckling Getup or pirate fledges and the Island War is not underway.
 
If check_only is false and you have the "stuff" needed, it will then actually suit you up: use sonars, put on haram girl outfit and use Knob Goblin perfume, use an already available day pass, and so on. This is the stuff that is in validate2. If you then automate after doing this, validate1 and validate2 will be no-ops. Your betweenBattleScript (and recoveryScript) will still fire and do whatever else it wants to do.
The problem here is that scripts wouldn't get an opportunity to evaluate whether they want to "spend" the item before doing so. In particular, day passes can vary wildly in cost. Maybe we want to change validate2 to do a *right now* check, and spin out a new function that suits up and/or uses items. Or maybe we make a distinction between suiting up (essentially free) and using an item (potentially expensive)
 
Yes. We do not currently use day passes.

validate1 - determines if it is at all possible to go somewhere:
Code:
  // Validation part 1:
  //
 // Determine if you are locked out of reaching a zone or location by level,
  // quest progress quest items, or other things that a betweenBattleScript
  // can't solve for you, like using a daypass, donning an outfit, or getting
  // an effect. After we've given that script a chance to run, part 2 of
  // validation might be able to do some of those things anyway.

betweenBattleScript - user decisions

validate2 - decides if it is REALLY possible to go somewhere, and suits up - and uses some items.
Code:
  // Validation part 2:
  //
  // The zone/location is within reach. If the pre-adventure script
  // donned outfits, bought supplies, or gained effects, cool.
  //
  // If it didn't do what we can here.

"some" items, currently being sonars, Knob Goblin perfume, astral mushroom, dinghy plans.

Yeah. I am potentially proposing two functions: one for validate1 and one for validate2.
 
Last edited:
I wish @zarqon could be enticed to visit :)

My recollections, especially from when Theraze and zarqon were maintaining different versions and tried to reconcile them, are wanting/needing:

A Boolean function that returns true if, and only if, the character in the current state with the current equipment and buffs can adventure there.​
If the above function returns False, then a function that returned a list of things that could be done (manually or by a script) that would allow the location to be visited.​
Using the results of the second script to prepare for adventuring was one of the reasons for the different versions. Equipping an outfit as part of the canIGoThere type check was not always what was wanted, for example.

I'm not sure I would use this in a way that expected a between battle script to meet conditions.

I would use it in planning in which case I would also want to script anything that was done to make an area available and possibly execute those before adventuring. I'm not sure if or when I would use validate2 unless I understood what it would, and would not do - swapping an outfit is probably OK, buying a day pass probably isn't.

There were a couple of canAdv bugs that were fixed in canAdv but it might be helpful to list anyway.

canAdv detected that an outfit was needed but did not always make it clear why the outfit was not available (i.e. stat requirement or missing parts). There were definitely times when a script went chasing a missing part only to find out a star requirement kept it from being equipped.

Conceptually the Hippy Camp and Frat House are different areas when the War is unstartled but otherwise could be started. Scripts failed to start the war because canAdv said (for example) the Frat Outfit was needed and equipped it even though the Frat Warrior Outfit was available and the war needed to be started.
 
I would use it in planning in which case I would also want to script anything that was done to make an area available and possibly execute those before adventuring. I'm not sure if or when I would use validate2 unless I understood what it would, and would not do - swapping an outfit is probably OK, buying a day pass probably isn't.
If you automate using the GUI or the adventure command or the adventure() or adv1() ASH functions, you are already using validate1 AND validate2. :)

I have a Draft PR where I am working on making validate1 look at essentially all potential adventuring areas and decide if what you have available to you right now will let you get there. A lot of that is making use of existing quest tracking.

The Frat House and Hippy Camp have MANY different zones, accessible based on quest status and outfit.

Pre-war or post-war:

Code:
Island    adventure=27    Env: indoor Stat: 30    Frat House
Island    adventure=29    Env: indoor Stat: 30    Frat House In Disguise
Island    adventure=150    Env: outdoor Stat: 100    The Orcish Frat House (Bombed Back to the Stone Age)
Island    adventure=26    Env: outdoor Stat: 30    Hippy Camp
Island    adventure=65    Env: outdoor Stat: 30    Hippy Camp In Disguise
Island    adventure=149    Env: outdoor Stat: 100    The Hippy Camp (Bombed Back to the Stone Age)

On verge of war:

Code:
IsleWar    adventure=135    Env: indoor Stat: 0    Wartime Frat House
IsleWar    adventure=134    Env: indoor Stat: 165    Wartime Frat House (Hippy Disguise)
IsleWar    adventure=133    Env: indoor Stat: 0    Wartime Hippy Camp
IsleWar    adventure=131    Env: indoor Stat: 165    Wartime Hippy Camp (Frat Disguise)
 
I am trying to understand this all, but it seems like you are [potentially] proposing that Mafia automatically switch outfits/equipment and/or use items.
I would strongly prefer a simple Boolean function that returns true if, and only if, the character in the current state with the current equipment and buffs can adventure there.

I can think of no current commands that switch outfits to adventure... maybe I am missing some. But I see potential problems with scripting this and having Mafia change equipment.
As an example: I want to fight a sausage goblin in a specific location loc. I equip the Kramco, then use location_accessible(loc) which switches equipment and I no longer have the Kramco equipped. I adventure at loc and end up fighting a non-sausage goblin monster. That makes me sad and uses a turn I did not expect.

Mafia commands that do switch equipment (to cast a skill or buy an item) always switch back before proceeding.
 
I am trying to understand this all, but it seems like you are [potentially] proposing that Mafia automatically switch outfits/equipment and/or use items.
I would strongly prefer a simple Boolean function that returns true if, and only if, the character in the current state with the current equipment and buffs can adventure there.
Yes. That is (more or less) what validate1 does right now.

I can think of no current commands that switch outfits to adventure... maybe I am missing some.
How about the "adventure" command? Or adventuring via the GUI? Or the adventure() or adv1() functions?

The following zones will all automatically switch outfits (or perhaps equip an accessory) for you if you try to automate:

Code:
Woods    adventure=73    Env: outdoor Stat: 20    8-Bit Realm
Knob    cobbsknob=0    Env: none Stat: 0 Level: 6    Throne Room
Plains    adventure=386    Env: outdoor Stat: 120    Inside the Palindome
McLarge    adventure=176    Env: underground Stat: 0    The Mine Foremens' Office
The Drip    adventure=542    Env: outdoor nowander    The Dripping Trees
The Drip    adventure=544    Env: indoor nowander    The Dripping Hall
Island    adventure=29    Env: indoor Stat: 30    Frat House In Disguise
Island    adventure=65    Env: outdoor Stat: 30    Hippy Camp In Disguise
IsleWar    adventure=134    Env: indoor Stat: 165    Wartime Frat House (Hippy Disguise)
IsleWar    adventure=131    Env: indoor Stat: 165    Wartime Hippy Camp (Frat Disguise)
McLarge    mining=1    Env: none Stat: 0    Itznotyerzitz Mine (in Disguise)
Rift    adventure=86    Env: outdoor Stat: 30    Battlefield (Cloaca Uniform)
Rift    adventure=87    Env: outdoor Stat: 30    Battlefield (Dyspepsi Uniform)
IsleWar    adventure=132    Env: outdoor Stat: 180    The Battlefield (Frat Uniform)
IsleWar    adventure=140    Env: outdoor Stat: 180    The Battlefield (Hippy Uniform)
This is existing code in validate2.

But I see potential problems with scripting this and having Mafia change equipment.
As an example: I want to fight a sausage goblin in a specific location loc. I equip the Kramco, then use location_accessible(loc) which switches equipment and I no longer have the Kramco equipped. I adventure at loc and end up fighting a non-sausage goblin monster. That makes me sad and uses a turn I did not expect.
Don't automate in a location that requires a specific off-hand item. :)

Mafia commands that do switch equipment (to cast a skill or buy an item) always switch back before proceeding.
Does the (existing code) "adventure" command switch back if you tell it automate in The Battlefield (Frat Uniform) and it put on the uniform for you?

Again - existing automation uses validate1 and validate2.
I am not talking about changing how existing automation works.
I am talking about opening them up to user scripts so they can assess their current ability to adventure in a particular place.
Like (my understanding of) what CanAdv does. (A script I have never personally used.)
 
can_adv came in two flavours: boolean can_adv(location) and boolean can_adv(location, true). The former told you whether you could adventure in a given location. The latter did the same, but would also put on relevant outfits, use relevant items, etc. The former was more popular, I think.
 
How about the "adventure" command? Or adventuring via the GUI? Or the adventure() or adv1() functions?

The following zones will all automatically switch outfits (or perhaps equip an accessory) for you if you try to automate:

Really? huh.
I'll have to check that out. I learned something new today.
Edit: it does indeed change equipment, and then politely puts it back.


Don't automate in a location that requires a specific off-hand item.
But I wanna automate everything!!!
Time to make sure my scripts carefully work around automatic outfit switching.
 
can_adv came in two flavours: boolean can_adv(location) and boolean can_adv(location, true). The former told you whether you could adventure in a given location. The latter did the same, but would also put on relevant outfits, use relevant items, etc. The former was more popular, I think.
I’m seeing several possible modes for can_adventure

- can I adventure in that location right now in the equipment I am currently wearing with the effects I currently have? (If you automate, validate1 will return true and validate2 will be a no-op).
- can I adventure in that location using simple Stuff I have available - outfit pieces, sonars, enchanted bean, Knob perfume, astral mushroom? (If you automate, validate1 will return true and validate2 will wear the gear and use the items).
- can I adventure in that location with stuff I don’t have? (If you automate, validate1 will return true, maybe your between battle script will buy stuff - or even use a day pass - and validate2 will either pass or fail as a result of that.)

Frono was talking about providing a version with the suiting up and using cheap on hand items. I.e. providing an extra interface to validate2 actions.
 
Think i've found an issue in location_accessible in r26624.

autoscend recently deprecated the dependancy on can_adv and replaced it with location_accessible(). Since then, once im past lvl 11 (in run, in NA), it seems to be attempting to adventure every other adventure in hidden office building (even though it had yet to unlock it (or in 1 case yet to unlock the black market). Having checked, this is due to the preference "hiddenOfficeProgress" being set to 1. if I ever reset the preference

Further debugging, i noticed this behaviour did not occur once I downgraded autoscend to depend on can_adv() again. once I noticed that, I tested the following:

> ash location_accessible($location[hidden office building])

Changing "hidden office building" to "The Hidden Office Building" would get rid of this message. (char 31 to char 53)
Returned: true

In fact, on further investigation, location_accessible returned true for all of hidden hospital, hidden apartment building, and hidden bowling alley, despite the fact I am yet to unlock the hidden city.

based on the description above, it feels like being unable to access the hidden city should cause validate1 / location_accessible to return false for the hidden city locations?
 
KoLAdventure.isCurrentlyAccessible() ends with
Code:
    return true;
  }
As none of
AdventurePool.HIDDEN_APARTMENT, AdventurePool.HIDDEN_HOSPITAL, AdventurePool.HIDDEN_OFFICE or AdventurePool.HIDDEN_BOWLING_ALLEY
are checked inside that function, thus location_accessible($location[The Hidden Office Building]) will always return true even for a character who has just left valhalla and done literally nothing.
 
I am actively working on this. It's a really big job, but I should be able to have a much more capable implementation out within a day or two.
I am renaming the ASH function to can_adventure(location). :)

Note that the first cut is basically just finishing validate1 - which checks that a location is available given path, zodiac, quest status, and so on - but is not checking for required outfits. It's assuming that if you can get to such a location, you can and will acquire necessary items - and validate2 will use them if necessary. (Just simple stuff. No day passes. ;))

 
I am actively working on this. It's a really big job, but I should be able to have a much more capable implementation out within a day or two.
I am renaming the ASH function to can_adventure(location). :)

Note that the first cut is basically just finishing validate1 - which checks that a location is available given path, zodiac, quest status, and so on - but is not checking for required outfits. It's assuming that if you can get to such a location, you can and will acquire necessary items - and validate2 will use them if necessary. (Just simple stuff. No day passes. ;))

Excellent work Veracity!
For now I've manually added a copy of canadv.ash to the autoscend repo and reverted the change to use location_accessible() but when you make can_adventure() available I'll switch it over to that so we can give as much feedback as possible.
Your efforts are very much appreciated (y)
 
The first version of my extensive revamp of this is out now.

From the PR documentation:

--------------------
The goal is for KoLAdventure.validate1 (exposed via ASH can_adventure(location)) to tell you if a zone is mechanically available to you right now.
  • true if your level/path/quest status/path/etc. means you COULD adventure in that location right now.
    Some zones require an outfit. We sometimes check that you own such - the Pirate Ship, for example - but mostly not; we figure your script will know what you need.
    Some zones may not be accessible right this second, but if you have items in inventory which will make them available - sonars, enchanted bean - we'll give you a true.
    KoLAdventure.validate2 will put on outfits and use such (simple) items to finish giving you access.
  • false if there is literally nothing you can do to go to the zone.
    For example,
    -- wrong zodiac sign or class or path
    -- IOTM supplied content, but you have Standard restrictions and the item is out of Standard
  • false if there is nothing you can do to go to a zone right now without spending expensive resources.
    For example,
    -- Adventuring to gain levels or fulfill quests.
    -- Using a (possibly expensive) day pass to access IOTM adventuring content.
Note that this is still used in the "automated adventuring" workflow:
  1. validate1 to decide if an adventuring zone is available
  2. (User's betweenBattle script to, perhaps, acquire outfit pieces)
  3. validate2 to do simple things that validate1 saw were already possible
This is ready to be released into the wild for testing. It is almost complete (additional work described at end), but should be eminently usable as is.

And although I wrote a bunch of tests - KoLAdventureValidationTest has over 1000 lines - I did not test anything close to all adventuring zones. I expect to add many more in future PRs. :)

User visible changes:
  • I added some adventures into one-adventure zones so I could attach an IOTM-related item. This means that, for example, The Neverending Party doesn't sort into the "Town" zone in the GUI - although its parent zone is "Town". And there is not a single "Psychoses" zone; each of the seven jars has its own zone - whose parent zone is "Psychoses" - so I can tag each zone with the appropriate jar.
If that is too annoying, perhaps we'll need to revamp how the GUI sorts zones with parent zones. Perhaps it should group together every zone whose parent (or parent of parent ...) is the same "top level" zone?
  • The betweenBattle script can no longer acquire items that would have made a less-strict validate1 return true and allow validate2 to return true. Like day-passes, which are probably too expensive. :)
  • Renamed Postwar Junkyard to The Junkyard - which is what KoL calls it.
    Renamed Hippy Camp Disguised to Hippy Camp (Hippy Disguise)
    Inspired by Wartime Hippy Camp (Frat Disguise)
    Renamed Frat House Disguised to Frat House (Frat Disguise)
    Inspired by Wartime Frat House (Hippy Disguise)
 
Back
Top