Super Metroid: Replace random module with world random in variaRandomizer (#4429)

This commit is contained in:
Mysteryem
2025-04-01 17:14:47 +01:00
committed by GitHub
parent 5a6b02dbd3
commit ca08e4b950
16 changed files with 154 additions and 142 deletions

View File

@@ -124,7 +124,7 @@ class SMWorld(World):
Logic.factory('vanilla') Logic.factory('vanilla')
dummy_rom_file = Utils.user_path(SMSettings.RomFile.copy_to) # actual rom set in generate_output dummy_rom_file = Utils.user_path(SMSettings.RomFile.copy_to) # actual rom set in generate_output
self.variaRando = VariaRandomizer(self.options, dummy_rom_file, self.player) self.variaRando = VariaRandomizer(self.options, dummy_rom_file, self.player, self.multiworld.seed, self.random)
self.multiworld.state.smbm[self.player] = SMBoolManager(self.player, self.variaRando.maxDifficulty) self.multiworld.state.smbm[self.player] = SMBoolManager(self.player, self.variaRando.maxDifficulty)
# keeps Nothing items local so no player will ever pickup Nothing # keeps Nothing items local so no player will ever pickup Nothing
@@ -314,11 +314,11 @@ class SMWorld(World):
raise KeyError(f"Item {name} for {self.player_name} is invalid.") raise KeyError(f"Item {name} for {self.player_name} is invalid.")
def get_filler_item_name(self) -> str: def get_filler_item_name(self) -> str:
if self.multiworld.random.randint(0, 100) < self.options.minor_qty.value: if self.random.randint(0, 100) < self.options.minor_qty.value:
power_bombs = self.options.power_bomb_qty.value power_bombs = self.options.power_bomb_qty.value
missiles = self.options.missile_qty.value missiles = self.options.missile_qty.value
super_missiles = self.options.super_qty.value super_missiles = self.options.super_qty.value
roll = self.multiworld.random.randint(1, power_bombs + missiles + super_missiles) roll = self.random.randint(1, power_bombs + missiles + super_missiles)
if roll <= power_bombs: if roll <= power_bombs:
return "Power Bomb" return "Power Bomb"
elif roll <= power_bombs + missiles: elif roll <= power_bombs + missiles:
@@ -340,8 +340,8 @@ class SMWorld(World):
else: else:
nonChozoLoc.append(loc) nonChozoLoc.append(loc)
self.multiworld.random.shuffle(nonChozoLoc) self.random.shuffle(nonChozoLoc)
self.multiworld.random.shuffle(chozoLoc) self.random.shuffle(chozoLoc)
missingCount = len(self.NothingPool) - len(nonChozoLoc) missingCount = len(self.NothingPool) - len(nonChozoLoc)
locations = nonChozoLoc locations = nonChozoLoc
if (missingCount > 0): if (missingCount > 0):

View File

@@ -1,5 +1,4 @@
import copy import copy
import random
from ..logic.logic import Logic from ..logic.logic import Logic
from ..utils.parameters import Knows from ..utils.parameters import Knows
from ..graph.location import locationsDict from ..graph.location import locationsDict
@@ -136,7 +135,8 @@ class GraphUtils:
refused[apName] = cause refused[apName] = cause
return ret, refused return ret, refused
def updateLocClassesStart(startGraphArea, split, possibleMajLocs, preserveMajLocs, nLocs): @staticmethod
def updateLocClassesStart(startGraphArea, split, possibleMajLocs, preserveMajLocs, nLocs, random):
locs = locationsDict locs = locationsDict
preserveMajLocs = [locs[locName] for locName in preserveMajLocs if locs[locName].isClass(split)] preserveMajLocs = [locs[locName] for locName in preserveMajLocs if locs[locName].isClass(split)]
possLocs = [locs[locName] for locName in possibleMajLocs][:nLocs] possLocs = [locs[locName] for locName in possibleMajLocs][:nLocs]
@@ -160,7 +160,8 @@ class GraphUtils:
ap = getAccessPoint(startApName) ap = getAccessPoint(startApName)
return ap.Start['patches'] if 'patches' in ap.Start else [] return ap.Start['patches'] if 'patches' in ap.Start else []
def createBossesTransitions(): @staticmethod
def createBossesTransitions(random):
transitions = vanillaBossesTransitions transitions = vanillaBossesTransitions
def isVanilla(): def isVanilla():
for t in vanillaBossesTransitions: for t in vanillaBossesTransitions:
@@ -180,13 +181,15 @@ class GraphUtils:
transitions.append((src,dst)) transitions.append((src,dst))
return transitions return transitions
def createAreaTransitions(lightAreaRando=False): @staticmethod
def createAreaTransitions(lightAreaRando=False, *, random):
if lightAreaRando: if lightAreaRando:
return GraphUtils.createLightAreaTransitions() return GraphUtils.createLightAreaTransitions(random=random)
else: else:
return GraphUtils.createRegularAreaTransitions() return GraphUtils.createRegularAreaTransitions(random=random)
def createRegularAreaTransitions(apList=None, apPred=None): @staticmethod
def createRegularAreaTransitions(apList=None, apPred=None, *, random):
if apList is None: if apList is None:
apList = Logic.accessPoints apList = Logic.accessPoints
if apPred is None: if apPred is None:
@@ -239,7 +242,8 @@ class GraphUtils:
transitions.append((ap.Name, ap.Name)) transitions.append((ap.Name, ap.Name))
# crateria can be forced in corner cases # crateria can be forced in corner cases
def createMinimizerTransitions(startApName, locLimit, forcedAreas=None): @staticmethod
def createMinimizerTransitions(startApName, locLimit, forcedAreas=None, *, random):
if forcedAreas is None: if forcedAreas is None:
forcedAreas = [] forcedAreas = []
if startApName == 'Ceres': if startApName == 'Ceres':
@@ -316,7 +320,8 @@ class GraphUtils:
GraphUtils.log.debug("FINAL MINIMIZER areas: "+str(areas)) GraphUtils.log.debug("FINAL MINIMIZER areas: "+str(areas))
return transitions return transitions
def createLightAreaTransitions(): @staticmethod
def createLightAreaTransitions(random):
# group APs by area # group APs by area
aps = {} aps = {}
totalCount = 0 totalCount = 0
@@ -407,7 +412,8 @@ class GraphUtils:
return rooms return rooms
def escapeAnimalsTransitions(graph, possibleTargets, firstEscape): @staticmethod
def escapeAnimalsTransitions(graph, possibleTargets, firstEscape, random):
n = len(possibleTargets) n = len(possibleTargets)
assert (n < 4 and firstEscape is not None) or (n <= 4 and firstEscape is None), "Invalid possibleTargets list: " + str(possibleTargets) assert (n < 4 and firstEscape is not None) or (n <= 4 and firstEscape is None), "Invalid possibleTargets list: " + str(possibleTargets)
GraphUtils.log.debug("escapeAnimalsTransitions. possibleTargets="+str(possibleTargets)+", firstEscape="+str(firstEscape)) GraphUtils.log.debug("escapeAnimalsTransitions. possibleTargets="+str(possibleTargets)+", firstEscape="+str(firstEscape))

View File

@@ -1,4 +1,3 @@
import random
from ..utils import log from ..utils import log
from ..utils.utils import getRangeDict, chooseFromRange from ..utils.utils import getRangeDict, chooseFromRange
from ..rando.ItemLocContainer import ItemLocation from ..rando.ItemLocContainer import ItemLocation
@@ -23,8 +22,9 @@ class Choice(object):
# simple random choice, that chooses an item first, then a locatio to put it in # simple random choice, that chooses an item first, then a locatio to put it in
class ItemThenLocChoice(Choice): class ItemThenLocChoice(Choice):
def __init__(self, restrictions): def __init__(self, restrictions, random):
super(ItemThenLocChoice, self).__init__(restrictions) super(ItemThenLocChoice, self).__init__(restrictions)
self.random = random
def chooseItemLoc(self, itemLocDict, isProg): def chooseItemLoc(self, itemLocDict, isProg):
itemList = self.getItemList(itemLocDict) itemList = self.getItemList(itemLocDict)
@@ -49,7 +49,7 @@ class ItemThenLocChoice(Choice):
return self.chooseItemRandom(itemList) return self.chooseItemRandom(itemList)
def chooseItemRandom(self, itemList): def chooseItemRandom(self, itemList):
return random.choice(itemList) return self.random.choice(itemList)
def chooseLocation(self, locList, item, isProg): def chooseLocation(self, locList, item, isProg):
if len(locList) == 0: if len(locList) == 0:
@@ -63,12 +63,12 @@ class ItemThenLocChoice(Choice):
return self.chooseLocationRandom(locList) return self.chooseLocationRandom(locList)
def chooseLocationRandom(self, locList): def chooseLocationRandom(self, locList):
return random.choice(locList) return self.random.choice(locList)
# Choice specialization for prog speed based filler # Choice specialization for prog speed based filler
class ItemThenLocChoiceProgSpeed(ItemThenLocChoice): class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
def __init__(self, restrictions, progSpeedParams, distanceProp, services): def __init__(self, restrictions, progSpeedParams, distanceProp, services, random):
super(ItemThenLocChoiceProgSpeed, self).__init__(restrictions) super(ItemThenLocChoiceProgSpeed, self).__init__(restrictions, random)
self.progSpeedParams = progSpeedParams self.progSpeedParams = progSpeedParams
self.distanceProp = distanceProp self.distanceProp = distanceProp
self.services = services self.services = services
@@ -104,7 +104,7 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
if self.restrictions.isLateMorph() and canRollback and len(itemLocDict) == 1: if self.restrictions.isLateMorph() and canRollback and len(itemLocDict) == 1:
item, locList = list(itemLocDict.items())[0] item, locList = list(itemLocDict.items())[0]
if item.Type == 'Morph': if item.Type == 'Morph':
morphLocs = self.restrictions.lateMorphCheck(container, locList) morphLocs = self.restrictions.lateMorphCheck(container, locList, self.random)
if morphLocs is not None: if morphLocs is not None:
itemLocDict[item] = morphLocs itemLocDict[item] = morphLocs
else: else:
@@ -115,7 +115,7 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
assert len(locs) == 1 and locs[0].Name == item.Name assert len(locs) == 1 and locs[0].Name == item.Name
return ItemLocation(item, locs[0]) return ItemLocation(item, locs[0])
# late doors check for random door colors # late doors check for random door colors
if self.restrictions.isLateDoors() and random.random() < self.lateDoorsProb: if self.restrictions.isLateDoors() and self.random.random() < self.lateDoorsProb:
self.processLateDoors(itemLocDict, ap, container) self.processLateDoors(itemLocDict, ap, container)
self.progressionItemLocs = progressionItemLocs self.progressionItemLocs = progressionItemLocs
self.ap = ap self.ap = ap
@@ -145,14 +145,14 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
def chooseLocationProg(self, locs, item): def chooseLocationProg(self, locs, item):
locs = self.getLocsSpreadProgression(locs) locs = self.getLocsSpreadProgression(locs)
random.shuffle(locs) self.random.shuffle(locs)
ret = self.getChooseFunc(self.chooseLocRanges, self.chooseLocFuncs)(locs) ret = self.getChooseFunc(self.chooseLocRanges, self.chooseLocFuncs)(locs)
self.log.debug('chooseLocationProg. ret='+ret.Name) self.log.debug('chooseLocationProg. ret='+ret.Name)
return ret return ret
# get choose function from a weighted dict # get choose function from a weighted dict
def getChooseFunc(self, rangeDict, funcDict): def getChooseFunc(self, rangeDict, funcDict):
v = chooseFromRange(rangeDict) v = chooseFromRange(rangeDict, self.random)
return funcDict[v] return funcDict[v]
@@ -209,6 +209,6 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
for i in range(len(availableLocations)): for i in range(len(availableLocations)):
loc = availableLocations[i] loc = availableLocations[i]
d = distances[i] d = distances[i]
if d == maxDist or random.random() >= self.spreadProb: if d == maxDist or self.random.random() >= self.spreadProb:
locs.append(loc) locs.append(loc)
return locs return locs

View File

@@ -1,5 +1,5 @@
import copy, time, random import copy, time
from ..utils import log from ..utils import log
from ..logic.cache import RequestCache from ..logic.cache import RequestCache
from ..rando.RandoServices import RandoServices from ..rando.RandoServices import RandoServices
@@ -15,11 +15,11 @@ from ..graph.graph_utils import GraphUtils
# item pool is not empty). # item pool is not empty).
# entry point is generateItems # entry point is generateItems
class Filler(object): class Filler(object):
def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity): def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity, *, random):
self.startAP = startAP self.startAP = startAP
self.cache = RequestCache() self.cache = RequestCache()
self.graph = graph self.graph = graph
self.services = RandoServices(graph, restrictions, self.cache) self.services = RandoServices(graph, restrictions, self.cache, random=random)
self.restrictions = restrictions self.restrictions = restrictions
self.settings = restrictions.settings self.settings = restrictions.settings
self.endDate = endDate self.endDate = endDate
@@ -108,9 +108,9 @@ class Filler(object):
# very simple front fill algorithm with no rollback and no "softlock checks" (== dessy algorithm) # very simple front fill algorithm with no rollback and no "softlock checks" (== dessy algorithm)
class FrontFiller(Filler): class FrontFiller(Filler):
def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity): def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity, *, random):
super(FrontFiller, self).__init__(startAP, graph, restrictions, emptyContainer, endDate) super(FrontFiller, self).__init__(startAP, graph, restrictions, emptyContainer, endDate, random=random)
self.choice = ItemThenLocChoice(restrictions) self.choice = ItemThenLocChoice(restrictions, random)
self.stdStart = GraphUtils.isStandardStart(self.startAP) self.stdStart = GraphUtils.isStandardStart(self.startAP)
def isEarlyGame(self): def isEarlyGame(self):

View File

@@ -1,5 +1,5 @@
import random, copy import copy
from ..utils import log from ..utils import log
from ..graph.graph_utils import GraphUtils, vanillaTransitions, vanillaBossesTransitions, escapeSource, escapeTargets, graphAreas, getAccessPoint from ..graph.graph_utils import GraphUtils, vanillaTransitions, vanillaBossesTransitions, escapeSource, escapeTargets, graphAreas, getAccessPoint
from ..logic.logic import Logic from ..logic.logic import Logic
@@ -11,13 +11,14 @@ from collections import defaultdict
# creates graph and handles randomized escape # creates graph and handles randomized escape
class GraphBuilder(object): class GraphBuilder(object):
def __init__(self, graphSettings): def __init__(self, graphSettings, random):
self.graphSettings = graphSettings self.graphSettings = graphSettings
self.areaRando = graphSettings.areaRando self.areaRando = graphSettings.areaRando
self.bossRando = graphSettings.bossRando self.bossRando = graphSettings.bossRando
self.escapeRando = graphSettings.escapeRando self.escapeRando = graphSettings.escapeRando
self.minimizerN = graphSettings.minimizerN self.minimizerN = graphSettings.minimizerN
self.log = log.get('GraphBuilder') self.log = log.get('GraphBuilder')
self.random = random
# builds everything but escape transitions # builds everything but escape transitions
def createGraph(self, maxDiff): def createGraph(self, maxDiff):
@@ -48,18 +49,18 @@ class GraphBuilder(object):
objForced = forcedAreas.intersection(escAreas) objForced = forcedAreas.intersection(escAreas)
escAreasList = sorted(list(escAreas)) escAreasList = sorted(list(escAreas))
while len(objForced) < n and len(escAreasList) > 0: while len(objForced) < n and len(escAreasList) > 0:
objForced.add(escAreasList.pop(random.randint(0, len(escAreasList)-1))) objForced.add(escAreasList.pop(self.random.randint(0, len(escAreasList)-1)))
forcedAreas = forcedAreas.union(objForced) forcedAreas = forcedAreas.union(objForced)
transitions = GraphUtils.createMinimizerTransitions(self.graphSettings.startAP, self.minimizerN, sorted(list(forcedAreas))) transitions = GraphUtils.createMinimizerTransitions(self.graphSettings.startAP, self.minimizerN, sorted(list(forcedAreas)), random=self.random)
else: else:
if not self.bossRando: if not self.bossRando:
transitions += vanillaBossesTransitions transitions += vanillaBossesTransitions
else: else:
transitions += GraphUtils.createBossesTransitions() transitions += GraphUtils.createBossesTransitions(self.random)
if not self.areaRando: if not self.areaRando:
transitions += vanillaTransitions transitions += vanillaTransitions
else: else:
transitions += GraphUtils.createAreaTransitions(self.graphSettings.lightAreaRando) transitions += GraphUtils.createAreaTransitions(self.graphSettings.lightAreaRando, random=self.random)
ret = AccessGraph(Logic.accessPoints, transitions, self.graphSettings.dotFile) ret = AccessGraph(Logic.accessPoints, transitions, self.graphSettings.dotFile)
Objectives.objDict[self.graphSettings.player].setGraph(ret, maxDiff) Objectives.objDict[self.graphSettings.player].setGraph(ret, maxDiff)
return ret return ret
@@ -100,7 +101,7 @@ class GraphBuilder(object):
self.escapeTimer(graph, paths, self.areaRando or escapeTrigger is not None) self.escapeTimer(graph, paths, self.areaRando or escapeTrigger is not None)
self.log.debug("escapeGraph: ({}, {}) timer: {}".format(escapeSource, dst, graph.EscapeAttributes['Timer'])) self.log.debug("escapeGraph: ({}, {}) timer: {}".format(escapeSource, dst, graph.EscapeAttributes['Timer']))
# animals # animals
GraphUtils.escapeAnimalsTransitions(graph, possibleTargets, dst) GraphUtils.escapeAnimalsTransitions(graph, possibleTargets, dst, self.random)
return True return True
def _getTargets(self, sm, graph, maxDiff): def _getTargets(self, sm, graph, maxDiff):
@@ -110,7 +111,7 @@ class GraphBuilder(object):
if len(possibleTargets) == 0: if len(possibleTargets) == 0:
self.log.debug("Can't randomize escape, fallback to vanilla") self.log.debug("Can't randomize escape, fallback to vanilla")
possibleTargets.append('Climb Bottom Left') possibleTargets.append('Climb Bottom Left')
random.shuffle(possibleTargets) self.random.shuffle(possibleTargets)
return possibleTargets return possibleTargets
def getPossibleEscapeTargets(self, emptyContainer, graph, maxDiff): def getPossibleEscapeTargets(self, emptyContainer, graph, maxDiff):

View File

@@ -1,6 +1,6 @@
from ..utils.utils import randGaussBounds, getRangeDict, chooseFromRange from ..utils.utils import randGaussBounds, getRangeDict, chooseFromRange
from ..utils import log from ..utils import log
import logging, copy, random import logging, copy
class Item: class Item:
__slots__ = ( 'Category', 'Class', 'Name', 'Code', 'Type', 'BeamBits', 'ItemBits', 'Id' ) __slots__ = ( 'Category', 'Class', 'Name', 'Code', 'Type', 'BeamBits', 'ItemBits', 'Id' )
@@ -335,7 +335,7 @@ class ItemManager:
itemCode = item.Code + modifier itemCode = item.Code + modifier
return itemCode return itemCode
def __init__(self, majorsSplit, qty, sm, nLocs, bossesItems, maxDiff): def __init__(self, majorsSplit, qty, sm, nLocs, bossesItems, maxDiff, random):
self.qty = qty self.qty = qty
self.sm = sm self.sm = sm
self.majorsSplit = majorsSplit self.majorsSplit = majorsSplit
@@ -344,6 +344,7 @@ class ItemManager:
self.maxDiff = maxDiff self.maxDiff = maxDiff
self.majorClass = 'Chozo' if majorsSplit == 'Chozo' else 'Major' self.majorClass = 'Chozo' if majorsSplit == 'Chozo' else 'Major'
self.itemPool = [] self.itemPool = []
self.random = random
def newItemPool(self, addBosses=True): def newItemPool(self, addBosses=True):
self.itemPool = [] self.itemPool = []
@@ -386,7 +387,7 @@ class ItemManager:
return ItemManager.Items[itemType].withClass(itemClass) return ItemManager.Items[itemType].withClass(itemClass)
def createItemPool(self, exclude=None): def createItemPool(self, exclude=None):
itemPoolGenerator = ItemPoolGenerator.factory(self.majorsSplit, self, self.qty, self.sm, exclude, self.nLocs, self.maxDiff) itemPoolGenerator = ItemPoolGenerator.factory(self.majorsSplit, self, self.qty, self.sm, exclude, self.nLocs, self.maxDiff, self.random)
self.itemPool = itemPoolGenerator.getItemPool() self.itemPool = itemPoolGenerator.getItemPool()
@staticmethod @staticmethod
@@ -402,20 +403,20 @@ class ItemPoolGenerator(object):
nbBosses = 9 nbBosses = 9
@staticmethod @staticmethod
def factory(majorsSplit, itemManager, qty, sm, exclude, nLocs, maxDiff): def factory(majorsSplit, itemManager, qty, sm, exclude, nLocs, maxDiff, random):
if majorsSplit == 'Chozo': if majorsSplit == 'Chozo':
return ItemPoolGeneratorChozo(itemManager, qty, sm, maxDiff) return ItemPoolGeneratorChozo(itemManager, qty, sm, maxDiff, random)
elif majorsSplit == 'Plando': elif majorsSplit == 'Plando':
return ItemPoolGeneratorPlando(itemManager, qty, sm, exclude, nLocs, maxDiff) return ItemPoolGeneratorPlando(itemManager, qty, sm, exclude, nLocs, maxDiff, random)
elif nLocs == ItemPoolGenerator.maxLocs: elif nLocs == ItemPoolGenerator.maxLocs:
if majorsSplit == "Scavenger": if majorsSplit == "Scavenger":
return ItemPoolGeneratorScavenger(itemManager, qty, sm, maxDiff) return ItemPoolGeneratorScavenger(itemManager, qty, sm, maxDiff, random)
else: else:
return ItemPoolGeneratorMajors(itemManager, qty, sm, maxDiff) return ItemPoolGeneratorMajors(itemManager, qty, sm, maxDiff, random)
else: else:
return ItemPoolGeneratorMinimizer(itemManager, qty, sm, nLocs, maxDiff) return ItemPoolGeneratorMinimizer(itemManager, qty, sm, nLocs, maxDiff, random)
def __init__(self, itemManager, qty, sm, maxDiff): def __init__(self, itemManager, qty, sm, maxDiff, random):
self.itemManager = itemManager self.itemManager = itemManager
self.qty = qty self.qty = qty
self.sm = sm self.sm = sm
@@ -423,12 +424,13 @@ class ItemPoolGenerator(object):
self.maxEnergy = 18 # 14E, 4R self.maxEnergy = 18 # 14E, 4R
self.maxDiff = maxDiff self.maxDiff = maxDiff
self.log = log.get('ItemPool') self.log = log.get('ItemPool')
self.random = random
def isUltraSparseNoTanks(self): def isUltraSparseNoTanks(self):
# if low stuff botwoon is not known there is a hard energy req of one tank, even # if low stuff botwoon is not known there is a hard energy req of one tank, even
# with both suits # with both suits
lowStuffBotwoon = self.sm.knowsLowStuffBotwoon() lowStuffBotwoon = self.sm.knowsLowStuffBotwoon()
return random.random() < 0.5 and (lowStuffBotwoon.bool == True and lowStuffBotwoon.difficulty <= self.maxDiff) return self.random.random() < 0.5 and (lowStuffBotwoon.bool == True and lowStuffBotwoon.difficulty <= self.maxDiff)
def calcMaxMinors(self): def calcMaxMinors(self):
pool = self.itemManager.getItemPool() pool = self.itemManager.getItemPool()
@@ -464,7 +466,7 @@ class ItemPoolGenerator(object):
rangeDict = getRangeDict(ammoQty) rangeDict = getRangeDict(ammoQty)
self.log.debug("rangeDict: {}".format(rangeDict)) self.log.debug("rangeDict: {}".format(rangeDict))
while len(self.itemManager.getItemPool()) < maxItems: while len(self.itemManager.getItemPool()) < maxItems:
item = chooseFromRange(rangeDict) item = chooseFromRange(rangeDict, self.random)
self.itemManager.addMinor(item) self.itemManager.addMinor(item)
else: else:
minorsTypes = ['Missile', 'Super', 'PowerBomb'] minorsTypes = ['Missile', 'Super', 'PowerBomb']
@@ -522,7 +524,7 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
# no etank nor reserve # no etank nor reserve
self.itemManager.removeItem('ETank') self.itemManager.removeItem('ETank')
self.itemManager.addItem('NoEnergy', 'Chozo') self.itemManager.addItem('NoEnergy', 'Chozo')
elif random.random() < 0.5: elif self.random.random() < 0.5:
# replace only etank with reserve # replace only etank with reserve
self.itemManager.removeItem('ETank') self.itemManager.removeItem('ETank')
self.itemManager.addItem('Reserve', 'Chozo') self.itemManager.addItem('Reserve', 'Chozo')
@@ -535,9 +537,9 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
# 4-6 # 4-6
# already 3E and 1R # already 3E and 1R
alreadyInPool = 4 alreadyInPool = 4
rest = randGaussBounds(2, 5) rest = randGaussBounds(self.random, 2, 5)
if rest >= 1: if rest >= 1:
if random.random() < 0.5: if self.random.random() < 0.5:
self.itemManager.addItem('Reserve', 'Minor') self.itemManager.addItem('Reserve', 'Minor')
else: else:
self.itemManager.addItem('ETank', 'Minor') self.itemManager.addItem('ETank', 'Minor')
@@ -550,13 +552,13 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
# 8-12 # 8-12
# add up to 3 Reserves or ETanks (cannot add more than 3 reserves) # add up to 3 Reserves or ETanks (cannot add more than 3 reserves)
for i in range(3): for i in range(3):
if random.random() < 0.5: if self.random.random() < 0.5:
self.itemManager.addItem('Reserve', 'Minor') self.itemManager.addItem('Reserve', 'Minor')
else: else:
self.itemManager.addItem('ETank', 'Minor') self.itemManager.addItem('ETank', 'Minor')
# 7 already in the pool (3 E, 1 R, + the previous 3) # 7 already in the pool (3 E, 1 R, + the previous 3)
alreadyInPool = 7 alreadyInPool = 7
rest = 1 + randGaussBounds(4, 3.7) rest = 1 + randGaussBounds(self.random, 4, 3.7)
for i in range(rest): for i in range(rest):
self.itemManager.addItem('ETank', 'Minor') self.itemManager.addItem('ETank', 'Minor')
# fill the rest with NoEnergy # fill the rest with NoEnergy
@@ -581,10 +583,10 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
return self.itemManager.getItemPool() return self.itemManager.getItemPool()
class ItemPoolGeneratorMajors(ItemPoolGenerator): class ItemPoolGeneratorMajors(ItemPoolGenerator):
def __init__(self, itemManager, qty, sm, maxDiff): def __init__(self, itemManager, qty, sm, maxDiff, random):
super(ItemPoolGeneratorMajors, self).__init__(itemManager, qty, sm, maxDiff) super(ItemPoolGeneratorMajors, self).__init__(itemManager, qty, sm, maxDiff, random)
self.sparseRest = 1 + randGaussBounds(2, 5) self.sparseRest = 1 + randGaussBounds(self.random,2, 5)
self.mediumRest = 3 + randGaussBounds(4, 3.7) self.mediumRest = 3 + randGaussBounds(self.random, 4, 3.7)
self.ultraSparseNoTanks = self.isUltraSparseNoTanks() self.ultraSparseNoTanks = self.isUltraSparseNoTanks()
def addNoEnergy(self): def addNoEnergy(self):
@@ -609,7 +611,7 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
# no energy at all # no energy at all
self.addNoEnergy() self.addNoEnergy()
else: else:
if random.random() < 0.5: if self.random.random() < 0.5:
self.itemManager.addItem('ETank') self.itemManager.addItem('ETank')
else: else:
self.itemManager.addItem('Reserve') self.itemManager.addItem('Reserve')
@@ -620,7 +622,7 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
elif energyQty == 'sparse': elif energyQty == 'sparse':
# 4-6 # 4-6
if random.random() < 0.5: if self.random.random() < 0.5:
self.itemManager.addItem('Reserve') self.itemManager.addItem('Reserve')
else: else:
self.itemManager.addItem('ETank') self.itemManager.addItem('ETank')
@@ -639,7 +641,7 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
alreadyInPool = 2 alreadyInPool = 2
n = getE(3) n = getE(3)
for i in range(n): for i in range(n):
if random.random() < 0.5: if self.random.random() < 0.5:
self.itemManager.addItem('Reserve') self.itemManager.addItem('Reserve')
else: else:
self.itemManager.addItem('ETank') self.itemManager.addItem('ETank')
@@ -676,15 +678,15 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
return self.itemManager.getItemPool() return self.itemManager.getItemPool()
class ItemPoolGeneratorScavenger(ItemPoolGeneratorMajors): class ItemPoolGeneratorScavenger(ItemPoolGeneratorMajors):
def __init__(self, itemManager, qty, sm, maxDiff): def __init__(self, itemManager, qty, sm, maxDiff, random):
super(ItemPoolGeneratorScavenger, self).__init__(itemManager, qty, sm, maxDiff) super(ItemPoolGeneratorScavenger, self).__init__(itemManager, qty, sm, maxDiff, random)
def addNoEnergy(self): def addNoEnergy(self):
self.itemManager.addItem('Nothing') self.itemManager.addItem('Nothing')
class ItemPoolGeneratorMinimizer(ItemPoolGeneratorMajors): class ItemPoolGeneratorMinimizer(ItemPoolGeneratorMajors):
def __init__(self, itemManager, qty, sm, nLocs, maxDiff): def __init__(self, itemManager, qty, sm, nLocs, maxDiff, random):
super(ItemPoolGeneratorMinimizer, self).__init__(itemManager, qty, sm, maxDiff) super(ItemPoolGeneratorMinimizer, self).__init__(itemManager, qty, sm, maxDiff, random)
self.maxItems = nLocs self.maxItems = nLocs
self.calcMaxAmmo() self.calcMaxAmmo()
nMajors = len([itemName for itemName,item in ItemManager.Items.items() if item.Class == 'Major' and item.Category != 'Energy']) nMajors = len([itemName for itemName,item in ItemManager.Items.items() if item.Class == 'Major' and item.Category != 'Energy'])
@@ -716,8 +718,8 @@ class ItemPoolGeneratorMinimizer(ItemPoolGeneratorMajors):
self.log.debug("maxEnergy: "+str(self.maxEnergy)) self.log.debug("maxEnergy: "+str(self.maxEnergy))
class ItemPoolGeneratorPlando(ItemPoolGenerator): class ItemPoolGeneratorPlando(ItemPoolGenerator):
def __init__(self, itemManager, qty, sm, exclude, nLocs, maxDiff): def __init__(self, itemManager, qty, sm, exclude, nLocs, maxDiff, random):
super(ItemPoolGeneratorPlando, self).__init__(itemManager, qty, sm, maxDiff) super(ItemPoolGeneratorPlando, self).__init__(itemManager, qty, sm, maxDiff, random)
# in exclude dict: # in exclude dict:
# in alreadyPlacedItems: # in alreadyPlacedItems:
# dict of 'itemType: count' of items already added in the plando. # dict of 'itemType: count' of items already added in the plando.
@@ -805,7 +807,7 @@ class ItemPoolGeneratorPlando(ItemPoolGenerator):
if ammoQty: if ammoQty:
rangeDict = getRangeDict(ammoQty) rangeDict = getRangeDict(ammoQty)
while len(self.itemManager.getItemPool()) < maxItems and remain > 0: while len(self.itemManager.getItemPool()) < maxItems and remain > 0:
item = chooseFromRange(rangeDict) item = chooseFromRange(rangeDict, self.random)
self.itemManager.addMinor(item) self.itemManager.addMinor(item)
remain -= 1 remain -= 1

View File

@@ -1,4 +1,4 @@
import sys, random, time import sys, time
from ..utils import log from ..utils import log
from ..logic.logic import Logic from ..logic.logic import Logic
@@ -14,7 +14,7 @@ from ..utils.doorsmanager import DoorsManager
# entry point for rando execution ("randomize" method) # entry point for rando execution ("randomize" method)
class RandoExec(object): class RandoExec(object):
def __init__(self, seedName, vcr, randoSettings, graphSettings, player): def __init__(self, seedName, vcr, randoSettings, graphSettings, player, random):
self.errorMsg = "" self.errorMsg = ""
self.seedName = seedName self.seedName = seedName
self.vcr = vcr self.vcr = vcr
@@ -22,6 +22,7 @@ class RandoExec(object):
self.graphSettings = graphSettings self.graphSettings = graphSettings
self.log = log.get('RandoExec') self.log = log.get('RandoExec')
self.player = player self.player = player
self.random = random
# processes settings to : # processes settings to :
# - create Restrictions and GraphBuilder objects # - create Restrictions and GraphBuilder objects
@@ -31,7 +32,7 @@ class RandoExec(object):
vcr = VCR(self.seedName, 'rando') if self.vcr == True else None vcr = VCR(self.seedName, 'rando') if self.vcr == True else None
self.errorMsg = "" self.errorMsg = ""
split = self.randoSettings.restrictions['MajorMinor'] split = self.randoSettings.restrictions['MajorMinor']
self.graphBuilder = GraphBuilder(self.graphSettings) self.graphBuilder = GraphBuilder(self.graphSettings, self.random)
container = None container = None
i = 0 i = 0
attempts = 500 if self.graphSettings.areaRando or self.graphSettings.doorsColorsRando or split == 'Scavenger' else 1 attempts = 500 if self.graphSettings.areaRando or self.graphSettings.doorsColorsRando or split == 'Scavenger' else 1
@@ -43,10 +44,10 @@ class RandoExec(object):
while container is None and i < attempts and now <= endDate: while container is None and i < attempts and now <= endDate:
self.restrictions = Restrictions(self.randoSettings) self.restrictions = Restrictions(self.randoSettings)
if self.graphSettings.doorsColorsRando == True: if self.graphSettings.doorsColorsRando == True:
DoorsManager.randomize(self.graphSettings.allowGreyDoors, self.player) DoorsManager.randomize(self.graphSettings.allowGreyDoors, self.player, self.random)
self.areaGraph = self.graphBuilder.createGraph(self.randoSettings.maxDiff) self.areaGraph = self.graphBuilder.createGraph(self.randoSettings.maxDiff)
services = RandoServices(self.areaGraph, self.restrictions) services = RandoServices(self.areaGraph, self.restrictions, random=self.random)
setup = RandoSetup(self.graphSettings, Logic.locations[:], services, self.player) setup = RandoSetup(self.graphSettings, Logic.locations[:], services, self.player, self.random)
self.setup = setup self.setup = setup
container = setup.createItemLocContainer(endDate, vcr) container = setup.createItemLocContainer(endDate, vcr)
if container is None: if container is None:
@@ -78,7 +79,7 @@ class RandoExec(object):
n = nMaj n = nMaj
elif split == 'Chozo': elif split == 'Chozo':
n = nChozo n = nChozo
GraphUtils.updateLocClassesStart(startAP.GraphArea, split, possibleMajLocs, preserveMajLocs, n) GraphUtils.updateLocClassesStart(startAP.GraphArea, split, possibleMajLocs, preserveMajLocs, n, self.random)
def postProcessItemLocs(self, itemLocs, hide): def postProcessItemLocs(self, itemLocs, hide):
# hide some items like in dessy's # hide some items like in dessy's
@@ -89,7 +90,7 @@ class RandoExec(object):
if (item.Category != "Nothing" if (item.Category != "Nothing"
and loc.CanHidden == True and loc.CanHidden == True
and loc.Visibility == 'Visible'): and loc.Visibility == 'Visible'):
if bool(random.getrandbits(1)) == True: if bool(self.random.getrandbits(1)) == True:
loc.Visibility = 'Hidden' loc.Visibility = 'Hidden'
# put nothing in unfilled locations # put nothing in unfilled locations
filledLocNames = [il.Location.Name for il in itemLocs] filledLocNames = [il.Location.Name for il in itemLocs]

View File

@@ -1,5 +1,4 @@
import copy, sys, logging, os
import copy, random, sys, logging, os
from enum import Enum, unique from enum import Enum, unique
from ..utils import log from ..utils import log
from ..utils.parameters import infinity from ..utils.parameters import infinity
@@ -19,12 +18,13 @@ class ComebackCheckType(Enum):
# collection of stateless services to be used mainly by fillers # collection of stateless services to be used mainly by fillers
class RandoServices(object): class RandoServices(object):
def __init__(self, graph, restrictions, cache=None): def __init__(self, graph, restrictions, cache=None, *, random):
self.restrictions = restrictions self.restrictions = restrictions
self.settings = restrictions.settings self.settings = restrictions.settings
self.areaGraph = graph self.areaGraph = graph
self.cache = cache self.cache = cache
self.log = log.get('RandoServices') self.log = log.get('RandoServices')
self.random = random
@staticmethod @staticmethod
def printProgress(s): def printProgress(s):
@@ -217,7 +217,7 @@ class RandoServices(object):
# choose a morph item location in that context # choose a morph item location in that context
morphItemLoc = ItemLocation( morphItemLoc = ItemLocation(
morph, morph,
random.choice(morphLocs) self.random.choice(morphLocs)
) )
# acquire morph in new context and see if we can still open new locs # acquire morph in new context and see if we can still open new locs
newAP = self.collect(ap, containerCpy, morphItemLoc) newAP = self.collect(ap, containerCpy, morphItemLoc)
@@ -232,7 +232,7 @@ class RandoServices(object):
if morphLocItem is None or len(itemLocDict) == 1: if morphLocItem is None or len(itemLocDict) == 1:
# no morph, or it is the only possibility: nothing to do # no morph, or it is the only possibility: nothing to do
return return
morphLocs = self.restrictions.lateMorphCheck(container, itemLocDict[morphLocItem]) morphLocs = self.restrictions.lateMorphCheck(container, itemLocDict[morphLocItem], self.random)
if morphLocs is not None: if morphLocs is not None:
itemLocDict[morphLocItem] = morphLocs itemLocDict[morphLocItem] = morphLocs
else: else:
@@ -380,10 +380,10 @@ class RandoServices(object):
(itemLocDict, isProg) = self.getPossiblePlacements(ap, container, ComebackCheckType.NoCheck) (itemLocDict, isProg) = self.getPossiblePlacements(ap, container, ComebackCheckType.NoCheck)
assert not isProg assert not isProg
items = list(itemLocDict.keys()) items = list(itemLocDict.keys())
random.shuffle(items) self.random.shuffle(items)
for item in items: for item in items:
cont = copy.copy(container) cont = copy.copy(container)
loc = random.choice(itemLocDict[item]) loc = self.random.choice(itemLocDict[item])
itemLoc1 = ItemLocation(item, loc) itemLoc1 = ItemLocation(item, loc)
self.log.debug("itemLoc1 attempt: "+getItemLocStr(itemLoc1)) self.log.debug("itemLoc1 attempt: "+getItemLocStr(itemLoc1))
newAP = self.collect(ap, cont, itemLoc1) newAP = self.collect(ap, cont, itemLoc1)
@@ -391,8 +391,8 @@ class RandoServices(object):
self.cache.reset() self.cache.reset()
(ild, isProg) = self.getPossiblePlacements(newAP, cont, ComebackCheckType.NoCheck) (ild, isProg) = self.getPossiblePlacements(newAP, cont, ComebackCheckType.NoCheck)
if isProg: if isProg:
item2 = random.choice(list(ild.keys())) item2 = self.random.choice(list(ild.keys()))
itemLoc2 = ItemLocation(item2, random.choice(ild[item2])) itemLoc2 = ItemLocation(item2, self.random.choice(ild[item2]))
self.log.debug("itemLoc2: "+getItemLocStr(itemLoc2)) self.log.debug("itemLoc2: "+getItemLocStr(itemLoc2))
return (itemLoc1, itemLoc2) return (itemLoc1, itemLoc2)
return None return None

View File

@@ -1,5 +1,4 @@
import sys
import sys, random
from collections import defaultdict from collections import defaultdict
from ..rando.Items import ItemManager from ..rando.Items import ItemManager
from ..utils.utils import getRangeDict, chooseFromRange from ..utils.utils import getRangeDict, chooseFromRange
@@ -32,11 +31,11 @@ class RandoSettings(object):
def isPlandoRando(self): def isPlandoRando(self):
return self.PlandoOptions is not None return self.PlandoOptions is not None
def getItemManager(self, smbm, nLocs, bossesItems): def getItemManager(self, smbm, nLocs, bossesItems, random):
if not self.isPlandoRando(): if not self.isPlandoRando():
return ItemManager(self.restrictions['MajorMinor'], self.qty, smbm, nLocs, bossesItems, self.maxDiff) return ItemManager(self.restrictions['MajorMinor'], self.qty, smbm, nLocs, bossesItems, self.maxDiff, random)
else: else:
return ItemManager('Plando', self.qty, smbm, nLocs, bossesItems, self.maxDiff) return ItemManager('Plando', self.qty, smbm, nLocs, bossesItems, self.maxDiff, random)
def getExcludeItems(self, locations): def getExcludeItems(self, locations):
if not self.isPlandoRando(): if not self.isPlandoRando():
@@ -94,7 +93,7 @@ class ProgSpeedParameters(object):
self.restrictions = restrictions self.restrictions = restrictions
self.nLocs = nLocs self.nLocs = nLocs
def getVariableSpeed(self): def getVariableSpeed(self, random):
ranges = getRangeDict({ ranges = getRangeDict({
'slowest':7, 'slowest':7,
'slow':20, 'slow':20,
@@ -102,7 +101,7 @@ class ProgSpeedParameters(object):
'fast':27, 'fast':27,
'fastest':11 'fastest':11
}) })
return chooseFromRange(ranges) return chooseFromRange(ranges, random)
def getMinorHelpProb(self, progSpeed): def getMinorHelpProb(self, progSpeed):
if self.restrictions.split != 'Major': if self.restrictions.split != 'Major':
@@ -134,7 +133,7 @@ class ProgSpeedParameters(object):
def isSlow(self, progSpeed): def isSlow(self, progSpeed):
return progSpeed == "slow" or (progSpeed == "slowest" and self.restrictions.split == "Chozo") return progSpeed == "slow" or (progSpeed == "slowest" and self.restrictions.split == "Chozo")
def getItemLimit(self, progSpeed): def getItemLimit(self, progSpeed, random):
itemLimit = self.nLocs itemLimit = self.nLocs
if self.isSlow(progSpeed): if self.isSlow(progSpeed):
itemLimit = int(self.nLocs*0.209) # 21 for 105 itemLimit = int(self.nLocs*0.209) # 21 for 105

View File

@@ -1,4 +1,4 @@
import copy, random import copy
from ..utils import log from ..utils import log
from ..utils.utils import randGaussBounds from ..utils.utils import randGaussBounds
@@ -16,8 +16,9 @@ from ..rom.rom_patches import RomPatches
# checks init conditions for the randomizer: processes super fun settings, graph, start location, special restrictions # checks init conditions for the randomizer: processes super fun settings, graph, start location, special restrictions
# the entry point is createItemLocContainer # the entry point is createItemLocContainer
class RandoSetup(object): class RandoSetup(object):
def __init__(self, graphSettings, locations, services, player): def __init__(self, graphSettings, locations, services, player, random):
self.sm = SMBoolManager(player, services.settings.maxDiff) self.sm = SMBoolManager(player, services.settings.maxDiff)
self.random = random
self.settings = services.settings self.settings = services.settings
self.graphSettings = graphSettings self.graphSettings = graphSettings
self.startAP = graphSettings.startAP self.startAP = graphSettings.startAP
@@ -31,7 +32,7 @@ class RandoSetup(object):
# print("nLocs Setup: "+str(len(self.locations))) # print("nLocs Setup: "+str(len(self.locations)))
# in minimizer we can have some missing boss locs # in minimizer we can have some missing boss locs
bossesItems = [loc.BossItemType for loc in self.locations if loc.isBoss()] bossesItems = [loc.BossItemType for loc in self.locations if loc.isBoss()]
self.itemManager = self.settings.getItemManager(self.sm, len(self.locations), bossesItems) self.itemManager = self.settings.getItemManager(self.sm, len(self.locations), bossesItems, random)
self.forbiddenItems = [] self.forbiddenItems = []
self.restrictedLocs = [] self.restrictedLocs = []
self.lastRestricted = [] self.lastRestricted = []
@@ -165,7 +166,7 @@ class RandoSetup(object):
return True return True
self.log.debug("********* PRE RANDO START") self.log.debug("********* PRE RANDO START")
container = copy.copy(self.container) container = copy.copy(self.container)
filler = FrontFiller(self.startAP, self.areaGraph, self.restrictions, container) filler = FrontFiller(self.startAP, self.areaGraph, self.restrictions, container, random=self.random)
condition = filler.createStepCountCondition(4) condition = filler.createStepCountCondition(4)
(isStuck, itemLocations, progItems) = filler.generateItems(condition) (isStuck, itemLocations, progItems) = filler.generateItems(condition)
self.log.debug("********* PRE RANDO END") self.log.debug("********* PRE RANDO END")
@@ -345,9 +346,9 @@ class RandoSetup(object):
def getForbiddenItemsFromList(self, itemList): def getForbiddenItemsFromList(self, itemList):
self.log.debug('getForbiddenItemsFromList: ' + str(itemList)) self.log.debug('getForbiddenItemsFromList: ' + str(itemList))
remove = [] remove = []
n = randGaussBounds(len(itemList)) n = randGaussBounds(self.random, len(itemList))
for i in range(n): for i in range(n):
idx = random.randint(0, len(itemList) - 1) idx = self.random.randint(0, len(itemList) - 1)
item = itemList.pop(idx) item = itemList.pop(idx)
if item is not None: if item is not None:
remove.append(item) remove.append(item)

View File

@@ -1,4 +1,4 @@
import copy, random import copy
from ..utils import log from ..utils import log
from ..graph.graph_utils import getAccessPoint from ..graph.graph_utils import getAccessPoint
from ..rando.ItemLocContainer import getLocListStr from ..rando.ItemLocContainer import getLocListStr
@@ -112,7 +112,7 @@ class Restrictions(object):
return item.Class == "Minor" return item.Class == "Minor"
# return True if we can keep morph as a possibility # return True if we can keep morph as a possibility
def lateMorphCheck(self, container, possibleLocs): def lateMorphCheck(self, container, possibleLocs, random):
# the closer we get to the limit the higher the chances of allowing morph # the closer we get to the limit the higher the chances of allowing morph
proba = random.randint(0, self.lateMorphLimit) proba = random.randint(0, self.lateMorphLimit)
if self.split == 'Full': if self.split == 'Full':

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python3 #!/usr/bin/python3
from Utils import output_path from Utils import output_path
import argparse, os.path, json, sys, shutil, random, copy, requests import argparse, os.path, json, sys, shutil, copy, requests
from .rando.RandoSettings import RandoSettings, GraphSettings from .rando.RandoSettings import RandoSettings, GraphSettings
from .rando.RandoExec import RandoExec from .rando.RandoExec import RandoExec
@@ -39,7 +39,7 @@ objectives = defaultMultiValues['objective']
tourians = defaultMultiValues['tourian'] tourians = defaultMultiValues['tourian']
areaRandomizations = defaultMultiValues['areaRandomization'] areaRandomizations = defaultMultiValues['areaRandomization']
def randomMulti(args, param, defaultMultiValues): def randomMulti(args, param, defaultMultiValues, random):
value = args[param] value = args[param]
isRandom = False isRandom = False
@@ -250,10 +250,11 @@ class VariaRandomizer:
parser.add_argument('--tourianList', help="list to choose from when random", parser.add_argument('--tourianList', help="list to choose from when random",
dest='tourianList', nargs='?', default=None) dest='tourianList', nargs='?', default=None)
def __init__(self, options, rom, player): def __init__(self, options, rom, player, seed, random):
# parse args # parse args
self.args = copy.deepcopy(VariaRandomizer.parser.parse_args(["--logic", "varia"])) #dummy custom args to skip parsing _sys.argv while still get default values self.args = copy.deepcopy(VariaRandomizer.parser.parse_args(["--logic", "varia"])) #dummy custom args to skip parsing _sys.argv while still get default values
self.player = player self.player = player
self.random = random
args = self.args args = self.args
args.rom = rom args.rom = rom
# args.startLocation = to_pascal_case_with_space(options.startLocation.current_key) # args.startLocation = to_pascal_case_with_space(options.startLocation.current_key)
@@ -323,11 +324,13 @@ class VariaRandomizer:
logger.debug("preset: {}".format(preset)) logger.debug("preset: {}".format(preset))
# if no seed given, choose one # Archipelago provides a seed for the multiworld.
if args.seed == 0: self.seed = seed
self.seed = random.randrange(sys.maxsize) # # if no seed given, choose one
else: # if args.seed == 0:
self.seed = args.seed # self.seed = random.randrange(sys.maxsize)
# else:
# self.seed = args.seed
logger.debug("seed: {}".format(self.seed)) logger.debug("seed: {}".format(self.seed))
if args.raceMagic is not None: if args.raceMagic is not None:
@@ -360,12 +363,12 @@ class VariaRandomizer:
logger.debug("maxDifficulty: {}".format(self.maxDifficulty)) logger.debug("maxDifficulty: {}".format(self.maxDifficulty))
# handle random parameters with dynamic pool of values # handle random parameters with dynamic pool of values
(_, progSpeed) = randomMulti(args.__dict__, "progressionSpeed", speeds) (_, progSpeed) = randomMulti(args.__dict__, "progressionSpeed", speeds, random)
(_, progDiff) = randomMulti(args.__dict__, "progressionDifficulty", progDiffs) (_, progDiff) = randomMulti(args.__dict__, "progressionDifficulty", progDiffs, random)
(majorsSplitRandom, args.majorsSplit) = randomMulti(args.__dict__, "majorsSplit", majorsSplits) (majorsSplitRandom, args.majorsSplit) = randomMulti(args.__dict__, "majorsSplit", majorsSplits, random)
(_, self.gravityBehaviour) = randomMulti(args.__dict__, "gravityBehaviour", gravityBehaviours) (_, self.gravityBehaviour) = randomMulti(args.__dict__, "gravityBehaviour", gravityBehaviours, random)
(_, args.tourian) = randomMulti(args.__dict__, "tourian", tourians) (_, args.tourian) = randomMulti(args.__dict__, "tourian", tourians, random)
(areaRandom, args.area) = randomMulti(args.__dict__, "area", areaRandomizations) (areaRandom, args.area) = randomMulti(args.__dict__, "area", areaRandomizations, random)
areaRandomization = args.area in ['light', 'full'] areaRandomization = args.area in ['light', 'full']
lightArea = args.area == 'light' lightArea = args.area == 'light'
@@ -626,7 +629,7 @@ class VariaRandomizer:
if args.objective: if args.objective:
if (args.objectiveRandom): if (args.objectiveRandom):
availableObjectives = [goal for goal in objectives if goal != "collect 100% items"] if "random" in args.objectiveList else args.objectiveList availableObjectives = [goal for goal in objectives if goal != "collect 100% items"] if "random" in args.objectiveList else args.objectiveList
self.objectivesManager.setRandom(args.nbObjective, availableObjectives) self.objectivesManager.setRandom(args.nbObjective, availableObjectives, self.random)
else: else:
maxActiveGoals = Objectives.maxActiveGoals - addedObjectives maxActiveGoals = Objectives.maxActiveGoals - addedObjectives
if len(args.objective) > maxActiveGoals: if len(args.objective) > maxActiveGoals:
@@ -660,7 +663,7 @@ class VariaRandomizer:
# print("energyQty:{}".format(energyQty)) # print("energyQty:{}".format(energyQty))
#try: #try:
self.randoExec = RandoExec(seedName, args.vcr, randoSettings, graphSettings, self.player) self.randoExec = RandoExec(seedName, args.vcr, randoSettings, graphSettings, self.player, self.random)
self.container = self.randoExec.randomize() self.container = self.randoExec.randomize()
# if we couldn't find an area layout then the escape graph is not created either # if we couldn't find an area layout then the escape graph is not created either
# and getDoorConnections will crash if random escape is activated. # and getDoorConnections will crash if random escape is activated.
@@ -690,7 +693,7 @@ class VariaRandomizer:
'gameend.ips', 'grey_door_animals.ips', 'low_timer.ips', 'metalimals.ips', 'gameend.ips', 'grey_door_animals.ips', 'low_timer.ips', 'metalimals.ips',
'phantoonimals.ips', 'ridleyimals.ips'] 'phantoonimals.ips', 'ridleyimals.ips']
if args.escapeRando == False: if args.escapeRando == False:
args.patches.append(random.choice(animalsPatches)) args.patches.append(self.random.choice(animalsPatches))
args.patches.append("Escape_Animals_Change_Event") args.patches.append("Escape_Animals_Change_Event")
else: else:
optErrMsgs.append("Ignored animals surprise because of escape randomization") optErrMsgs.append("Ignored animals surprise because of escape randomization")
@@ -760,9 +763,9 @@ class VariaRandomizer:
# patch local rom # patch local rom
# romFileName = args.rom # romFileName = args.rom
# shutil.copyfile(romFileName, outputFilename) # shutil.copyfile(romFileName, outputFilename)
romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic, player=self.player) romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic, player=self.player, random=self.random)
else: else:
romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic) romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic, random=self.random)
if customPrePatchApply != None: if customPrePatchApply != None:
customPrePatchApply(romPatcher) customPrePatchApply(romPatcher)

View File

@@ -49,7 +49,7 @@ class RomPatcher:
'DoorsColors': ['beam_doors_plms.ips', 'beam_doors_gfx.ips', 'red_doors.ips'] 'DoorsColors': ['beam_doors_plms.ips', 'beam_doors_gfx.ips', 'red_doors.ips']
} }
def __init__(self, settings=None, romFileName=None, magic=None, player=0): def __init__(self, settings=None, romFileName=None, magic=None, player=0, *, random):
self.log = log.get('RomPatcher') self.log = log.get('RomPatcher')
self.settings = settings self.settings = settings
#self.romFileName = romFileName #self.romFileName = romFileName
@@ -76,6 +76,7 @@ class RomPatcher:
0x93ea: self.forceRoomCRE 0x93ea: self.forceRoomCRE
} }
self.player = player self.player = player
self.random = random
def patchRom(self): def patchRom(self):
self.applyIPSPatches() self.applyIPSPatches()
@@ -496,9 +497,9 @@ class RomPatcher:
self.ipsPatches = [] self.ipsPatches = []
def writeSeed(self, seed): def writeSeed(self, seed):
random.seed(seed) r = random.Random(seed)
seedInfo = random.randint(0, 0xFFFF) seedInfo = r.randint(0, 0xFFFF)
seedInfo2 = random.randint(0, 0xFFFF) seedInfo2 = r.randint(0, 0xFFFF)
self.romFile.writeWord(seedInfo, snes_to_pc(0xdfff00)) self.romFile.writeWord(seedInfo, snes_to_pc(0xdfff00))
self.romFile.writeWord(seedInfo2) self.romFile.writeWord(seedInfo2)
@@ -1066,7 +1067,7 @@ class RomPatcher:
def writeObjectives(self, itemLocs, tourian): def writeObjectives(self, itemLocs, tourian):
objectives = Objectives.objDict[self.player] objectives = Objectives.objDict[self.player]
objectives.writeGoals(self.romFile) objectives.writeGoals(self.romFile, self.random)
objectives.writeIntroObjectives(self.romFile, tourian) objectives.writeIntroObjectives(self.romFile, tourian)
self.writeItemsMasks(itemLocs) self.writeItemsMasks(itemLocs)
# hack bomb_torizo.ips to wake BT in all cases if necessary, ie chozo bots objective is on, and nothing at bombs # hack bomb_torizo.ips to wake BT in all cases if necessary, ie chozo bots objective is on, and nothing at bombs

View File

@@ -1,4 +1,3 @@
import random
from enum import IntEnum,IntFlag from enum import IntEnum,IntFlag
import copy import copy
from ..logic.smbool import SMBool from ..logic.smbool import SMBool
@@ -123,7 +122,7 @@ class Door(object):
else: else:
return [color for color in colorsList if color not in self.forbiddenColors] return [color for color in colorsList if color not in self.forbiddenColors]
def randomize(self, allowGreyDoors): def randomize(self, allowGreyDoors, random):
if self.canRandomize(): if self.canRandomize():
if self.canGrey and allowGreyDoors: if self.canGrey and allowGreyDoors:
self.setColor(random.choice(self.filterColorList(colorsListGrey))) self.setColor(random.choice(self.filterColorList(colorsListGrey)))
@@ -347,9 +346,9 @@ class DoorsManager():
currentDoors['CrabShaftRight'].forceBlue() currentDoors['CrabShaftRight'].forceBlue()
@staticmethod @staticmethod
def randomize(allowGreyDoors, player): def randomize(allowGreyDoors, player, random):
for door in DoorsManager.doorsDict[player].values(): for door in DoorsManager.doorsDict[player].values():
door.randomize(allowGreyDoors) door.randomize(allowGreyDoors, random)
# set both ends of toilet to the same color to avoid soft locking in area rando # set both ends of toilet to the same color to avoid soft locking in area rando
toiletTop = DoorsManager.doorsDict[player]['PlasmaSparkBottom'] toiletTop = DoorsManager.doorsDict[player]['PlasmaSparkBottom']
toiletBottom = DoorsManager.doorsDict[player]['OasisTop'] toiletBottom = DoorsManager.doorsDict[player]['OasisTop']

View File

@@ -1,5 +1,4 @@
import copy import copy
import random
from ..rom.addresses import Addresses from ..rom.addresses import Addresses
from ..rom.rom import pc_to_snes from ..rom.rom import pc_to_snes
from ..logic.helpers import Bosses from ..logic.helpers import Bosses
@@ -28,7 +27,7 @@ class Synonyms(object):
] ]
alreadyUsed = [] alreadyUsed = []
@staticmethod @staticmethod
def getVerb(): def getVerb(random):
verb = random.choice(Synonyms.killSynonyms) verb = random.choice(Synonyms.killSynonyms)
while verb in Synonyms.alreadyUsed: while verb in Synonyms.alreadyUsed:
verb = random.choice(Synonyms.killSynonyms) verb = random.choice(Synonyms.killSynonyms)
@@ -88,10 +87,10 @@ class Goal(object):
# not all objectives require an ap (like limit objectives) # not all objectives require an ap (like limit objectives)
return self.clearFunc(smbm, ap) return self.clearFunc(smbm, ap)
def getText(self): def getText(self, random):
out = "{}. ".format(self.rank) out = "{}. ".format(self.rank)
if self.useSynonym: if self.useSynonym:
out += self.text.format(Synonyms.getVerb()) out += self.text.format(Synonyms.getVerb(random))
else: else:
out += self.text out += self.text
assert len(out) <= 28, "Goal text '{}' is too long: {}, max 28".format(out, len(out)) assert len(out) <= 28, "Goal text '{}' is too long: {}, max 28".format(out, len(out))
@@ -676,7 +675,7 @@ class Objectives(object):
return [goal.name for goal in _goals.values() if goal.available and (not removeNothing or goal.name != "nothing")] return [goal.name for goal in _goals.values() if goal.available and (not removeNothing or goal.name != "nothing")]
# call from rando # call from rando
def setRandom(self, nbGoals, availableGoals): def setRandom(self, nbGoals, availableGoals, random):
while self.nbActiveGoals < nbGoals and availableGoals: while self.nbActiveGoals < nbGoals and availableGoals:
goalName = random.choice(availableGoals) goalName = random.choice(availableGoals)
self.addGoal(goalName) self.addGoal(goalName)
@@ -702,7 +701,7 @@ class Objectives(object):
LOG.debug("tourianRequired: {}".format(self.tourianRequired)) LOG.debug("tourianRequired: {}".format(self.tourianRequired))
# call from rando # call from rando
def writeGoals(self, romFile): def writeGoals(self, romFile, random):
# write check functions # write check functions
romFile.seek(Addresses.getOne('objectivesList')) romFile.seek(Addresses.getOne('objectivesList'))
for goal in self.activeGoals: for goal in self.activeGoals:
@@ -736,7 +735,7 @@ class Objectives(object):
space = 3 if self.nbActiveGoals == 5 else 4 space = 3 if self.nbActiveGoals == 5 else 4
for i, goal in enumerate(self.activeGoals): for i, goal in enumerate(self.activeGoals):
addr = baseAddr + i * lineLength * space addr = baseAddr + i * lineLength * space
text = goal.getText() text = goal.getText(random)
romFile.seek(addr) romFile.seek(addr)
for c in text: for c in text:
if c not in char2tile: if c not in char2tile:

View File

@@ -1,5 +1,5 @@
import io import io
import os, json, re, random import os, json, re
import pathlib import pathlib
import sys import sys
from typing import Any from typing import Any
@@ -88,7 +88,7 @@ def normalizeRounding(n):
# gauss random in [0, r] range # gauss random in [0, r] range
# the higher the slope, the less probable extreme values are. # the higher the slope, the less probable extreme values are.
def randGaussBounds(r, slope=5): def randGaussBounds(random, r, slope=5):
r = float(r) r = float(r)
n = normalizeRounding(random.gauss(r/2, r/slope)) n = normalizeRounding(random.gauss(r/2, r/slope))
if n < 0: if n < 0:
@@ -111,7 +111,7 @@ def getRangeDict(weightDict):
return rangeDict return rangeDict
def chooseFromRange(rangeDict): def chooseFromRange(rangeDict, random):
r = random.random() r = random.random()
val = None val = None
for v in sorted(rangeDict, key=rangeDict.get): for v in sorted(rangeDict, key=rangeDict.get):