| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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, | 
					
						
							| 
									
										
										
										
											2023-01-17 17:25:59 +01:00
										 |  |  |                  superFun, runtimeLimit_s, PlandoOptions, minDiff): | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2023-01-17 17:25:59 +01:00
										 |  |  |         self.PlandoOptions = PlandoOptions | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         self.minDiff = minDiff | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getSuperFun(self): | 
					
						
							|  |  |  |         return self.superFun[:] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def updateSuperFun(self, superFun): | 
					
						
							|  |  |  |         self.superFun = superFun[:] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def isPlandoRando(self): | 
					
						
							| 
									
										
										
										
											2023-01-17 17:25:59 +01:00
										 |  |  |         return self.PlandoOptions is not None | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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'} | 
					
						
							| 
									
										
										
										
											2023-01-17 17:25:59 +01:00
										 |  |  |         for locName,itemType in self.PlandoOptions["locsItems"].items(): | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             if not any(loc.Name == locName for loc in locations): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             exclude['alreadyPlacedItems'][itemType] += 1 | 
					
						
							|  |  |  |             exclude['alreadyPlacedItems']['total'] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-17 17:25:59 +01:00
										 |  |  |         exclude['forbiddenItems'] = self.PlandoOptions['forbiddenItems'] | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return exclude | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def collectAlreadyPlacedItemLocations(self, container): | 
					
						
							|  |  |  |         if not self.isPlandoRando(): | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2023-01-17 17:25:59 +01:00
										 |  |  |         for locName,itemType in self.PlandoOptions["locsItems"].items(): | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             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 |