@@ -15,6 +15,10 @@ class FillError(RuntimeError):
15
15
pass
16
16
17
17
18
+ def _log_fill_progress (name : str , placed : int , total_items : int ) -> None :
19
+ logging .info (f"Current fill step ({ name } ) at { placed } /{ total_items } items placed." )
20
+
21
+
18
22
def sweep_from_pool (base_state : CollectionState , itempool : typing .Sequence [Item ] = tuple ()) -> CollectionState :
19
23
new_state = base_state .copy ()
20
24
for item in itempool :
@@ -26,7 +30,7 @@ def sweep_from_pool(base_state: CollectionState, itempool: typing.Sequence[Item]
26
30
def fill_restrictive (world : MultiWorld , base_state : CollectionState , locations : typing .List [Location ],
27
31
item_pool : typing .List [Item ], single_player_placement : bool = False , lock : bool = False ,
28
32
swap : bool = True , on_place : typing .Optional [typing .Callable [[Location ], None ]] = None ,
29
- allow_partial : bool = False , allow_excluded : bool = False ) -> None :
33
+ allow_partial : bool = False , allow_excluded : bool = False , name : str = "Unknown" ) -> None :
30
34
"""
31
35
:param world: Multiworld to be filled.
32
36
:param base_state: State assumed before fill.
@@ -38,16 +42,20 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations:
38
42
:param on_place: callback that is called when a placement happens
39
43
:param allow_partial: only place what is possible. Remaining items will be in the item_pool list.
40
44
:param allow_excluded: if true and placement fails, it is re-attempted while ignoring excluded on Locations
45
+ :param name: name of this fill step for progress logging purposes
41
46
"""
42
47
unplaced_items : typing .List [Item ] = []
43
48
placements : typing .List [Location ] = []
44
49
cleanup_required = False
45
-
46
50
swapped_items : typing .Counter [typing .Tuple [int , str , bool ]] = Counter ()
47
51
reachable_items : typing .Dict [int , typing .Deque [Item ]] = {}
48
52
for item in item_pool :
49
53
reachable_items .setdefault (item .player , deque ()).append (item )
50
54
55
+ # for progress logging
56
+ total = min (len (item_pool ), len (locations ))
57
+ placed = 0
58
+
51
59
while any (reachable_items .values ()) and locations :
52
60
# grab one item per player
53
61
items_to_place = [items .pop ()
@@ -152,9 +160,15 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations:
152
160
spot_to_fill .locked = lock
153
161
placements .append (spot_to_fill )
154
162
spot_to_fill .event = item_to_place .advancement
163
+ placed += 1
164
+ if not placed % 1000 :
165
+ _log_fill_progress (name , placed , total )
155
166
if on_place :
156
167
on_place (spot_to_fill )
157
168
169
+ if total > 1000 :
170
+ _log_fill_progress (name , placed , total )
171
+
158
172
if cleanup_required :
159
173
# validate all placements and remove invalid ones
160
174
state = sweep_from_pool (base_state , [])
@@ -198,6 +212,8 @@ def remaining_fill(world: MultiWorld,
198
212
unplaced_items : typing .List [Item ] = []
199
213
placements : typing .List [Location ] = []
200
214
swapped_items : typing .Counter [typing .Tuple [int , str ]] = Counter ()
215
+ total = min (len (itempool ), len (locations ))
216
+ placed = 0
201
217
while locations and itempool :
202
218
item_to_place = itempool .pop ()
203
219
spot_to_fill : typing .Optional [Location ] = None
@@ -247,6 +263,12 @@ def remaining_fill(world: MultiWorld,
247
263
248
264
world .push_item (spot_to_fill , item_to_place , False )
249
265
placements .append (spot_to_fill )
266
+ placed += 1
267
+ if not placed % 1000 :
268
+ _log_fill_progress ("Remaining" , placed , total )
269
+
270
+ if total > 1000 :
271
+ _log_fill_progress ("Remaining" , placed , total )
250
272
251
273
if unplaced_items and locations :
252
274
# There are leftover unplaceable items and locations that won't accept them
@@ -282,7 +304,7 @@ def accessibility_corrections(world: MultiWorld, state: CollectionState, locatio
282
304
locations .append (location )
283
305
if pool and locations :
284
306
locations .sort (key = lambda loc : loc .progress_type != LocationProgressType .PRIORITY )
285
- fill_restrictive (world , state , locations , pool )
307
+ fill_restrictive (world , state , locations , pool , name = "Accessibility Corrections" )
286
308
287
309
288
310
def inaccessible_location_rules (world : MultiWorld , state : CollectionState , locations ):
@@ -352,23 +374,25 @@ def distribute_early_items(world: MultiWorld,
352
374
player_local = early_local_rest_items [player ]
353
375
fill_restrictive (world , base_state ,
354
376
[loc for loc in early_locations if loc .player == player ],
355
- player_local , lock = True , allow_partial = True )
377
+ player_local , lock = True , allow_partial = True , name = f"Local Early Items P { player } " )
356
378
if player_local :
357
379
logging .warning (f"Could not fulfill rules of early items: { player_local } " )
358
380
early_rest_items .extend (early_local_rest_items [player ])
359
381
early_locations = [loc for loc in early_locations if not loc .item ]
360
- fill_restrictive (world , base_state , early_locations , early_rest_items , lock = True , allow_partial = True )
382
+ fill_restrictive (world , base_state , early_locations , early_rest_items , lock = True , allow_partial = True ,
383
+ name = "Early Items" )
361
384
early_locations += early_priority_locations
362
385
for player in world .player_ids :
363
386
player_local = early_local_prog_items [player ]
364
387
fill_restrictive (world , base_state ,
365
388
[loc for loc in early_locations if loc .player == player ],
366
- player_local , lock = True , allow_partial = True )
389
+ player_local , lock = True , allow_partial = True , name = f"Local Early Progression P { player } " )
367
390
if player_local :
368
391
logging .warning (f"Could not fulfill rules of early items: { player_local } " )
369
392
early_prog_items .extend (player_local )
370
393
early_locations = [loc for loc in early_locations if not loc .item ]
371
- fill_restrictive (world , base_state , early_locations , early_prog_items , lock = True , allow_partial = True )
394
+ fill_restrictive (world , base_state , early_locations , early_prog_items , lock = True , allow_partial = True ,
395
+ name = "Early Progression" )
372
396
unplaced_early_items = early_rest_items + early_prog_items
373
397
if unplaced_early_items :
374
398
logging .warning ("Ran out of early locations for early items. Failed to place "
@@ -422,13 +446,14 @@ def mark_for_locking(location: Location):
422
446
423
447
if prioritylocations :
424
448
# "priority fill"
425
- fill_restrictive (world , world .state , prioritylocations , progitempool , swap = False , on_place = mark_for_locking )
449
+ fill_restrictive (world , world .state , prioritylocations , progitempool , swap = False , on_place = mark_for_locking ,
450
+ name = "Priority" )
426
451
accessibility_corrections (world , world .state , prioritylocations , progitempool )
427
452
defaultlocations = prioritylocations + defaultlocations
428
453
429
454
if progitempool :
430
- # "progression fill"
431
- fill_restrictive (world , world .state , defaultlocations , progitempool )
455
+ # "advancement/ progression fill"
456
+ fill_restrictive (world , world .state , defaultlocations , progitempool , name = "Progression" )
432
457
if progitempool :
433
458
raise FillError (
434
459
f'Not enough locations for progress items. There are { len (progitempool )} more items than locations' )
0 commit comments