The Witness: Automatic Postgame & Disabled Panels Calculation (#2698)

* Refactor postgame code to be more readable

* Change all references to options to strings

* oops

* Fix some outdated code related to yaml-disabled EPs

* Small fixes to short/longbox stuff (thanks Medic)

* comment

* fix duplicate

* Removed triplicate lmfao

* Better comment

* added another 'unfun' postgame consideration

* comment

* more option strings

* oops

* Remove an unnecessary comparison

* another string missed

* New classification changes (Credit: Exempt-Medic)

* Don't need to pass world

* Comments

* Replace it with another magic system because why not at this point :DDDDDD

* oops

* Oops

* Another was missed

* Make events conditions. Disable_Non_Randomized will no longer just 'have all events'

* What the fuck? Has this just always been broken?

* Don't have boolean function with 'not' in the name

* Another useful classification

* slight code refactor

* Funny haha booleans

* This would create a really bad merge error

* I can't believe this actually kind of works

* And here's the punchline. + some bugfixes

* Comment dat code

* Comments galore

* LMAO OOPS

* so nice I did it twice

* debug x2

* Careful

* Add more comments

* That comment is a bit unnecessary now

* Fix overriding region connections

* Correct a comment

* Correct again

* Rename variable

* Idk I guess this is in this branch now

* More tweaking of postgame & comments

* This is commit just exists to fix that grammar error

* I think I can just fucking delete this now???

* Forgot to reset something here

* Delete dead codepath

* Obelisk Keys were getting yote erroneously

* More comments

* Fix duplicate connections

* Oopsington III

* performance improvements & cleanup

* More rules cleanup and performance improvements

* Oh cool I can do this huh

* Okay but this is even more swag tho

* Lazy eval

* remove some implicit checks

* Is this too magical yet

* more guard magic

* Maaaaaaaagiccccccccc

* Laaaaaaaaaaaaaaaazzzzzzyyyyyyyyyyy

* Make it docstring

* Newline bc I like that better

* this is a little spooky lol

* lol

* Wait

* spoO

* Better variable name and comment

* Improved comment again

* better API

* oops I deleted a deepcopy

* lol help

* Help???

* player_regionsns lmao

* Add some comments

* Make doors disabled properly again. I hope this works

* Don't disable lasers

* Omega oops

* Make Floor 2 Exit not exist

* Make a fix that's warps compatible

* I think this was an oversight, I tested a seed and it seems to have the same result

* This is definitely less Violet than before

* Does this feel more violet lol

* Exception if a laser gets disabled, cleanup

* Ruff

* >:(

* consistent utils import

* Make autopostgame more reviewable (hopefully)

* more reviewability

* WitnessRule

* replace another instance of it

* lint

* style

* comment

* found the bug

* Move comment

* Get rid of cache and ugly allow_victory

* comments and lint
This commit is contained in:
NewSoupVi
2024-06-01 23:11:28 +02:00
committed by GitHub
parent da33d1576a
commit e49b1f9fbb
19 changed files with 643 additions and 518 deletions

View File

@@ -1,5 +1,6 @@
from collections import defaultdict
from functools import lru_cache
from typing import Dict, List
from typing import Dict, List, Set, Tuple
from .item_definition_classes import (
CATEGORY_NAME_MAPPINGS,
@@ -10,11 +11,13 @@ from .item_definition_classes import (
WeightedItemDefinition,
)
from .utils import (
WitnessRule,
define_new_region,
get_items,
get_sigma_expert_logic,
get_sigma_normal_logic,
get_vanilla_logic,
logical_or_witness_rules,
parse_lambda,
)
@@ -41,7 +44,8 @@ class StaticWitnessLogicObj:
current_region = new_region_and_connections[0]
region_name = current_region["name"]
self.ALL_REGIONS_BY_NAME[region_name] = current_region
self.STATIC_CONNECTIONS_BY_REGION_NAME[region_name] = new_region_and_connections[1]
for connection in new_region_and_connections[1]:
self.CONNECTIONS_WITH_DUPLICATES[region_name][connection[0]].add(connection[1])
current_area["regions"].append(region_name)
continue
@@ -80,13 +84,15 @@ class StaticWitnessLogicObj:
self.ENTITIES_BY_NAME[self.ENTITIES_BY_HEX[entity_hex]["checkName"]] = self.ENTITIES_BY_HEX[entity_hex]
self.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX[entity_hex] = {
"panels": parse_lambda(required_panel_lambda)
"entities": parse_lambda(required_panel_lambda)
}
# Lasers and Doors exist in a region, but don't have a regional *requirement*
# If a laser is activated, you don't need to physically walk up to it for it to count
# As such, logically, they behave more as if they were part of the "Entry" region
self.ALL_REGIONS_BY_NAME["Entry"]["panels"].append(entity_hex)
self.ALL_REGIONS_BY_NAME["Entry"]["entities"].append(entity_hex)
# However, it will also be important to keep track of their physical location for postgame purposes.
current_region["physical_entities"].append(entity_hex)
continue
required_item_lambda = line_split.pop(0)
@@ -117,7 +123,7 @@ class StaticWitnessLogicObj:
required_items = frozenset(required_items)
requirement = {
"panels": required_panels,
"entities": required_panels,
"items": required_items
}
@@ -145,7 +151,37 @@ class StaticWitnessLogicObj:
self.ENTITIES_BY_NAME[self.ENTITIES_BY_HEX[entity_hex]["checkName"]] = self.ENTITIES_BY_HEX[entity_hex]
self.STATIC_DEPENDENT_REQUIREMENTS_BY_HEX[entity_hex] = requirement
current_region["panels"].append(entity_hex)
current_region["entities"].append(entity_hex)
current_region["physical_entities"].append(entity_hex)
def reverse_connection(self, source_region: str, connection: Tuple[str, Set[WitnessRule]]):
target = connection[0]
traversal_options = connection[1]
# Reverse this connection with all its possibilities, except the ones marked as "OneWay".
for requirement in traversal_options:
remaining_options = set()
for option in requirement:
if not any(req == "TrueOneWay" for req in option):
remaining_options.add(option)
if remaining_options:
self.CONNECTIONS_WITH_DUPLICATES[target][source_region].add(frozenset(remaining_options))
def reverse_connections(self):
# Iterate all connections
for region_name, connections in list(self.CONNECTIONS_WITH_DUPLICATES.items()):
for connection in connections.items():
self.reverse_connection(region_name, connection)
def combine_connections(self):
# All regions need to be present, and this dict is copied later - Thus, defaultdict is not the correct choice.
self.STATIC_CONNECTIONS_BY_REGION_NAME = {region_name: set() for region_name in self.ALL_REGIONS_BY_NAME}
for source, connections in self.CONNECTIONS_WITH_DUPLICATES.items():
for target, requirement in connections.items():
combined_req = logical_or_witness_rules(requirement)
self.STATIC_CONNECTIONS_BY_REGION_NAME[source].add((target, combined_req))
def __init__(self, lines=None) -> None:
if lines is None:
@@ -154,6 +190,7 @@ class StaticWitnessLogicObj:
# All regions with a list of panels in them and the connections to other regions, before logic adjustments
self.ALL_REGIONS_BY_NAME = dict()
self.ALL_AREAS_BY_NAME = dict()
self.CONNECTIONS_WITH_DUPLICATES = defaultdict(lambda: defaultdict(lambda: set()))
self.STATIC_CONNECTIONS_BY_REGION_NAME = dict()
self.ENTITIES_BY_HEX = dict()
@@ -167,6 +204,8 @@ class StaticWitnessLogicObj:
self.ENTITY_ID_TO_NAME = dict()
self.read_logic_file(lines)
self.reverse_connections()
self.combine_connections()
# Item data parsed from WitnessItems.txt