import sys, random from collections import defaultdict from rando.Items import ItemManager from utils.utils import getRangeDict, chooseFromRange from rando.ItemLocContainer import ItemLocation # Holder for settings and a few utility functions related to them # (especially for plando/rando). # Holds settings not related to graph layout. class RandoSettings(object): def __init__(self, maxDiff, progSpeed, progDiff, qty, restrictions, superFun, runtimeLimit_s, plandoSettings, minDiff): self.progSpeed = progSpeed.lower() self.progDiff = progDiff.lower() self.maxDiff = maxDiff self.qty = qty self.restrictions = restrictions self.superFun = superFun self.runtimeLimit_s = runtimeLimit_s if self.runtimeLimit_s <= 0: self.runtimeLimit_s = sys.maxsize self.plandoSettings = plandoSettings self.minDiff = minDiff def getSuperFun(self): return self.superFun[:] def updateSuperFun(self, superFun): self.superFun = superFun[:] def isPlandoRando(self): return self.plandoSettings is not None def getItemManager(self, smbm, nLocs): if not self.isPlandoRando(): return ItemManager(self.restrictions['MajorMinor'], self.qty, smbm, nLocs, self.maxDiff) else: return ItemManager('Plando', self.qty, smbm, nLocs, self.maxDiff) def getExcludeItems(self, locations): if not self.isPlandoRando(): return None exclude = {'alreadyPlacedItems': defaultdict(int), 'forbiddenItems': []} # locsItems is a dict {'loc name': 'item type'} for locName,itemType in self.plandoSettings["locsItems"].items(): if not any(loc.Name == locName for loc in locations): continue exclude['alreadyPlacedItems'][itemType] += 1 exclude['alreadyPlacedItems']['total'] += 1 exclude['forbiddenItems'] = self.plandoSettings['forbiddenItems'] return exclude def collectAlreadyPlacedItemLocations(self, container): if not self.isPlandoRando(): return for locName,itemType in self.plandoSettings["locsItems"].items(): if not any(loc.Name == locName for loc in container.unusedLocations): continue item = container.getNextItemInPool(itemType) assert item is not None, "Invalid plando item pool" location = container.getLocs(lambda loc: loc.Name == locName)[0] itemLoc = ItemLocation(item, location) container.collect(itemLoc, pickup=False) # Holds settings and utiliy functions related to graph layout class GraphSettings(object): def __init__(self, startAP, areaRando, lightAreaRando, bossRando, escapeRando, minimizerN, dotFile, doorsColorsRando, allowGreyDoors, plandoRandoTransitions): self.startAP = startAP self.areaRando = areaRando self.lightAreaRando = lightAreaRando self.bossRando = bossRando self.escapeRando = escapeRando self.minimizerN = minimizerN self.dotFile = dotFile self.doorsColorsRando = doorsColorsRando self.allowGreyDoors = allowGreyDoors self.plandoRandoTransitions = plandoRandoTransitions def isMinimizer(self): return self.minimizerN is not None # algo settings depending on prog speed (slowest to fastest+variable, # other "speeds" are actually different algorithms) class ProgSpeedParameters(object): def __init__(self, restrictions, nLocs): self.restrictions = restrictions self.nLocs = nLocs def getVariableSpeed(self): ranges = getRangeDict({ 'slowest':7, 'slow':20, 'medium':35, 'fast':27, 'fastest':11 }) return chooseFromRange(ranges) def getMinorHelpProb(self, progSpeed): if self.restrictions.split != 'Major': return 0 if progSpeed == 'slowest': return 0.16 elif progSpeed == 'slow': return 0.33 elif progSpeed == 'medium': return 0.5 return 1 def getLateDoorsProb(self, progSpeed): if progSpeed == 'slowest': return 1 elif progSpeed == 'slow': return 0.8 elif progSpeed == 'medium': return 0.66 elif progSpeed == 'fast': return 0.5 elif progSpeed == 'fastest': return 0.33 return 0 def getItemLimit(self, progSpeed): itemLimit = self.nLocs if progSpeed == 'slow': itemLimit = int(self.nLocs*0.209) # 21 for 105 elif progSpeed == 'medium': itemLimit = int(self.nLocs*0.095) # 9 for 105 elif progSpeed == 'fast': itemLimit = int(self.nLocs*0.057) # 5 for 105 elif progSpeed == 'fastest': itemLimit = int(self.nLocs*0.019) # 1 for 105 minLimit = itemLimit - int(itemLimit/5) maxLimit = itemLimit + int(itemLimit/5) if minLimit == maxLimit: itemLimit = minLimit else: itemLimit = random.randint(minLimit, maxLimit) return itemLimit def getLocLimit(self, progSpeed): locLimit = -1 if progSpeed == 'slow': locLimit = 1 elif progSpeed == 'medium': locLimit = 2 elif progSpeed == 'fast': locLimit = 3 elif progSpeed == 'fastest': locLimit = 4 return locLimit def getProgressionItemTypes(self, progSpeed): progTypes = ItemManager.getProgTypes() if self.restrictions.isLateDoors(): progTypes += ['Wave','Spazer','Plasma'] progTypes.append('Charge') if progSpeed == 'slowest': return progTypes else: progTypes.remove('HiJump') progTypes.remove('Charge') if progSpeed == 'slow': return progTypes else: progTypes.remove('Bomb') progTypes.remove('Grapple') if progSpeed == 'medium': return progTypes else: if not self.restrictions.isLateDoors(): progTypes.remove('Ice') progTypes.remove('SpaceJump') if progSpeed == 'fast': return progTypes else: progTypes.remove('SpeedBooster') if progSpeed == 'fastest': return progTypes # only morph, varia, gravity raise RuntimeError("Unknown prog speed " + progSpeed) def getPossibleSoftlockProb(self, progSpeed): if progSpeed == 'slowest': return 1 if progSpeed == 'slow': return 0.66 if progSpeed == 'medium': return 0.33 if progSpeed == 'fast': return 0.1 if progSpeed == 'fastest': return 0 raise RuntimeError("Unknown prog speed " + progSpeed) def getChooseLocDict(self, progDiff): if progDiff == 'normal': return { 'Random' : 1, 'MinDiff' : 0, 'MaxDiff' : 0 } elif progDiff == 'easier': return { 'Random' : 2, 'MinDiff' : 1, 'MaxDiff' : 0 } elif progDiff == 'harder': return { 'Random' : 2, 'MinDiff' : 0, 'MaxDiff' : 1 } def getChooseItemDict(self, progSpeed): if progSpeed == 'slowest': return { 'MinProgression' : 1, 'Random' : 2, 'MaxProgression' : 0 } elif progSpeed == 'slow': return { 'MinProgression' : 25, 'Random' : 75, 'MaxProgression' : 0 } elif progSpeed == 'medium': return { 'MinProgression' : 0, 'Random' : 1, 'MaxProgression' : 0 } elif progSpeed == 'fast': return { 'MinProgression' : 0, 'Random' : 85, 'MaxProgression' : 15 } elif progSpeed == 'fastest': return { 'MinProgression' : 0, 'Random' : 2, 'MaxProgression' : 1 } def getSpreadFactor(self, progSpeed): if progSpeed == 'slowest': return 0.9 elif progSpeed == 'slow': return 0.7 elif progSpeed == 'medium': return 0.4 elif progSpeed == 'fast': return 0.1 return 0 def getChozoSecondPhaseRestrictionProb(self, progSpeed): if progSpeed == 'slowest': return 0 if progSpeed == 'slow': return 0.16 if progSpeed == 'medium': return 0.5 if progSpeed == 'fast': return 0.9 return 1