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.
If the rule is empty, it no longer sets lambda state: any(all([[]])), instead it just skips setting an access rule.
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.
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.
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.
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.
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.
lambda state: any(all([[]]))
, instead it just skips setting an access rule.len(rule) == 1
), we skip the outerany
and just callall
on the one option that exists.len(rule) == 1
) and that one option only has a single element (len(rule[0]) == 1
), we skip both theany
and theall
and set the singular CollectionRule as the access rule directly.state.has_all_counts
, instead of chaining togetherstate.has
in an inefficient way.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.