271 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			271 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | 
 | ||
|  | 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 |