2017-10-15 19:35:45 +00:00
import random
import logging
def distribute_items_cutoff ( world , cutoffrate = 0.33 ) :
# get list of locations to fill in
fill_locations = world . get_unfilled_locations ( )
random . shuffle ( fill_locations )
# get items to distribute
random . shuffle ( world . itempool )
itempool = world . itempool
total_advancement_items = len ( [ item for item in itempool if item . advancement ] )
placed_advancement_items = 0
progress_done = False
advancement_placed = False
# sweep once to pick up preplaced items
world . state . sweep_for_events ( )
while itempool and fill_locations :
candidate_item_to_place = None
item_to_place = None
for item in itempool :
if advancement_placed or ( progress_done and ( item . advancement or item . priority ) ) :
item_to_place = item
break
if item . advancement :
candidate_item_to_place = item
if world . unlocks_new_location ( item ) :
item_to_place = item
placed_advancement_items + = 1
break
if item_to_place is None :
# check if we can reach all locations and that is why we find no new locations to place
if not progress_done and len ( world . get_reachable_locations ( ) ) == len ( world . get_locations ( ) ) :
progress_done = True
continue
# check if we have now placed all advancement items
if progress_done :
advancement_placed = True
continue
# we might be in a situation where all new locations require multiple items to reach. If that is the case, just place any advancement item we've found and continue trying
if candidate_item_to_place is not None :
item_to_place = candidate_item_to_place
placed_advancement_items + = 1
else :
# we placed all available progress items. Maybe the game can be beaten anyway?
if world . can_beat_game ( ) :
logging . getLogger ( ' ' ) . warning ( ' Not all locations reachable. Game beatable anyway. ' )
progress_done = True
continue
raise RuntimeError ( ' No more progress items left to place. ' )
spot_to_fill = None
for location in ( fill_locations if placed_advancement_items / total_advancement_items < cutoffrate else reversed ( fill_locations ) ) :
if world . state . can_reach ( location ) and location . can_fill ( item_to_place ) :
spot_to_fill = location
break
if spot_to_fill is None :
# we filled all reachable spots. Maybe the game can be beaten anyway?
if world . can_beat_game ( ) :
logging . getLogger ( ' ' ) . warning ( ' Not all items placed. Game beatable anyway. ' )
break
raise RuntimeError ( ' No more spots to place %s ' % item_to_place )
world . push_item ( spot_to_fill , item_to_place , True )
itempool . remove ( item_to_place )
fill_locations . remove ( spot_to_fill )
logging . getLogger ( ' ' ) . debug ( ' Unplaced items: %s - Unfilled Locations: %s ' % ( [ item . name for item in itempool ] , [ location . name for location in fill_locations ] ) )
def distribute_items_staleness ( world ) :
# get list of locations to fill in
fill_locations = world . get_unfilled_locations ( )
random . shuffle ( fill_locations )
# get items to distribute
random . shuffle ( world . itempool )
itempool = world . itempool
progress_done = False
advancement_placed = False
# sweep once to pick up preplaced items
world . state . sweep_for_events ( )
while itempool and fill_locations :
candidate_item_to_place = None
item_to_place = None
for item in itempool :
if advancement_placed or ( progress_done and ( item . advancement or item . priority ) ) :
item_to_place = item
break
if item . advancement :
candidate_item_to_place = item
if world . unlocks_new_location ( item ) :
item_to_place = item
break
if item_to_place is None :
# check if we can reach all locations and that is why we find no new locations to place
if not progress_done and len ( world . get_reachable_locations ( ) ) == len ( world . get_locations ( ) ) :
progress_done = True
continue
# check if we have now placed all advancement items
if progress_done :
advancement_placed = True
continue
# we might be in a situation where all new locations require multiple items to reach. If that is the case, just place any advancement item we've found and continue trying
if candidate_item_to_place is not None :
item_to_place = candidate_item_to_place
else :
# we placed all available progress items. Maybe the game can be beaten anyway?
if world . can_beat_game ( ) :
logging . getLogger ( ' ' ) . warning ( ' Not all locations reachable. Game beatable anyway. ' )
progress_done = True
continue
raise RuntimeError ( ' No more progress items left to place. ' )
spot_to_fill = None
for location in fill_locations :
# increase likelyhood of skipping a location if it has been found stale
if not progress_done and random . randint ( 0 , location . staleness_count ) > 2 :
continue
if world . state . can_reach ( location ) and location . can_fill ( item_to_place ) :
spot_to_fill = location
break
else :
location . staleness_count + = 1
# might have skipped too many locations due to potential staleness. Do not check for staleness now to find a candidate
if spot_to_fill is None :
for location in fill_locations :
if world . state . can_reach ( location ) and location . can_fill ( item_to_place ) :
spot_to_fill = location
break
if spot_to_fill is None :
# we filled all reachable spots. Maybe the game can be beaten anyway?
if world . can_beat_game ( ) :
logging . getLogger ( ' ' ) . warning ( ' Not all items placed. Game beatable anyway. ' )
break
raise RuntimeError ( ' No more spots to place %s ' % item_to_place )
world . push_item ( spot_to_fill , item_to_place , True )
itempool . remove ( item_to_place )
fill_locations . remove ( spot_to_fill )
logging . getLogger ( ' ' ) . debug ( ' Unplaced items: %s - Unfilled Locations: %s ' % ( [ item . name for item in itempool ] , [ location . name for location in fill_locations ] ) )
def fill_restrictive ( world , base_state , locations , itempool ) :
def sweep_from_pool ( ) :
new_state = base_state . copy ( )
for item in itempool :
new_state . collect ( item , True )
new_state . sweep_for_events ( )
return new_state
while itempool and locations :
item_to_place = itempool . pop ( )
maximum_exploration_state = sweep_from_pool ( )
spot_to_fill = None
for location in locations :
if location . can_fill ( item_to_place ) :
if world . check_beatable_only :
starting_state = base_state . copy ( )
for item in itempool :
starting_state . collect ( item , True )
if maximum_exploration_state . can_reach ( location ) :
if world . check_beatable_only :
starting_state . collect ( item_to_place , True )
else :
spot_to_fill = location
break
if world . check_beatable_only and world . can_beat_game ( starting_state ) :
spot_to_fill = location
break
if spot_to_fill is None :
# we filled all reachable spots. Maybe the game can be beaten anyway?
if world . can_beat_game ( ) :
if not world . check_beatable_only :
logging . getLogger ( ' ' ) . warning ( ' Not all items placed. Game beatable anyway. ' )
break
raise RuntimeError ( ' No more spots to place %s ' % item_to_place )
world . push_item ( spot_to_fill , item_to_place , False )
locations . remove ( spot_to_fill )
spot_to_fill . event = True
2017-10-15 20:34:46 +00:00
def distribute_items_restrictive ( world , gftower_trash_count = 0 , fill_locations = None ) :
# If not passed in, then get a shuffled list of locations to fill in
if not fill_locations :
fill_locations = world . get_unfilled_locations ( )
random . shuffle ( fill_locations )
2017-10-15 19:35:45 +00:00
# get items to distribute
random . shuffle ( world . itempool )
progitempool = [ item for item in world . itempool if item . advancement ]
prioitempool = [ item for item in world . itempool if not item . advancement and item . priority ]
restitempool = [ item for item in world . itempool if not item . advancement and not item . priority ]
# fill in gtower locations with trash first
gtower_locations = [ location for location in fill_locations if ' Ganons Tower ' in location . name ]
random . shuffle ( gtower_locations )
trashcnt = 0
while gtower_locations and restitempool and trashcnt < gftower_trash_count :
spot_to_fill = gtower_locations . pop ( )
item_to_place = restitempool . pop ( )
world . push_item ( spot_to_fill , item_to_place , False )
fill_locations . remove ( spot_to_fill )
trashcnt + = 1
random . shuffle ( fill_locations )
2017-10-15 20:34:46 +00:00
fill_locations . reverse ( )
2017-10-15 19:35:45 +00:00
fill_restrictive ( world , world . state , fill_locations , progitempool )
random . shuffle ( fill_locations )
2017-10-15 20:34:46 +00:00
fast_fill ( world , prioitempool , fill_locations )
2017-10-15 19:35:45 +00:00
2017-10-15 20:34:46 +00:00
fast_fill ( world , restitempool , fill_locations )
2017-10-15 19:35:45 +00:00
logging . getLogger ( ' ' ) . debug ( ' Unplaced items: %s - Unfilled Locations: %s ' % ( [ item . name for item in progitempool + prioitempool + restitempool ] , [ location . name for location in fill_locations ] ) )
2017-10-15 20:34:46 +00:00
def fast_fill ( world , item_pool , fill_locations ) :
while item_pool and fill_locations :
spot_to_fill = fill_locations . pop ( )
item_to_place = item_pool . pop ( )
world . push_item ( spot_to_fill , item_to_place , False )
2017-10-15 19:35:45 +00:00
def flood_items ( world ) :
# get items to distribute
random . shuffle ( world . itempool )
itempool = world . itempool
progress_done = False
# sweep once to pick up preplaced items
world . state . sweep_for_events ( )
# fill world from top of itempool while we can
while not progress_done :
location_list = world . get_unfilled_locations ( )
random . shuffle ( location_list )
spot_to_fill = None
for location in location_list :
if world . state . can_reach ( location ) and location . can_fill ( itempool [ 0 ] ) :
spot_to_fill = location
break
if spot_to_fill :
item = itempool . pop ( 0 )
world . push_item ( spot_to_fill , item , True )
continue
# ran out of spots, check if we need to step in and correct things
if len ( world . get_reachable_locations ( ) ) == len ( world . get_locations ( ) ) :
progress_done = True
continue
# need to place a progress item instead of an already placed item, find candidate
item_to_place = None
candidate_item_to_place = None
for item in itempool :
if item . advancement :
candidate_item_to_place = item
if world . unlocks_new_location ( item ) :
item_to_place = item
break
# we might be in a situation where all new locations require multiple items to reach. If that is the case, just place any advancement item we've found and continue trying
if item_to_place is None :
if candidate_item_to_place is not None :
item_to_place = candidate_item_to_place
else :
raise RuntimeError ( ' No more progress items left to place. ' )
# find item to replace with progress item
location_list = world . get_reachable_locations ( )
random . shuffle ( location_list )
for location in location_list :
if location . item is not None and not location . item . advancement and not location . item . priority and not location . item . key :
# safe to replace
replace_item = location . item
replace_item . location = None
itempool . append ( replace_item )
world . push_item ( location , item_to_place , True )
itempool . remove ( item_to_place )
break