update basic and normal boss shuffling with a less biased algorithm
This commit is contained in:
parent
4985daefee
commit
1f5bcb6273
|
@ -1059,6 +1059,9 @@ class Boss():
|
||||||
def can_defeat(self, state) -> bool:
|
def can_defeat(self, state) -> bool:
|
||||||
return self.defeat_rule(state, self.player)
|
return self.defeat_rule(state, self.player)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"Boss({self.name})"
|
||||||
|
|
||||||
class Location():
|
class Location():
|
||||||
shop_slot: bool = False
|
shop_slot: bool = False
|
||||||
shop_slot_disabled: bool = False
|
shop_slot_disabled: bool = False
|
||||||
|
|
|
@ -148,19 +148,19 @@ boss_table = {
|
||||||
}
|
}
|
||||||
|
|
||||||
boss_location_table = [
|
boss_location_table = [
|
||||||
['Ganons Tower', 'top'],
|
('Ganons Tower', 'top'),
|
||||||
['Tower of Hera', None],
|
('Tower of Hera', None),
|
||||||
['Skull Woods', None],
|
('Skull Woods', None),
|
||||||
['Ganons Tower', 'middle'],
|
('Ganons Tower', 'middle'),
|
||||||
['Eastern Palace', None],
|
('Eastern Palace', None),
|
||||||
['Desert Palace', None],
|
('Desert Palace', None),
|
||||||
['Palace of Darkness', None],
|
('Palace of Darkness', None),
|
||||||
['Swamp Palace', None],
|
('Swamp Palace', None),
|
||||||
['Thieves Town', None],
|
('Thieves Town', None),
|
||||||
['Ice Palace', None],
|
('Ice Palace', None),
|
||||||
['Misery Mire', None],
|
('Misery Mire', None),
|
||||||
['Turtle Rock', None],
|
('Turtle Rock', None),
|
||||||
['Ganons Tower', 'bottom'],
|
('Ganons Tower', 'bottom'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -187,6 +187,10 @@ def can_place_boss(boss: str, dungeon_name: str, level: Optional[str] = None) ->
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
restrictive_boss_locations = {}
|
||||||
|
for location in boss_location_table:
|
||||||
|
restrictive_boss_locations[location] = not all(can_place_boss(boss, *location)
|
||||||
|
for boss in boss_table if not boss.startswith("Agahnim"))
|
||||||
|
|
||||||
def place_boss(world, player: int, boss: str, location: str, level: Optional[str]):
|
def place_boss(world, player: int, boss: str, location: str, level: Optional[str]):
|
||||||
if location == 'Ganons Tower' and world.mode[player] == 'inverted':
|
if location == 'Ganons Tower' and world.mode[player] == 'inverted':
|
||||||
|
@ -194,12 +198,16 @@ def place_boss(world, player: int, boss: str, location: str, level: Optional[str
|
||||||
logging.debug('Placing boss %s at %s', boss, location + (' (' + level + ')' if level else ''))
|
logging.debug('Placing boss %s at %s', boss, location + (' (' + level + ')' if level else ''))
|
||||||
world.get_dungeon(location, player).bosses[level] = BossFactory(boss, player)
|
world.get_dungeon(location, player).bosses[level] = BossFactory(boss, player)
|
||||||
|
|
||||||
|
def format_boss_location(location, level):
|
||||||
|
return location + (' (' + level + ')' if level else '')
|
||||||
|
|
||||||
def place_bosses(world, player: int):
|
def place_bosses(world, player: int):
|
||||||
if world.boss_shuffle[player] == 'none':
|
if world.boss_shuffle[player] == 'none':
|
||||||
return
|
return
|
||||||
# Most to least restrictive order
|
# Most to least restrictive order
|
||||||
boss_locations = boss_location_table.copy()
|
boss_locations = boss_location_table.copy()
|
||||||
|
world.random.shuffle(boss_locations)
|
||||||
|
boss_locations.sort(key= lambda location: -int(restrictive_boss_locations[location]))
|
||||||
|
|
||||||
all_bosses = sorted(boss_table.keys()) # sorted to be deterministic on older pythons
|
all_bosses = sorted(boss_table.keys()) # sorted to be deterministic on older pythons
|
||||||
placeable_bosses = [boss for boss in all_bosses if boss not in ['Agahnim', 'Agahnim2', 'Ganon']]
|
placeable_bosses = [boss for boss in all_bosses if boss not in ['Agahnim', 'Agahnim2', 'Ganon']]
|
||||||
|
@ -225,7 +233,7 @@ def place_bosses(world, player: int):
|
||||||
already_placed_bosses.append(boss)
|
already_placed_bosses.append(boss)
|
||||||
boss_locations.remove([loc, level])
|
boss_locations.remove([loc, level])
|
||||||
else:
|
else:
|
||||||
raise Exception("Cannot place", boss, "at", loc, level, "for player", player)
|
raise Exception(f"Cannot place {boss} at {format_boss_location(loc, level)} for player {player}.")
|
||||||
else:
|
else:
|
||||||
boss = boss.title()
|
boss = boss.title()
|
||||||
boss_locations, already_placed_bosses = place_where_possible(world, player, boss, boss_locations)
|
boss_locations, already_placed_bosses = place_where_possible(world, player, boss, boss_locations)
|
||||||
|
@ -237,7 +245,7 @@ def place_bosses(world, player: int):
|
||||||
if world.boss_shuffle[player] == "basic": # vanilla bosses shuffled
|
if world.boss_shuffle[player] == "basic": # vanilla bosses shuffled
|
||||||
bosses = placeable_bosses + ['Armos Knights', 'Lanmolas', 'Moldorm']
|
bosses = placeable_bosses + ['Armos Knights', 'Lanmolas', 'Moldorm']
|
||||||
else: # all bosses present, the three duplicates chosen at random
|
else: # all bosses present, the three duplicates chosen at random
|
||||||
bosses = all_bosses + [world.random.choice(placeable_bosses) for _ in range(3)]
|
bosses = placeable_bosses + world.random.sample(placeable_bosses, 3)
|
||||||
|
|
||||||
# there is probably a better way to do this
|
# there is probably a better way to do this
|
||||||
while already_placed_bosses:
|
while already_placed_bosses:
|
||||||
|
@ -251,11 +259,17 @@ def place_bosses(world, player: int):
|
||||||
|
|
||||||
world.random.shuffle(bosses)
|
world.random.shuffle(bosses)
|
||||||
for loc, level in boss_locations:
|
for loc, level in boss_locations:
|
||||||
boss = next((b for b in bosses if can_place_boss(b, loc, level)), None)
|
for _ in range(len(bosses)):
|
||||||
if not boss:
|
boss = bosses.pop()
|
||||||
loc_text = loc + (' (' + level + ')' if level else '')
|
if can_place_boss(boss, loc, level):
|
||||||
raise FillError('Could not place boss for location %s' % loc_text)
|
break
|
||||||
bosses.remove(boss)
|
# put the boss back in queue
|
||||||
|
bosses.insert(0, boss) # this would be faster with deque,
|
||||||
|
# but the deque size is small enough that it should not matter
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise FillError(f'Could not place boss for location {format_boss_location(loc, level)}')
|
||||||
|
|
||||||
place_boss(world, player, boss, loc, level)
|
place_boss(world, player, boss, loc, level)
|
||||||
|
|
||||||
elif shuffle_mode == "chaos": # all bosses chosen at random
|
elif shuffle_mode == "chaos": # all bosses chosen at random
|
||||||
|
@ -264,8 +278,7 @@ def place_bosses(world, player: int):
|
||||||
boss = world.random.choice(
|
boss = world.random.choice(
|
||||||
[b for b in placeable_bosses if can_place_boss(b, loc, level)])
|
[b for b in placeable_bosses if can_place_boss(b, loc, level)])
|
||||||
except IndexError:
|
except IndexError:
|
||||||
loc_text = loc + (' (' + level + ')' if level else '')
|
raise FillError(f'Could not place boss for location {format_boss_location(loc, level)}')
|
||||||
raise FillError('Could not place boss for location %s' % loc_text)
|
|
||||||
else:
|
else:
|
||||||
place_boss(world, player, boss, loc, level)
|
place_boss(world, player, boss, loc, level)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue