mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
Lingo: Various generation optimizations (#2479)
Almost all of the events have been eradicated, which significantly improves both generation speed and playthrough calculation. Previously, checking for access to a location involved checking for access to each panel in the location, as well as recursively checking for access to any panels required by those panels. This potentially performed the same check multiple times. The access requirements for locations are now calculated and flattened in generate_early, so that the access function can directly check for the required rooms, doors, and colors. These flattened access requirements are also used for Entrance checking, and register_indirect_condition is used to make sure that can_reach(Region) is safe to use. The Mastery and Level 2 rules now just run a bunch of access rules and count the number of them that succeed, instead of relying on event items. Finally: the Level 2 panel hunt is now enabled even when Level 2 is not the victory condition, as I feel that generation is fast enough now for that to be acceptable.
This commit is contained in:

committed by
GitHub

parent
8a852abdc4
commit
6dccf36f88
@@ -1,23 +1,23 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from BaseClasses import CollectionState
|
||||
from .options import VictoryCondition
|
||||
from .player_logic import LingoPlayerLogic, PlayerLocation
|
||||
from .static_logic import PANELS_BY_ROOM, PROGRESSION_BY_ROOM, PROGRESSIVE_ITEMS, RoomAndDoor
|
||||
from .player_logic import AccessRequirements, LingoPlayerLogic, PlayerLocation
|
||||
from .static_logic import PROGRESSION_BY_ROOM, PROGRESSIVE_ITEMS, RoomAndDoor
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import LingoWorld
|
||||
|
||||
|
||||
def lingo_can_use_entrance(state: CollectionState, room: str, door: RoomAndDoor, player: int,
|
||||
def lingo_can_use_entrance(state: CollectionState, room: str, door: RoomAndDoor, world: "LingoWorld",
|
||||
player_logic: LingoPlayerLogic):
|
||||
if door is None:
|
||||
return True
|
||||
|
||||
return _lingo_can_open_door(state, room, room if door.room is None else door.room, door.door, player, player_logic)
|
||||
effective_room = room if door.room is None else door.room
|
||||
return _lingo_can_open_door(state, effective_room, door.door, world, player_logic)
|
||||
|
||||
|
||||
def lingo_can_use_pilgrimage(state: CollectionState, player: int, player_logic: LingoPlayerLogic):
|
||||
def lingo_can_use_pilgrimage(state: CollectionState, world: "LingoWorld", player_logic: LingoPlayerLogic):
|
||||
fake_pilgrimage = [
|
||||
["Second Room", "Exit Door"], ["Crossroads", "Tower Entrance"],
|
||||
["Orange Tower Fourth Floor", "Hot Crusts Door"], ["Outside The Initiated", "Shortcut to Hub Room"],
|
||||
@@ -28,77 +28,77 @@ def lingo_can_use_pilgrimage(state: CollectionState, player: int, player_logic:
|
||||
["Outside The Agreeable", "Tenacious Entrance"]
|
||||
]
|
||||
for entrance in fake_pilgrimage:
|
||||
if not state.has(player_logic.ITEM_BY_DOOR[entrance[0]][entrance[1]], player):
|
||||
if not _lingo_can_open_door(state, entrance[0], entrance[1], world, player_logic):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def lingo_can_use_location(state: CollectionState, location: PlayerLocation, room_name: str, world: "LingoWorld",
|
||||
def lingo_can_use_location(state: CollectionState, location: PlayerLocation, world: "LingoWorld",
|
||||
player_logic: LingoPlayerLogic):
|
||||
for panel in location.panels:
|
||||
panel_room = room_name if panel.room is None else panel.room
|
||||
if not _lingo_can_solve_panel(state, room_name, panel_room, panel.panel, world, player_logic):
|
||||
return False
|
||||
|
||||
return True
|
||||
return _lingo_can_satisfy_requirements(state, location.access, world, player_logic)
|
||||
|
||||
|
||||
def lingo_can_use_mastery_location(state: CollectionState, world: "LingoWorld"):
|
||||
return state.has("Mastery Achievement", world.player, world.options.mastery_achievements.value)
|
||||
def lingo_can_use_mastery_location(state: CollectionState, world: "LingoWorld", player_logic: LingoPlayerLogic):
|
||||
satisfied_count = 0
|
||||
for access_req in player_logic.mastery_reqs:
|
||||
if _lingo_can_satisfy_requirements(state, access_req, world, player_logic):
|
||||
satisfied_count += 1
|
||||
return satisfied_count >= world.options.mastery_achievements.value
|
||||
|
||||
|
||||
def _lingo_can_open_door(state: CollectionState, start_room: str, room: str, door: str, player: int,
|
||||
player_logic: LingoPlayerLogic):
|
||||
"""
|
||||
Determines whether a door can be opened
|
||||
"""
|
||||
item_name = player_logic.ITEM_BY_DOOR[room][door]
|
||||
if item_name in PROGRESSIVE_ITEMS:
|
||||
progression = PROGRESSION_BY_ROOM[room][door]
|
||||
return state.has(item_name, player, progression.index)
|
||||
|
||||
return state.has(item_name, player)
|
||||
def lingo_can_use_level_2_location(state: CollectionState, world: "LingoWorld", player_logic: LingoPlayerLogic):
|
||||
counted_panels = 0
|
||||
state.update_reachable_regions(world.player)
|
||||
for region in state.reachable_regions[world.player]:
|
||||
for access_req, panel_count in player_logic.counting_panel_reqs.get(region.name, []):
|
||||
if _lingo_can_satisfy_requirements(state, access_req, world, player_logic):
|
||||
counted_panels += panel_count
|
||||
if counted_panels >= world.options.level_2_requirement.value - 1:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _lingo_can_solve_panel(state: CollectionState, start_room: str, room: str, panel: str, world: "LingoWorld",
|
||||
player_logic: LingoPlayerLogic):
|
||||
"""
|
||||
Determines whether a panel can be solved
|
||||
"""
|
||||
if start_room != room and not state.can_reach(room, "Region", world.player):
|
||||
return False
|
||||
|
||||
if room == "Second Room" and panel == "ANOTHER TRY" \
|
||||
and world.options.victory_condition == VictoryCondition.option_level_2 \
|
||||
and not state.has("Counting Panel Solved", world.player, world.options.level_2_requirement.value - 1):
|
||||
return False
|
||||
|
||||
panel_object = PANELS_BY_ROOM[room][panel]
|
||||
for req_room in panel_object.required_rooms:
|
||||
def _lingo_can_satisfy_requirements(state: CollectionState, access: AccessRequirements, world: "LingoWorld",
|
||||
player_logic: LingoPlayerLogic):
|
||||
for req_room in access.rooms:
|
||||
if not state.can_reach(req_room, "Region", world.player):
|
||||
return False
|
||||
|
||||
for req_door in panel_object.required_doors:
|
||||
if not _lingo_can_open_door(state, start_room, room if req_door.room is None else req_door.room,
|
||||
req_door.door, world.player, player_logic):
|
||||
for req_door in access.doors:
|
||||
if not _lingo_can_open_door(state, req_door.room, req_door.door, world, player_logic):
|
||||
return False
|
||||
|
||||
for req_panel in panel_object.required_panels:
|
||||
if not _lingo_can_solve_panel(state, start_room, room if req_panel.room is None else req_panel.room,
|
||||
req_panel.panel, world, player_logic):
|
||||
return False
|
||||
|
||||
if len(panel_object.colors) > 0 and world.options.shuffle_colors:
|
||||
for color in panel_object.colors:
|
||||
if len(access.colors) > 0 and world.options.shuffle_colors:
|
||||
for color in access.colors:
|
||||
if not state.has(color.capitalize(), world.player):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def make_location_lambda(location: PlayerLocation, room_name: str, world: "LingoWorld", player_logic: LingoPlayerLogic):
|
||||
if location.name == player_logic.MASTERY_LOCATION:
|
||||
return lambda state: lingo_can_use_mastery_location(state, world)
|
||||
def _lingo_can_open_door(state: CollectionState, room: str, door: str, world: "LingoWorld",
|
||||
player_logic: LingoPlayerLogic):
|
||||
"""
|
||||
Determines whether a door can be opened
|
||||
"""
|
||||
if door not in player_logic.item_by_door.get(room, {}):
|
||||
return _lingo_can_satisfy_requirements(state, player_logic.door_reqs[room][door], world, player_logic)
|
||||
|
||||
return lambda state: lingo_can_use_location(state, location, room_name, world, player_logic)
|
||||
item_name = player_logic.item_by_door[room][door]
|
||||
if item_name in PROGRESSIVE_ITEMS:
|
||||
progression = PROGRESSION_BY_ROOM[room][door]
|
||||
return state.has(item_name, world.player, progression.index)
|
||||
|
||||
return state.has(item_name, world.player)
|
||||
|
||||
|
||||
def make_location_lambda(location: PlayerLocation, world: "LingoWorld", player_logic: LingoPlayerLogic):
|
||||
if location.name == player_logic.mastery_location:
|
||||
return lambda state: lingo_can_use_mastery_location(state, world, player_logic)
|
||||
|
||||
if world.options.level_2_requirement > 1\
|
||||
and (location.name == "Second Room - ANOTHER TRY" or location.name == player_logic.level_2_location):
|
||||
return lambda state: lingo_can_use_level_2_location(state, world, player_logic)
|
||||
|
||||
return lambda state: lingo_can_use_location(state, location, world, player_logic)
|
||||
|
Reference in New Issue
Block a user