Archipelago icon indicating copy to clipboard operation
Archipelago copied to clipboard

Core: Add "has_all_counts" and "has_any_count" functions to CollectionState

Open NewSoupVi opened this issue 11 months ago • 0 comments

What was done

If you have a requirement like: 3 Progressive Tools, Bucket and 1 Progressive Resource Crafting, right now you'd have to express that as state.has_all(["Progressive Resource Crafting", "Bucket"], player) and state.has("Progressive Tools", player, 3). It's not horrible, but it's slightly unwieldy and a little slower than it could be because more function calls do in fact == longer runtime. I'm not sure how that optimises for an .exe build though.

So, this PR aims to add two new functions: CollectionState.has_all_counts and CollectionState.has_any_count. These take a dict of item names to counts. So instead, the condition from the example above could be written as: state.has_all_counts({"Progressive Tools": 3, "Bucket": 1, "Progressive Resource Crafting": 1}, player), which looks really nice in my opinion.

These functions are really simple and just use the any function / the all function on a generator comprehension over the dict items. This yields fast, lazy evaluation that should be on par with other functions.

Personal Motivation

I don't actually know what my location's requirements are when I set them in The Witness. I have a standardised "Logic Condition" format. At the start, this gets filled for each interactable entity in the game for all items that could possibly ever needed to activate it, and what other entities it depends on. I then run a function that automatically 1. recursively flattens the recursive requirements to a single requirement per entity, 2. removes any items that don't exist due to settings, and then 3. optimises the condition (Getting rid of redundancies).

At this point, what I have is basically a Set of and chains of items, with the chains being ored together, per entity. This has yielded incredibly fast results for Witness.

However, I do not have a general way of converting these "or chains of and chains" to a simple CollectionRule, specifically because has_all doesn't support items with multiple copies!. If this existed, I could just do:

location.access_rule = any(state.has_all_counts(possibility, player) for possibility in condition)

Don't take it from just me though! One new world dev has said "I was just wanting this" and another world dev pointed out that KH2 already implements this behavior because it was useful to them. However, notably (no shade intended), they did a much worse job of implementing this behavior.

I think thesenew functions are very pretty, they should be really fast, and they are useful to me and multiple other world devs.

On naming

I would've just called the "all" function has_counts, but I couldn't think of a good way to name the "any" function, which was suggested for addition as well. And I thought they should be named similarly, so I decided to go with has_all_counts and has_any_count. Absolutely willing to hear alternate suggestions.

NewSoupVi avatar Mar 11 '24 22:03 NewSoupVi