Place pieces with only one possible position first
This commit is contained in:
@@ -273,6 +273,25 @@ def create_empty_grid_info(edge_connection_seed: float) -> GridInfo:
|
||||
edge_connection_seed=edge_connection_seed
|
||||
)
|
||||
|
||||
def copy_grid_info(source: GridInfo, edge_connection_seed: float) -> GridInfo:
|
||||
"""
|
||||
Create a deep copy of a GridInfo object with a new edge_connection_seed.
|
||||
Only copies the grid data structures, not the OWEdge references (which are shared).
|
||||
"""
|
||||
return GridInfo(
|
||||
grid=[[row[:] for row in world_grid] for world_grid in source.grid],
|
||||
north_edges_grid=[[[cell[:] for cell in row] for row in world_grid] for world_grid in source.north_edges_grid],
|
||||
south_edges_grid=[[[cell[:] for cell in row] for row in world_grid] for world_grid in source.south_edges_grid],
|
||||
west_edges_grid=[[[cell[:] for cell in row] for row in world_grid] for world_grid in source.west_edges_grid],
|
||||
east_edges_grid=[[[cell[:] for cell in row] for row in world_grid] for world_grid in source.east_edges_grid],
|
||||
north_edges_water_grid=[[[cell[:] for cell in row] for row in world_grid] for world_grid in source.north_edges_water_grid],
|
||||
south_edges_water_grid=[[[cell[:] for cell in row] for row in world_grid] for world_grid in source.south_edges_water_grid],
|
||||
west_edges_water_grid=[[[cell[:] for cell in row] for row in world_grid] for world_grid in source.west_edges_water_grid],
|
||||
east_edges_water_grid=[[[cell[:] for cell in row] for row in world_grid] for world_grid in source.east_edges_water_grid],
|
||||
crossed_groups=[row[:] for row in source.crossed_groups],
|
||||
edge_connection_seed=edge_connection_seed
|
||||
)
|
||||
|
||||
def initialize_screens(world: World, player: int) -> Dict[int, Screen]:
|
||||
overworld_screens: Dict[int, Screen] = {}
|
||||
screen_edges_map = group_owedges_by_screens(world, player)
|
||||
@@ -1171,21 +1190,152 @@ def random_place_piece(
|
||||
|
||||
return PiecePlacementResult(success=True, piece=piece, score_major=used_score_major, score_minor=used_score_minor)
|
||||
|
||||
def place_single_restriction_pieces(
|
||||
world: World,
|
||||
player: int,
|
||||
grid_info: GridInfo,
|
||||
options: LayoutGeneratorOptions,
|
||||
pieces: List[Piece]
|
||||
) -> Tuple[List[Piece], int]:
|
||||
"""
|
||||
Place pieces that have a restriction list with only a single element.
|
||||
These pieces are forced into a single position, so we can place them deterministically.
|
||||
|
||||
This function iteratively:
|
||||
1. Validates restriction lists against current grid state and grid bounds
|
||||
2. Places pieces with single-element restrictions
|
||||
3. Repeats until no more pieces can be placed
|
||||
|
||||
Returns a tuple of (remaining_pieces, count_of_placed_pieces).
|
||||
"""
|
||||
use_crossed_groups = (world.owCrossed[player] == 'polar' and world.owMixed[player]) or world.owCrossed[player] == 'grouped'
|
||||
|
||||
remaining_pieces = list(pieces)
|
||||
placed_count = 0
|
||||
|
||||
placed_this_iteration = True
|
||||
while placed_this_iteration:
|
||||
placed_this_iteration = False
|
||||
|
||||
# Validate and update restriction lists for all remaining pieces
|
||||
for piece in remaining_pieces:
|
||||
if piece.restriction is None:
|
||||
continue
|
||||
|
||||
valid_positions = []
|
||||
for position in piece.restriction:
|
||||
row = position // 8
|
||||
column = position % 8
|
||||
wrld = piece.world
|
||||
piece_crossed_groups = piece.crossed_groups
|
||||
|
||||
# Check if this position is valid
|
||||
is_valid = True
|
||||
|
||||
# Check if piece would go outside grid bounds when wrapping is disabled
|
||||
if not options.horizontal_wrap and column + piece.width > 8:
|
||||
is_valid = False
|
||||
if not options.vertical_wrap and row + piece.height > 8:
|
||||
is_valid = False
|
||||
|
||||
# Check for overlap with already placed pieces
|
||||
if is_valid:
|
||||
for k in range(piece.height):
|
||||
if not is_valid:
|
||||
break
|
||||
row_idx = (row + k) % 8
|
||||
for l in range(piece.width):
|
||||
col_idx = (column + l) % 8
|
||||
|
||||
# Check main world overlap
|
||||
if grid_info.grid[wrld][row_idx][col_idx] != -1 and piece.main.screens[k][l]:
|
||||
is_valid = False
|
||||
break
|
||||
|
||||
# Check parallel world overlap
|
||||
if piece.parallel and grid_info.grid[1 - wrld][row_idx][col_idx] != -1 and piece.parallel.screens[k][l]:
|
||||
is_valid = False
|
||||
break
|
||||
|
||||
# Check crossed groups
|
||||
if use_crossed_groups and grid_info.crossed_groups[row_idx][col_idx] != -1 and grid_info.crossed_groups[row_idx][col_idx] != piece_crossed_groups[k][l]:
|
||||
is_valid = False
|
||||
break
|
||||
|
||||
if is_valid:
|
||||
valid_positions.append(position)
|
||||
|
||||
# Update the restriction list
|
||||
if len(valid_positions) == 0:
|
||||
raise GenerationException(f"No valid positions remaining for piece with restriction list (original: {piece.restriction})")
|
||||
|
||||
piece.restriction = valid_positions
|
||||
|
||||
# Place pieces with single-element restrictions
|
||||
new_remaining_pieces = []
|
||||
for piece in remaining_pieces:
|
||||
# Check if this piece has exactly one restriction position
|
||||
if piece.restriction is not None and len(piece.restriction) == 1:
|
||||
position = piece.restriction[0]
|
||||
row = position // 8
|
||||
column = position % 8
|
||||
wrld = piece.world
|
||||
piece_crossed_groups = piece.crossed_groups
|
||||
|
||||
# Place the piece on the grid
|
||||
for k in range(piece.height):
|
||||
row_idx = (row + k) % 8
|
||||
for l in range(piece.width):
|
||||
col_idx = (column + l) % 8
|
||||
num_pieces = 2 if piece.parallel else 1
|
||||
for p in range(num_pieces):
|
||||
world_piece = piece.main if p == 0 else piece.parallel
|
||||
w = wrld if p == 0 else 1 - wrld
|
||||
|
||||
grid_info.grid[w][row_idx][col_idx] = world_piece.grid[k][l]
|
||||
grid_info.north_edges_grid[w][row_idx][col_idx] = world_piece.north_edges[k][l]
|
||||
grid_info.south_edges_grid[w][row_idx][col_idx] = world_piece.south_edges[k][l]
|
||||
grid_info.west_edges_grid[w][row_idx][col_idx] = world_piece.west_edges[k][l]
|
||||
grid_info.east_edges_grid[w][row_idx][col_idx] = world_piece.east_edges[k][l]
|
||||
|
||||
if not world.owTerrain[player]:
|
||||
grid_info.north_edges_water_grid[w][row_idx][col_idx] = world_piece.north_edges_water[k][l]
|
||||
grid_info.south_edges_water_grid[w][row_idx][col_idx] = world_piece.south_edges_water[k][l]
|
||||
grid_info.west_edges_water_grid[w][row_idx][col_idx] = world_piece.west_edges_water[k][l]
|
||||
grid_info.east_edges_water_grid[w][row_idx][col_idx] = world_piece.east_edges_water[k][l]
|
||||
|
||||
if use_crossed_groups:
|
||||
grid_info.crossed_groups[row_idx][col_idx] = piece_crossed_groups[k][l]
|
||||
|
||||
placed_count += 1
|
||||
placed_this_iteration = True
|
||||
else:
|
||||
new_remaining_pieces.append(piece)
|
||||
|
||||
remaining_pieces = new_remaining_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:
|
||||
total_score = 0
|
||||
best_score = -1000000
|
||||
worst_score = 1000000
|
||||
best_grid_info = 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)
|
||||
|
||||
successes = 0
|
||||
failures = 0
|
||||
run = 0
|
||||
while run < options.min_runs or (run * successes < options.target_runs_times_successes and run < options.max_runs):
|
||||
run += 1
|
||||
connected_edges = connected_edges_cache.copy()
|
||||
piece_list = pieces_to_place.copy()
|
||||
piece_list = remaining_pieces.copy()
|
||||
|
||||
grid_info = create_empty_grid_info(random.random())
|
||||
# Copy the pre-placed grid with a new random seed for edge connections
|
||||
grid_info = copy_grid_info(base_grid_info, random.random())
|
||||
for piece in piece_list:
|
||||
piece.delay = 0
|
||||
|
||||
@@ -1212,7 +1362,7 @@ def get_random_layout(world: World, player: int, connected_edges_cache: List[str
|
||||
for i in range(1, min(options.multi_choice, len(piece_list))):
|
||||
pieces.append(piece_list[i])
|
||||
|
||||
result = random_place_piece(world, player, grid_info, options, pieces, len(placed_pieces) < options.first_ignore_bonus_points)
|
||||
result = random_place_piece(world, player, grid_info, options, pieces, len(placed_pieces) + preplaced_count < options.first_ignore_bonus_points)
|
||||
|
||||
if not result.success:
|
||||
failures += 1
|
||||
|
||||
Reference in New Issue
Block a user