make random world targeting smarter, in only considering possible unfilled locations
This commit is contained in:
		
							parent
							
								
									c24a376dd0
								
							
						
					
					
						commit
						bd86a07115
					
				|  | @ -5,7 +5,7 @@ from enum import Enum, unique | ||||||
| import logging | import logging | ||||||
| import json | import json | ||||||
| from collections import OrderedDict, Counter, deque | from collections import OrderedDict, Counter, deque | ||||||
| from typing import Union, Optional, List, Set, Dict, NamedTuple | from typing import Union, Optional, List, Set, Dict, NamedTuple, Iterable | ||||||
| import secrets | import secrets | ||||||
| import random | import random | ||||||
| 
 | 
 | ||||||
|  | @ -393,6 +393,12 @@ class World(object): | ||||||
|         return [location for location in self.get_locations() if |         return [location for location in self.get_locations() if | ||||||
|                 (player is None or location.player == player) and location.item is None and location.can_reach(state)] |                 (player is None or location.player == player) and location.item is None and location.can_reach(state)] | ||||||
| 
 | 
 | ||||||
|  |     def get_unfilled_locations_for_players(self, location_name: str, players: Iterable[int]): | ||||||
|  |         for player in players: | ||||||
|  |             location = self.get_location(location_name, player) | ||||||
|  |             if location.item is None: | ||||||
|  |                 yield location | ||||||
|  | 
 | ||||||
|     def unlocks_new_location(self, item) -> bool: |     def unlocks_new_location(self, item) -> bool: | ||||||
|         temp_state = self.state.copy() |         temp_state = self.state.copy() | ||||||
|         temp_state.collect(item, True) |         temp_state.collect(item, True) | ||||||
|  |  | ||||||
							
								
								
									
										58
									
								
								Fill.py
								
								
								
								
							
							
						
						
									
										58
									
								
								Fill.py
								
								
								
								
							|  | @ -1,12 +1,14 @@ | ||||||
| import logging | import logging | ||||||
| import typing | import typing | ||||||
| 
 | 
 | ||||||
| from BaseClasses import CollectionState | from BaseClasses import CollectionState, PlandoItem | ||||||
|  | from Items import ItemFactory | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class FillError(RuntimeError): | class FillError(RuntimeError): | ||||||
|     pass |     pass | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def fill_restrictive(world, base_state: CollectionState, locations, itempool, single_player_placement=False): | def fill_restrictive(world, base_state: CollectionState, locations, itempool, single_player_placement=False): | ||||||
|     def sweep_from_pool(): |     def sweep_from_pool(): | ||||||
|         new_state = base_state.copy() |         new_state = base_state.copy() | ||||||
|  | @ -339,3 +341,57 @@ def balance_multiworld_progression(world): | ||||||
|                 break |                 break | ||||||
|             elif not sphere_locations: |             elif not sphere_locations: | ||||||
|                 raise RuntimeError('Not all required items reachable. Something went terribly wrong here.') |                 raise RuntimeError('Not all required items reachable. Something went terribly wrong here.') | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def distribute_planned(world): | ||||||
|  |     world_name_lookup = {world.player_names[player_id][0]: player_id for player_id in world.player_ids} | ||||||
|  | 
 | ||||||
|  |     for player in world.player_ids: | ||||||
|  |         placement: PlandoItem | ||||||
|  |         for placement in world.plando_items[player]: | ||||||
|  |             item = ItemFactory(placement.item, player) | ||||||
|  |             target_world: int = placement.world | ||||||
|  |             if target_world is False or world.players == 1: | ||||||
|  |                 target_world = player  # in own world | ||||||
|  |             elif target_world is True:  # in any other world | ||||||
|  |                 unfilled = list(location for location in world.get_unfilled_locations_for_players( | ||||||
|  |                     placement.location, | ||||||
|  |                     set(world.player_ids) - {player}) if location.item_rule(item) | ||||||
|  |                                 ) | ||||||
|  |                 if not unfilled: | ||||||
|  |                     raise FillError(f"Could not find a world with an unfilled location {placement.location}") | ||||||
|  | 
 | ||||||
|  |                 target_world = world.random.choice(unfilled).player | ||||||
|  | 
 | ||||||
|  |             elif target_world is None:  # any random world | ||||||
|  |                 unfilled = list(location for location in world.get_unfilled_locations_for_players( | ||||||
|  |                     placement.location, | ||||||
|  |                     set(world.player_ids)) if location.item_rule(item) | ||||||
|  |                                 ) | ||||||
|  |                 if not unfilled: | ||||||
|  |                     raise FillError(f"Could not find a world with an unfilled location {placement.location}") | ||||||
|  | 
 | ||||||
|  |                 target_world = world.random.choice(unfilled).player | ||||||
|  | 
 | ||||||
|  |             elif type(target_world) == int:  # target world by player id | ||||||
|  |                 pass | ||||||
|  |             else:  # find world by name | ||||||
|  |                 target_world = world_name_lookup[target_world] | ||||||
|  | 
 | ||||||
|  |             location = world.get_location(placement.location, target_world) | ||||||
|  |             if location.item: | ||||||
|  |                 raise Exception(f"Cannot place item into already filled location {location}.") | ||||||
|  | 
 | ||||||
|  |             if placement.from_pool: | ||||||
|  |                 try: | ||||||
|  |                     world.itempool.remove(item) | ||||||
|  |                 except ValueError: | ||||||
|  |                     logging.warning(f"Could not remove {item} from pool as it's already missing from it.") | ||||||
|  | 
 | ||||||
|  |             if location.can_fill(world.state, item, False): | ||||||
|  |                 world.push_item(location, item, collect=False) | ||||||
|  |                 location.event = True  # flag location to be checked during fill | ||||||
|  |                 location.locked = True | ||||||
|  |                 logging.debug(f"Plando placed {item} at {location}") | ||||||
|  |             else: | ||||||
|  |                 raise Exception(f"Can't place {item} at {location} due to fill condition not met.") | ||||||
|  |  | ||||||
							
								
								
									
										39
									
								
								Main.py
								
								
								
								
							
							
						
						
									
										39
									
								
								Main.py
								
								
								
								
							|  | @ -17,7 +17,7 @@ from EntranceShuffle import link_entrances, link_inverted_entrances, plando_conn | ||||||
| from Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, get_hash_string | from Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, get_hash_string | ||||||
| from Rules import set_rules | from Rules import set_rules | ||||||
| from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive | from Dungeons import create_dungeons, fill_dungeons, fill_dungeons_restrictive | ||||||
| from Fill import distribute_items_restrictive, flood_items, balance_multiworld_progression | from Fill import distribute_items_restrictive, flood_items, balance_multiworld_progression, distribute_planned | ||||||
| from ItemPool import generate_itempool, difficulties, fill_prizes | from ItemPool import generate_itempool, difficulties, fill_prizes | ||||||
| from Utils import output_path, parse_player_names, get_options, __version__, _version_tuple | from Utils import output_path, parse_player_names, get_options, __version__, _version_tuple | ||||||
| import Patch | import Patch | ||||||
|  | @ -179,42 +179,7 @@ def main(args, seed=None): | ||||||
| 
 | 
 | ||||||
|     logger.info("Running Item Plando") |     logger.info("Running Item Plando") | ||||||
| 
 | 
 | ||||||
|     world_name_lookup = {world.player_names[player_id][0]: player_id for player_id in world.player_ids} |     distribute_planned(world) | ||||||
| 
 |  | ||||||
|     for player in world.player_ids: |  | ||||||
|         placement: PlandoItem |  | ||||||
|         for placement in world.plando_items[player]: |  | ||||||
|             target_world: int = placement.world |  | ||||||
|             if target_world is False or world.players == 1: |  | ||||||
|                 target_world = player  # in own world |  | ||||||
|             elif target_world is True:  # in any other world |  | ||||||
|                 target_world = player |  | ||||||
|                 while target_world == player: |  | ||||||
|                     target_world = world.random.randint(1, world.players + 1) |  | ||||||
|             elif target_world is None:  # any random world |  | ||||||
|                 target_world = world.random.randint(1, world.players + 1) |  | ||||||
|             elif type(target_world) == int:  # target world by player id |  | ||||||
|                 pass |  | ||||||
|             else:  # find world by name |  | ||||||
|                 target_world = world_name_lookup[target_world] |  | ||||||
| 
 |  | ||||||
|             location = world.get_location(placement.location, target_world) |  | ||||||
|             if location.item: |  | ||||||
|                 raise Exception(f"Cannot place item into already filled location {location}.") |  | ||||||
|             item = ItemFactory(placement.item, player) |  | ||||||
|             if placement.from_pool: |  | ||||||
|                 try: |  | ||||||
|                     world.itempool.remove(item) |  | ||||||
|                 except ValueError: |  | ||||||
|                     logger.warning(f"Could not remove {item} from pool as it's already missing from it.") |  | ||||||
| 
 |  | ||||||
|             if location.can_fill(world.state, item, False): |  | ||||||
|                 world.push_item(location, item, collect=False) |  | ||||||
|                 location.event = True  # flag location to be checked during fill |  | ||||||
|                 location.locked = True |  | ||||||
|                 logger.debug(f"Plando placed {item} at {location}") |  | ||||||
|             else: |  | ||||||
|                 raise Exception(f"Can't place {item} at {location} due to fill condition not met.") |  | ||||||
| 
 | 
 | ||||||
|     logger.info('Placing Dungeon Items.') |     logger.info('Placing Dungeon Items.') | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue