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