ArchipelagoMW / Archipelago

Archipelago Multi-Game Randomizer and Server
https://archipelago.gg
Other
412 stars 557 forks source link

The Witness: Rules Optimisation #3617

Open NewSoupVi opened 5 days ago

NewSoupVi commented 5 days ago

The rules in The Witness are automatically generated in a "RandoCore" kind of way.

This means that each requirement is a Set[Set[str]], and is then converted first to a List[List[CollectionRule]], and then to a CollectionRule via any(all(...) ...)

This introduces a lot of function call overhead.

In this PR, I aimed to catch some of these cases.

  1. If the rule is empty, it no longer sets lambda state: any(all([[]])), instead it just skips setting an access rule.
  2. If the rule only has one access option (len(rule) == 1), we skip the outer any and just call all on the one option that exists.
  3. If the rule only has one access option (len(rule) == 1) and that one option only has a single element (len(rule[0]) == 1), we skip both the any and the all and set the singular CollectionRule as the access rule directly.
  4. If an access option only contains item requirements (as opposed to region requirements), we collect them and use state.has_all_counts, instead of chaining together state.has in an inefficient way.
  5. Laser events have been simplified. They aren't as simple as I wish they were, but that's because of a legitimate difference in how some checks require the Desert Laser to be redirected, and others do not.

Tested Using the unit tests added in a different PR, and by comparing outputs with the same seed

Benchmark This leads to tangible improvements - A two player MultiWorld with shuffle_doors: panels generates in 0.78s instead of 0.95s.


Drafted for now because I'm still debating how I want to structure this code. Also, it's based on the panel hunt PR because of a change that PR makes to how lasers are counted.