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
|
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]:
|
def initialize_screens(world: World, player: int) -> Dict[int, Screen]:
|
||||||
overworld_screens: Dict[int, Screen] = {}
|
overworld_screens: Dict[int, Screen] = {}
|
||||||
screen_edges_map = group_owedges_by_screens(world, player)
|
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)
|
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:
|
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
|
total_score = 0
|
||||||
best_score = -1000000
|
best_score = -1000000
|
||||||
worst_score = 1000000
|
worst_score = 1000000
|
||||||
best_grid_info = None
|
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
|
successes = 0
|
||||||
failures = 0
|
failures = 0
|
||||||
run = 0
|
run = 0
|
||||||
while run < options.min_runs or (run * successes < options.target_runs_times_successes and run < options.max_runs):
|
while run < options.min_runs or (run * successes < options.target_runs_times_successes and run < options.max_runs):
|
||||||
run += 1
|
run += 1
|
||||||
connected_edges = connected_edges_cache.copy()
|
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:
|
for piece in piece_list:
|
||||||
piece.delay = 0
|
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))):
|
for i in range(1, min(options.multi_choice, len(piece_list))):
|
||||||
pieces.append(piece_list[i])
|
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:
|
if not result.success:
|
||||||
failures += 1
|
failures += 1
|
||||||
|
|||||||
Reference in New Issue
Block a user