Consider number of separate overworld areas when picking a grid layout

This commit is contained in:
Catobat
2026-02-21 21:19:16 +01:00
parent b8884e9010
commit 6a59f58230
2 changed files with 79 additions and 22 deletions

View File

@@ -4,7 +4,7 @@ import RaceRandom as random
import random as _random
from typing import List, Dict, Optional, Set, Tuple
from BaseClasses import OWEdge, World, Direction, Terrain
from OverworldShuffle import connect_two_way, validate_layout
from OverworldShuffle import connect_two_way, get_separate_ow_areas, validate_layout
ENABLE_KEEP_SIMILAR_SPECIAL_HANDLING = False
DRAW_IMAGE = True
@@ -155,7 +155,7 @@ class LayoutGeneratorOptions:
'forced_non_crossed_edges', 'forced_crossed_edges', 'check_reachability',
'crossed_chance', 'crossed_limit',
'sort_by_edge_sides', 'sort_by_max_edges_per_side', 'sort_by_piece_size',
'min_runs', 'max_runs', 'target_runs_times_successes')
'min_runs', 'max_runs', 'target_runs_times_successes', 'score_mult_separate_areas')
def __init__(
self,
@@ -183,7 +183,8 @@ class LayoutGeneratorOptions:
sort_by_piece_size: bool = False,
min_runs: int = 100,
max_runs: int = 10000,
target_runs_times_successes: int = 5000
target_runs_times_successes: int = 5000,
score_mult_separate_areas: float = 4
):
self.horizontal_wrap = horizontal_wrap
self.vertical_wrap = vertical_wrap
@@ -210,6 +211,7 @@ class LayoutGeneratorOptions:
self.min_runs = min_runs
self.max_runs = max_runs
self.target_runs_times_successes = target_runs_times_successes
self.score_mult_separate_areas = score_mult_separate_areas
class LayoutGeneratorResult:
"""
@@ -1655,14 +1657,18 @@ def place_single_restriction_pieces(
return remaining_pieces, placed_count
def get_random_layout(world: World, player: int, connected_edges_cache: List[str], pieces_to_place: List[Piece], options: LayoutGeneratorOptions, prio_edges: List[str], overworld_screens: Dict[int, Screen]) -> LayoutGeneratorResult:
skip_validate_layout = world.accessibility[player] == 'none'
score_mult_separate_areas = options.score_mult_separate_areas
total_score = 0
best_score = -1000000
worst_score = 1000000
best_grid_info = None
separate_areas = None
# Pre-place pieces with single-element restriction lists
base_grid_info = create_empty_grid_info(0.0)
remaining_pieces, preplaced_count = place_single_restriction_pieces(world, player, base_grid_info, options, pieces_to_place)
logger = logging.getLogger('')
successes = 0
failures = 0
@@ -1716,21 +1722,18 @@ def get_random_layout(world: World, player: int, connected_edges_cache: List[str
# Successfully placed all pieces
if options.check_reachability:
disabled_count = connect_edges_for_screen_layout(world, player, grid_info, options, connected_edges, prio_edges, overworld_screens, False)
valid_layout = validate_layout(world, player)
# Clean up connected entrances and edges
for edge_name in connected_edges:
if edge_name not in connected_edges_cache:
entrance = world.get_entrance(edge_name, player)
entrance.connected_region.entrances.remove(entrance)
entrance.connected_region = None
edge = world.get_owedge(edge_name, player)
edge.dest = None
valid_layout = skip_validate_layout or validate_layout(world, player)
if not valid_layout:
clean_up_connected_edges(world, player, connected_edges_cache, connected_edges)
failures += 1
continue
logging.getLogger('').debug("Found valid layout with " + str(disabled_count)+ " disabled edges")
successes += 1
score = -disabled_count
if score_mult_separate_areas > 0:
separate_areas = len(get_separate_ow_areas(world, player))
score -= score_mult_separate_areas * separate_areas
logger.debug("Found valid layout with " + str(disabled_count) + " disabled edges and " + str(separate_areas) + " separate areas")
clean_up_connected_edges(world, player, connected_edges_cache, connected_edges)
successes += 1
else:
successes += 1
score = major_score
@@ -1759,6 +1762,15 @@ def get_random_layout(world: World, player: int, connected_edges_cache: List[str
failures=failures
)
def clean_up_connected_edges(world: World, player: int, connected_edges_cache: List[str], connected_edges: List[str]) -> None:
for edge_name in connected_edges:
if edge_name not in connected_edges_cache:
entrance = world.get_entrance(edge_name, player)
entrance.connected_region.entrances.remove(entrance)
entrance.connected_region = None
edge = world.get_owedge(edge_name, player)
edge.dest = None
def get_prioritized_edges(world: World, player: int) -> List[str]:
prio_edges = []
if world.accessibility[player] != 'none':
@@ -2080,7 +2092,8 @@ def generate_random_grid_layout(world: World, player: int, connected_edges: List
sort_by_piece_size=True,
min_runs=100,
max_runs=10000,
target_runs_times_successes=5000
target_runs_times_successes=5000,
score_mult_separate_areas=4
)
overworld_screens = initialize_screens(world, player)
@@ -2112,6 +2125,7 @@ def generate_random_grid_layout(world: World, player: int, connected_edges: List
logger.debug(f" Successes: {result.successes}")
logger.debug(f" Failures: {result.failures}")
logger.debug(f" Generation time: {elapsed_time:.3f}s")
logger.debug(f" Layouts per second: {(result.successes+result.failures)/elapsed_time:.3f}")
if DRAW_IMAGE:
logger.debug("Creating layout visualization...")