2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								import  logging  
						 
					
						
							
								
									
										
										
										
											2020-08-14 00:34:41 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								import  typing  
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								import  collections  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  itertools  
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-07-21 18:08:15 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								from  BaseClasses  import  CollectionState ,  Location ,  MultiWorld  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								from  worlds . generic  import  PlandoItem  
						 
					
						
							
								
									
										
										
										
											2021-08-10 09:03:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								from  worlds . AutoWorld  import  call_all  
						 
					
						
							
								
									
										
										
										
											2019-04-18 11:23:24 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2018-01-27 16:21:32 -05:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								class  FillError ( RuntimeError ) :  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    pass 
							 
						 
					
						
							
								
									
										
										
										
											2017-11-04 14:23:57 -04:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-01-04 15:14:20 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 07:57:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								def  fill_restrictive ( world :  MultiWorld ,  base_state :  CollectionState ,  locations ,  itempool ,  single_player_placement = False ,  
						 
					
						
							
								
									
										
										
										
											2021-01-10 15:50:18 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                     lock = False ) : 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    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 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2019-12-14 17:47:36 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    unplaced_items  =  [ ] 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-13 10:40:23 -08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    placements  =  [ ] 
							 
						 
					
						
							
								
									
										
										
										
											2019-12-14 17:47:36 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2019-12-18 20:47:35 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    reachable_items  =  { } 
							 
						 
					
						
							
								
									
										
										
										
											2019-12-14 17:47:36 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    for  item  in  itempool : 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-18 17:27:31 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        reachable_items . setdefault ( item . player ,  [ ] ) . append ( item ) 
							 
						 
					
						
							
								
									
										
										
										
											2019-12-18 20:47:35 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-03-18 17:27:31 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    while  any ( reachable_items . values ( ) )  and  locations : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        items_to_place  =  [ items . pop ( )  for  items  in  reachable_items . values ( )  if  items ]   # grab one item per player 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        for  item  in  items_to_place : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            itempool . remove ( item ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        maximum_exploration_state  =  sweep_from_pool ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        has_beaten_game  =  world . has_beaten_game ( maximum_exploration_state ) 
							 
						 
					
						
							
								
									
										
										
										
											2019-12-18 20:47:35 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-03-18 17:27:31 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        for  item_to_place  in  items_to_place : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  world . accessibility [ item_to_place . player ]  ==  ' none ' : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                perform_access_check  =  not  world . has_beaten_game ( maximum_exploration_state , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                                                 item_to_place . player )  if  single_player_placement  else  not  has_beaten_game 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            else : 
							 
						 
					
						
							
								
									
										
										
										
											2019-12-18 20:47:35 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                perform_access_check  =  True 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-18 17:27:31 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            for  i ,  location  in  enumerate ( locations ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  ( not  single_player_placement  or  location . player  ==  item_to_place . player )  \
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        and  location . can_fill ( maximum_exploration_state ,  item_to_place ,  perform_access_check ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    spot_to_fill  =  locations . pop ( i )  # poping by index is faster than removing by content, 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    # skipping a scan for the element 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    break 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            else : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                # we filled all reachable spots. Maybe the game can be beaten anyway? 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                unplaced_items . append ( item_to_place ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  world . accessibility [ item_to_place . player ]  !=  ' none '  and  world . can_beat_game ( ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    logging . warning ( 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        f ' Not all items placed. Game beatable anyway. (Could not place  { item_to_place } ) ' ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    continue 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                raise  FillError ( f ' No more spots to place  { item_to_place } , locations  { locations }  are invalid.  ' 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                f ' Already placed  { len ( placements ) } :  { " ,  " . join ( str ( place )  for  place  in  placements ) } ' ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            world . push_item ( spot_to_fill ,  item_to_place ,  False ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            spot_to_fill . locked  =  lock 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            placements . append ( spot_to_fill ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            spot_to_fill . event  =  True 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2019-12-14 17:47:36 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    itempool . extend ( unplaced_items ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-10 09:03:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								def  distribute_items_restrictive ( world :  MultiWorld ,  fill_locations = None ) :  
						 
					
						
							
								
									
										
										
										
											2017-10-15 16:34:46 -04:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    # If not passed in, then get a shuffled list of locations to fill in 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  not  fill_locations : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        fill_locations  =  world . get_unfilled_locations ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-07-14 07:01:51 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        world . random . shuffle ( fill_locations ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    # get items to distribute 
							 
						 
					
						
							
								
									
										
										
										
											2020-07-14 07:01:51 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    world . random . shuffle ( world . itempool ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-06-04 03:30:59 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    progitempool  =  [ ] 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-15 16:52:30 -05:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    nonexcludeditempool  =  [ ] 
							 
						 
					
						
							
								
									
										
										
										
											2020-06-04 03:30:59 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    localrestitempool  =  { player :  [ ]  for  player  in  range ( 1 ,  world . players  +  1 ) } 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-30 22:20:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    nonlocalrestitempool  =  [ ] 
							 
						 
					
						
							
								
									
										
										
										
											2020-06-04 03:30:59 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    restitempool  =  [ ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  item  in  world . itempool : 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-15 16:52:30 -05:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        if  item . advancement : 
							 
						 
					
						
							
								
									
										
										
										
											2020-06-04 03:30:59 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            progitempool . append ( item ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-23 08:55:44 -05:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        elif  item . never_exclude :   # this only gets nonprogression items which should not appear in excluded locations 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-15 16:52:30 -05:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            nonexcludeditempool . append ( item ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-06-04 03:30:59 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        elif  item . name  in  world . local_items [ item . player ] : 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-30 09:57:25 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            localrestitempool [ item . player ] . append ( item ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-30 22:20:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        elif  item . name  in  world . non_local_items [ item . player ] : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            nonlocalrestitempool . append ( item ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-06-04 03:30:59 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        else : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            restitempool . append ( item ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-02-27 16:27:35 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    world . random . shuffle ( fill_locations ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-30 22:20:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    call_all ( world ,  " fill_hook " ,  progitempool ,  nonexcludeditempool ,  localrestitempool ,  nonlocalrestitempool ,  restitempool ,  fill_locations ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-10 09:03:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    fill_restrictive ( world ,  world . state ,  fill_locations ,  progitempool ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-07-15 16:52:30 -05:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    if  nonexcludeditempool : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        world . random . shuffle ( fill_locations ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-23 08:55:44 -05:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        fill_restrictive ( world ,  world . state ,  fill_locations ,  nonexcludeditempool )   # needs logical fill to not conflict with local items 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-15 16:52:30 -05:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-01-30 09:57:25 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    if  any ( localrestitempool . values ( ) ) :   # we need to make sure some fills are limited to certain worlds 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-07 12:43:11 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        local_locations  =  { player :  [ ]  for  player  in  world . player_ids } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        for  location  in  fill_locations : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            local_locations [ location . player ] . append ( location ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        for  locations  in  local_locations . values ( ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            world . random . shuffle ( locations ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2020-07-09 16:16:31 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        for  player ,  items  in  localrestitempool . items ( ) :   # items already shuffled 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-07 12:43:11 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            player_local_locations  =  local_locations [ player ] 
							 
						 
					
						
							
								
									
										
										
										
											2020-06-04 03:30:59 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            for  item_to_place  in  items : 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-07 12:43:11 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                if  not  player_local_locations : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    logging . warning ( f " Ran out of local locations for player  { player } ,  " 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                    f " cannot place  { item_to_place } . " ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    break 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                spot_to_fill  =  player_local_locations . pop ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-06-04 03:30:59 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                world . push_item ( spot_to_fill ,  item_to_place ,  False ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                fill_locations . remove ( spot_to_fill ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-30 22:20:44 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    for  item_to_place  in  nonlocalrestitempool : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        for  i ,  location  in  enumerate ( fill_locations ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  location . player  !=  item_to_place . player : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                world . push_item ( fill_locations . pop ( i ) ,  item_to_place ,  False ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                break 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        else : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            logging . warning ( f " Could not place non_local_item  { item_to_place }  among  { fill_locations } , tossing. " ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2020-07-14 07:01:51 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    world . random . shuffle ( fill_locations ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2020-08-14 00:34:41 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    restitempool ,  fill_locations  =  fast_fill ( world ,  restitempool ,  fill_locations ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-24 09:52:12 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    unplaced  =  progitempool  +  restitempool 
							 
						 
					
						
							
								
									
										
										
										
											2020-08-01 06:22:59 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    unfilled  =  [ location . name  for  location  in  fill_locations ] 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2020-08-01 06:22:59 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    if  unplaced  or  unfilled : 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-24 09:52:12 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        logging . warning ( f ' Unplaced items( { len ( unplaced ) } ):  { unplaced }  - Unfilled Locations( { len ( unfilled ) } ):  { unfilled } ' ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 07:57:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								def  fast_fill ( world :  MultiWorld ,  item_pool :  typing . List ,  fill_locations :  typing . List )  - >  typing . Tuple [ typing . List ,  typing . List ] :  
						 
					
						
							
								
									
										
										
										
											2020-08-14 00:34:41 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    placing  =  min ( len ( item_pool ) ,  len ( fill_locations ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  item ,  location  in  zip ( item_pool ,  fill_locations ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        world . push_item ( location ,  item ,  False ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    return  item_pool [ placing : ] ,  fill_locations [ placing : ] 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 16:34:46 -04:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2017-11-04 14:23:57 -04:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 07:57:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								def  flood_items ( world :  MultiWorld ) :  
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    # get items to distribute 
							 
						 
					
						
							
								
									
										
										
										
											2020-07-14 07:01:51 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    world . random . shuffle ( world . itempool ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								    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 ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-07-14 07:01:51 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        world . random . shuffle ( location_list ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        spot_to_fill  =  None 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        for  location  in  location_list : 
							 
						 
					
						
							
								
									
										
										
										
											2018-01-02 00:39:53 -05:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            if  location . can_fill ( world . state ,  itempool [ 0 ] ) : 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								                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 : 
							 
						 
					
						
							
								
									
										
										
										
											2018-01-27 16:21:32 -05:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                raise  FillError ( ' No more progress items left to place. ' ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        # find item to replace with progress item 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        location_list  =  world . get_reachable_locations ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-07-14 07:01:51 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        world . random . shuffle ( location_list ) 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								        for  location  in  location_list : 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-10 09:47:28 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            if  location . item  is  not  None  and  not  location . item . advancement : 
							 
						 
					
						
							
								
									
										
										
										
											2017-10-15 15:35:45 -04:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								                # 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 
							 
						 
					
						
							
								
									
										
										
										
											2019-04-18 11:23:24 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2020-12-23 11:28:42 -06:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 07:57:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								def  balance_multiworld_progression ( world :  MultiWorld ) :  
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    balanceable_players  =  { player  for  player  in  range ( 1 ,  world . players  +  1 )  if  world . progression_balancing [ player ] } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    if  not  balanceable_players : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        logging . info ( ' Skipping multiworld progression balancing. ' ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    else : 
							 
						 
					
						
							
								
									
										
										
										
											2020-07-30 20:17:04 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        logging . info ( f ' Balancing multiworld progression for  { len ( balanceable_players ) }  Players. ' ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        state  =  CollectionState ( world ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        checked_locations  =  set ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        unchecked_locations  =  set ( world . get_locations ( ) ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        reachable_locations_count  =  { player :  0  for  player  in  world . player_ids } 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-17 22:08:28 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        def  get_sphere_locations ( sphere_state ,  locations ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            sphere_state . sweep_for_events ( key_only = True ,  locations = locations ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            return  { loc  for  loc  in  locations  if  sphere_state . can_reach ( loc ) } 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        while  True : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            sphere_locations  =  get_sphere_locations ( state ,  unchecked_locations ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            for  location  in  sphere_locations : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                unchecked_locations . remove ( location ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                reachable_locations_count [ location . player ]  + =  1 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  checked_locations : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                threshold  =  max ( reachable_locations_count . values ( ) )  -  20 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                balancing_players  =  { player  for  player ,  reachables  in  reachable_locations_count . items ( )  if 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                     reachables  <  threshold  and  player  in  balanceable_players } 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                if  balancing_players : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    balancing_state  =  state . copy ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    balancing_unchecked_locations  =  unchecked_locations . copy ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    balancing_reachables  =  reachable_locations_count . copy ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    balancing_sphere  =  sphere_locations . copy ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    candidate_items  =  collections . defaultdict ( set ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    while  True : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        for  location  in  balancing_sphere : 
							 
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                            if  location . event : 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                                balancing_state . collect ( location . item ,  True ,  location ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                                player  =  location . item . player 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                # only replace items that end up in another player's world 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                if  not  location . locked  and  player  in  balancing_players  and  location . player  !=  player : 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                                    candidate_items [ player ] . add ( location ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                        balancing_sphere  =  get_sphere_locations ( balancing_state ,  balancing_unchecked_locations ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        for  location  in  balancing_sphere : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            balancing_unchecked_locations . remove ( location ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            balancing_reachables [ location . player ]  + =  1 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        if  world . has_beaten_game ( balancing_state )  or  all ( 
							 
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                                reachables  > =  threshold  for  reachables  in  balancing_reachables . values ( ) ) : 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                            break 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        elif  not  balancing_sphere : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            raise  RuntimeError ( ' Not all required items reachable. Something went terribly wrong here. ' ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    unlocked_locations  =  collections . defaultdict ( set ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    for  l  in  unchecked_locations : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        if  l  not  in  balancing_unchecked_locations : 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                            unlocked_locations [ l . player ] . add ( l ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    items_to_replace  =  [ ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    for  player  in  balancing_players : 
							 
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                        locations_to_test  =  unlocked_locations [ player ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        items_to_test  =  candidate_items [ player ] 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                        while  items_to_test : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            testing  =  items_to_test . pop ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            reducing_state  =  state . copy ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                            for  location  in  itertools . chain ( ( l  for  l  in  items_to_replace  if  l . item . player  ==  player ) , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                                            items_to_test ) : 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                                reducing_state . collect ( location . item ,  True ,  location ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            reducing_state . sweep_for_events ( locations = locations_to_test ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            if  world . has_beaten_game ( balancing_state ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                if  not  world . has_beaten_game ( reducing_state ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                    items_to_replace . append ( testing ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            else : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                reduced_sphere  =  get_sphere_locations ( reducing_state ,  locations_to_test ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                if  reachable_locations_count [ player ]  +  len ( reduced_sphere )  <  threshold : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                    items_to_replace . append ( testing ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    replaced_items  =  False 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    # sort then shuffle to maintain deterministic behaviour, 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    # while allowing use of set for better algorithm growth behaviour elsewhere 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    replacement_locations  =  sorted ( l  for  l  in  checked_locations  if  not  l . event  and  not  l . locked ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    world . random . shuffle ( replacement_locations ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    items_to_replace . sort ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    world . random . shuffle ( items_to_replace ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    while  replacement_locations  and  items_to_replace : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        old_location  =  items_to_replace . pop ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        for  new_location  in  replacement_locations : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            if  new_location . can_fill ( state ,  old_location . item ,  False )  and  \
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                    old_location . can_fill ( state ,  new_location . item ,  False ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                replacement_locations . remove ( new_location ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                swap_location_item ( old_location ,  new_location ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                logging . debug ( f " Progression balancing moved  { new_location . item }  to  { new_location } ,  " 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                              f " displacing  { old_location . item }  into  { old_location } " ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                state . collect ( new_location . item ,  True ,  new_location ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                replaced_items  =  True 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                break 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        else : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            logging . warning ( f " Could not Progression Balance  { old_location . item } " ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    if  replaced_items : 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                        unlocked  =  { fresh  for  player  in  balancing_players  for  fresh  in  unlocked_locations [ player ] } 
							 
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                        for  location  in  get_sphere_locations ( state ,  unlocked ) : 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                            unchecked_locations . remove ( location ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            reachable_locations_count [ location . player ]  + =  1 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                            sphere_locations . add ( location ) 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            for  location  in  sphere_locations : 
							 
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                if  location . event : 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    state . collect ( location . item ,  True ,  location ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-03-04 08:10:30 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            checked_locations  | =  sphere_locations 
							 
						 
					
						
							
								
									
										
										
										
											2020-05-18 03:54:29 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            if  world . has_beaten_game ( state ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                break 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            elif  not  sphere_locations : 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-24 01:42:00 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                logging . warning ( " Progression Balancing ran out of paths. " ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                break 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-04 15:14:20 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-01-11 13:35:48 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								def  swap_location_item ( location_1 :  Location ,  location_2 :  Location ,  check_locked = True ) :  
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    """ Swaps Items of locations. Does NOT swap flags like shop_slot or locked, but does swap event """ 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-11 13:35:48 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    if  check_locked : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  location_1 . locked : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            logging . warning ( f " Swapping  { location_1 } , which is marked as locked. " ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        if  location_2 . locked : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            logging . warning ( f " Swapping  { location_2 } , which is marked as locked. " ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    location_2 . item ,  location_1 . item  =  location_1 . item ,  location_2 . item 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    location_1 . item . location  =  location_1 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    location_2 . item . location  =  location_2 
							 
						 
					
						
							
								
									
										
										
										
											2021-02-05 08:07:12 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    location_1 . event ,  location_2 . event  =  location_2 . event ,  location_1 . event 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-11 13:35:48 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 07:57:34 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								def  distribute_planned ( world :  MultiWorld ) :  
						 
					
						
							
								
									
										
										
										
											2021-09-02 03:45:37 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    # TODO: remove. Preferably by implementing key drop 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    from  worlds . alttp . Regions  import  key_drop_data 
							 
						 
					
						
							
								
									
										
										
										
											2021-02-20 02:30:55 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								    world_name_lookup  =  world . world_name_lookup 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-04 15:14:20 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								    for  player  in  world . player_ids : 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-14 23:42:13 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								        try : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            placement :  PlandoItem 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								            for  placement  in  world . plando_items [ player ] : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  placement . location  in  key_drop_data : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    placement . warn ( 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        f " Can ' t place  ' { placement . item } '  at  ' { placement . location } ' , as key drop shuffle locations are not supported yet. " ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-05 09:53:52 -08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    continue 
							 
						 
					
						
							
								
									
										
										
										
											2021-09-02 03:45:37 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                item  =  world . worlds [ player ] . create_item ( placement . item ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-14 23:42:13 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                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 : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        placement . failed ( f " Could not find a world with an unfilled location  { placement . location } " , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                         FillError ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        continue 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    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 : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        placement . failed ( f " Could not find a world with an unfilled location  { placement . location } " , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                         FillError ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        continue 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    target_world  =  world . random . choice ( unfilled ) . player 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                elif  type ( target_world )  ==  int :   # target world by player id 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    if  target_world  not  in  range ( 1 ,  world . players  +  1 ) : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        placement . failed ( 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            f " Cannot place item in world  { target_world }  as it is not in range of (1,  { world . players } ) " , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                            ValueError ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        continue 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                else :   # find world by name 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    if  target_world  not  in  world_name_lookup : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        placement . failed ( f " Cannot place item to  { target_world } ' s world as that world does not exist. " , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                                         ValueError ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        continue 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    target_world  =  world_name_lookup [ target_world ] 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                location  =  world . get_location ( placement . location ,  target_world ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  location . item : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    placement . failed ( f " Cannot place item into already filled location  { location } . " ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-05 09:53:52 -08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    continue 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-04 15:14:20 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-06-14 23:42:13 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                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 : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    placement . failed ( f " Can ' t place  { item }  at  { location }  due to fill condition not met. " ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-01-05 09:53:52 -08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								                    continue 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-14 23:42:13 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                if  placement . from_pool :   # Should happen AFTER the item is placed, in case it was allowed to skip failed placement. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    try : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        world . itempool . remove ( item ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                    except  ValueError : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								                        placement . warn ( f " Could not remove  { item }  from pool as it ' s already missing from it. " ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								        except  Exception  as  e : 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-09 09:15:41 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								            raise  Exception ( f " Error running plando for player  { player }  ( { world . player_name [ player ] } ) " )  from  e