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, PlandoOptions, 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.PlandoOptions = PlandoOptions
|
|
self.minDiff = minDiff
|
|
|
|
def getSuperFun(self):
|
|
return self.superFun[:]
|
|
|
|
def updateSuperFun(self, superFun):
|
|
self.superFun = superFun[:]
|
|
|
|
def isPlandoRando(self):
|
|
return self.PlandoOptions 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.PlandoOptions["locsItems"].items():
|
|
if not any(loc.Name == locName for loc in locations):
|
|
continue
|
|
exclude['alreadyPlacedItems'][itemType] += 1
|
|
exclude['alreadyPlacedItems']['total'] += 1
|
|
|
|
exclude['forbiddenItems'] = self.PlandoOptions['forbiddenItems']
|
|
|
|
return exclude
|
|
|
|
def collectAlreadyPlacedItemLocations(self, container):
|
|
if not self.isPlandoRando():
|
|
return
|
|
for locName,itemType in self.PlandoOptions["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
|