Ejemplo n.º 1
0
 def onlyBossesLeft(self, ap, container):
     if self.settings.maxDiff == infinity:
         return False
     self.log.debug('onlyBossesLeft, diff=' + str(self.settings.maxDiff) + ", ap="+ap)
     sm = container.sm
     bossesLeft = container.getAllItemsInPoolFromCategory('Boss')
     if len(bossesLeft) == 0:
         return False
     def getLocList():
         curLocs = self.currentLocations(ap, container)
         self.log.debug('onlyBossesLeft, curLocs=' + getLocListStr(curLocs))
         return self.getPlacementLocs(ap, container, ComebackCheckType.JustComeback, None, curLocs)
     prevLocs = getLocList()
     self.log.debug("onlyBossesLeft. prevLocs="+getLocListStr(prevLocs))
     # fake kill remaining bosses and see if we can access the rest of the game
     if self.cache is not None:
         self.cache.reset()
     for boss in bossesLeft:
         self.log.debug('onlyBossesLeft. kill '+boss.Name)
         sm.addItem(boss.Type)
     # get bosses locations and newly accessible locations (for bosses that open up locs)
     newLocs = getLocList()
     self.log.debug("onlyBossesLeft. newLocs="+getLocListStr(newLocs))
     locs = newLocs + container.getLocs(lambda loc: loc.isBoss() and not loc in newLocs)
     self.log.debug("onlyBossesLeft. locs="+getLocListStr(locs))
     ret = (len(locs) > len(prevLocs) and len(locs) == len(container.unusedLocations))
     # restore bosses killed state
     for boss in bossesLeft:
         self.log.debug('onlyBossesLeft. revive '+boss.Name)
         sm.removeItem(boss.Type)
     if self.cache is not None:
         self.cache.reset()
     self.log.debug("onlyBossesLeft? " +str(ret))
     return ret
Ejemplo n.º 2
0
 def nextDecision(self, majorsAvailable, minorsAvailable, hasEnoughMinors,
                  diffThreshold):
     # since solver split is forced to Full, majorsAvailable=minorsAvailable
     # we don't care about hasEnoughMinors, we're gonna pick enough anyway
     # we can ignore diffThreshold as well, we have self.maxDiff
     scavAvailable = [
         loc for loc in majorsAvailable if loc in self.remainingScavLocs and
         loc.difficulty.difficulty <= self.maxDiff and loc.comeBack == True
     ]
     minorsAvailable = [
         loc for loc in majorsAvailable
         if loc not in self.remainingScavLocs and
         loc.difficulty.difficulty <= self.maxDiff and loc.comeBack == True
     ]
     ret = None
     if len(minorsAvailable) > 0:
         nextMinor = random.choice(minorsAvailable)
         ret = self.collectMajor(nextMinor)
     elif len(scavAvailable) > 0:
         scavAvailable = self.filterScavAvailable(scavAvailable)
         self.log.debug("scavAvailable: " + getLocListStr(scavAvailable))
         nextScav = self.chooseNextScavLoc(scavAvailable)
         self.pickupScav(nextScav)
         ret = self.collectMajor(nextScav)
     else:
         # fallback to base solver behaviour to handle comeback etc
         ret = super(ScavengerSolver, self).nextDecision(
             majorsAvailable, majorsAvailable, hasEnoughMinors,
             diffThreshold
         )  # not a typo, the args are the same, and we overwrote minorsAvailable
         if ret is not None and ret in self.remainingScavLocs:
             self.pickupScav(ret)
     if ret is not None:
         self.visited.append(ret)
     return ret
Ejemplo n.º 3
0
 def prepareFirstPhase(self):
     self.changedKnows = {}
     # forces IceZebSkip if necessary to finish with 10-10-5
     if Knows.IceZebSkip.bool == False or Knows.IceZebSkip.difficulty > self.maxDiff:
         self.changedKnows['IceZebSkip'] = Knows.IceZebSkip
         Knows.IceZebSkip = SMBool(True, 0, [])
     # hack knows to remove those > maxDiff
     for attr,k in Knows.__dict__.items():
         if isKnows(attr) and k.bool == True and k.difficulty > self.maxDiff:
             self.log.debug("prepareFirstPhase. disabling knows "+attr)
             self.changedKnows[attr] = k
             setattr(Knows, attr, SMBool(False, 0))
     # set max diff to god (for hard rooms/hellruns/bosses)
     self.settings.maxDiff = god
     # prepare 1st phase container
     itemCond = isChozoItem
     locCond = lambda loc: loc.isChozo() or loc.isBoss()
     # this will create a new smbm with new knows functions
     cont = self.baseContainer.slice(itemCond, locCond)
     secondPhaseItems = [item for item in self.baseContainer.itemPool if item not in cont.itemPool]
     contLocs = self.baseContainer.extractLocs(cont.unusedLocations)
     secondPhaseLocs = [loc for loc in self.baseContainer.unusedLocations if loc not in contLocs]
     self.log.debug("prepareFirstPhase. secondPhaseItems="+getItemListStr(secondPhaseItems))
     self.log.debug("prepareFirstPhase. secondPhaseLocs="+getLocListStr(secondPhaseLocs))
     self.secondPhaseContainer = ItemLocContainer(cont.sm, secondPhaseItems, secondPhaseLocs)
     return self.fillerFactory.createFirstPhaseFiller(cont)
Ejemplo n.º 4
0
 def fill(locs, getPred):
     self.log.debug("fillRestrictedLocations. locs=" +
                    getLocListStr(locs))
     for loc in locs:
         loc.restricted = True
         itemLocation = ItemLocation(None, loc)
         if self.container.hasItemInPool(getPred('Nothing')):
             itemLocation.Item = self.container.getNextItemInPoolMatching(
                 getPred('Nothing'))
         elif self.container.hasItemInPool(getPred('NoEnergy')):
             itemLocation.Item = self.container.getNextItemInPoolMatching(
                 getPred('NoEnergy'))
         elif self.container.countItems(getPred('Missile')) > 3:
             itemLocation.Item = self.container.getNextItemInPoolMatching(
                 getPred('Missile'))
         elif self.container.countItems(getPred('Super')) > 2:
             itemLocation.Item = self.container.getNextItemInPoolMatching(
                 getPred('Super'))
         elif self.container.countItems(getPred('PowerBomb')) > 1:
             itemLocation.Item = self.container.getNextItemInPoolMatching(
                 getPred('PowerBomb'))
         elif self.container.countItems(getPred('Reserve')) > 1:
             itemLocation.Item = self.container.getNextItemInPoolMatching(
                 getPred('Reserve'))
         elif self.container.countItems(getPred('ETank')) > 3:
             itemLocation.Item = self.container.getNextItemInPoolMatching(
                 getPred('ETank'))
         else:
             raise RuntimeError("Cannot fill restricted locations")
         self.log.debug("Fill: {} at {}".format(
             itemLocation.Item.Type, itemLocation.Location.Name))
         self.container.collect(itemLocation, False)
Ejemplo n.º 5
0
 def fillRestrictedLocations(self):
     def getPred(itemType, loc):
         return lambda item: (itemType is None or item.Type == itemType) and self.restrictions.canPlaceAtLocation(item, loc, self.container)
     locs = self.restrictedLocs
     self.log.debug("fillRestrictedLocations. locs="+getLocListStr(locs))
     for loc in locs:
         itemLocation = ItemLocation(None, loc)
         if loc.BossItemType is not None:
             itemLocation.Item = self.container.getNextItemInPoolMatching(getPred(loc.BossItemType, loc))
         elif self.container.hasItemInPool(getPred('Nothing', loc)):
             itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Nothing', loc))
         elif self.container.hasItemInPool(getPred('NoEnergy', loc)):
             itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('NoEnergy', loc))
         elif self.container.countItems(getPred('Missile', loc)) > 3:
             itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Missile', loc))
         elif self.container.countItems(getPred('Super', loc)) > 2:
             itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Super', loc))
         elif self.container.countItems(getPred('PowerBomb', loc)) > 1:
             itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('PowerBomb', loc))
         elif self.container.countItems(getPred('Reserve', loc)) > 1:
             itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Reserve', loc))
         elif self.container.countItems(getPred('ETank', loc)) > 3:
             itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('ETank', loc))
         else:
             raise RuntimeError("Cannot fill restricted locations")
         self.log.debug("Fill: {}/{} at {}".format(itemLocation.Item.Type, itemLocation.Item.Class, itemLocation.Location.Name))
         self.container.collect(itemLocation, False)
Ejemplo n.º 6
0
 def lateMorphInit(self, ap, emptyContainer, services):
     assert self.isLateMorph()
     morph = emptyContainer.getNextItemInPool('Morph')
     assert morph is not None
     locs = services.possibleLocations(morph, ap, emptyContainer)
     self.log.debug('lateMorphInit. locs=' + getLocListStr(locs))
     self.lateMorphLimit = len(locs)
     if len(set([loc['GraphArea'] for loc in locs])) > 1:
         self.lateMorphForbiddenArea = getAccessPoint(ap).GraphArea
     else:
         self.lateMorphForbiddenArea = None
Ejemplo n.º 7
0
 def __init__(self,
              startAP,
              graph,
              restrictions,
              fullContainer,
              endDate=infinity):
     super(FillerScavenger, self).__init__(startAP, graph, restrictions,
                                           fullContainer, endDate)
     self.remainingScavLocs = restrictions.scavLocs[:]
     self.log.debug("FillerScavenger ctor. scavLocs=" +
                    getLocListStr(self.remainingScavLocs))
 def step(self):
     if len(self.conditions) > 1:
         self.determineParameters()
         curLocs = []
         while self.firstPhaseIndex < len(self.firstPhaseItemLocs):
             self.cache.reset()
             newCurLocs = [loc for loc in self.currentLocations() if loc not in curLocs]
             curLocs += newCurLocs
             cond = self.nextMetCondition()
             if cond is not None:
                 self.log.debug('step. cond item='+cond[0])
                 self.conditions.remove(cond)
                 break
             itemLoc = self.firstPhaseItemLocs[self.firstPhaseIndex]
             self.collect(itemLoc, container=self.firstPhaseContainer)
             self.firstPhaseIndex += 1
         self.log.debug('step. curLocs='+getLocListStr(curLocs))
         restrictedItemTypes = [cond[0] for cond in self.conditions]
         self.log.debug('step. restrictedItemTypes='+str(restrictedItemTypes))
         basePool = self.container.itemPool[:]
         itemPool = []
         self.log.debug('step. basePool: {}'.format(getItemListStr(basePool)))
         while len(itemPool) < len(curLocs):
             item = random.choice(basePool)
             if item.Type not in restrictedItemTypes or\
                random.random() < self.restrictedItemProba or\
                self.restrictedItemProba == 0 and not any(item for item in basePool if item.Type not in restrictedItemTypes):
                 itemPool.append(item)
                 basePool.remove(item)
         self.log.debug('step. itemPool='+getItemListStr(itemPool))
         cont = ItemLocContainer(self.container.sm, itemPool, curLocs)
         self.container.transferCollected(cont)
         filler = FillerRandomItems(self.ap, self.graph, self.restrictions, cont, self.endDate)
         (stuck, itemLocs, prog) = filler.generateItems()
         if stuck:
             if len(filler.errorMsg) > 0:
                 self.errorMsg += '\n'+filler.errorMsg
             return False
         for itemLoc in itemLocs:
             if itemLoc.Location in self.container.unusedLocations:
                 self.log.debug("step. POST COLLECT "+itemLoc.Item.Type+" at "+itemLoc.Location.Name)
                 self.container.collect(itemLoc)
     else:
         # merge collected of 1st phase and 2nd phase so far for seed to be solvable by random fill
         self.container.itemLocations += self.firstPhaseItemLocs
         self.log.debug("step. LAST FILL. cont: "+self.container.dump())
         filler = FillerRandomNoCopy(self.startAP, self.graph, self.restrictions, self.container, self.endDate, diffSteps=100)
         (stuck, itemLocs, prog) = filler.generateItems()
         if len(filler.errorMsg) > 0:
             self.errorMsg += '\n'+filler.errorMsg
         if stuck:
             return False
     return True
Ejemplo n.º 9
0
 def possibleLocations(self, item, ap, emptyContainer, bossesKilled=True):
     assert len(emptyContainer.currentItems) == 0, "Invalid call to possibleLocations. emptyContainer had collected items"
     emptyContainer.sm.resetItems()
     self.log.debug('possibleLocations. item='+item.Type)
     if bossesKilled:
         itemLambda = lambda it: it.Type != item.Type
     else:
         itemLambda = lambda it: it.Type != item.Type and it.Category != 'Boss'
     allBut = emptyContainer.getItems(itemLambda)
     self.log.debug('possibleLocations. allBut='+getItemListStr(allBut))
     emptyContainer.sm.addItems([it.Type for it in allBut])
     ret = [loc for loc in self.currentLocations(ap, emptyContainer, post=True) if self.restrictions.canPlaceAtLocation(item, loc, emptyContainer)]
     self.log.debug('possibleLocations='+getLocListStr(ret))
     emptyContainer.sm.resetItems()
     return ret
Ejemplo n.º 10
0
 def initScavenger(self, endDate, vcr=None):
     attempts = 30 if self.restrictions.scavIsVanilla else 1
     majorLocs = [loc for loc in self.container.unusedLocations if self.restrictions.isLocMajor(loc) and loc not in self.restrictedLocs and (not self.restrictions.scavIsVanilla or (loc.VanillaItemType not in self.forbiddenItems and self.container.getNextItemInPool(loc.VanillaItemType) is not None))]
     # if scav randomized and super funs we have to remove super fun items
     superFunCount = len(self.forbiddenItems) if not self.restrictions.scavIsVanilla and len(self.forbiddenItems) > 0 else 0
     nLocs = min(self.settings.restrictions['ScavengerParams']['numLocs'], len(majorLocs) - superFunCount)
     self.log.debug("initScavenger. nLocs="+str(nLocs))
     cont = None
     restr = None
     i = 0
     def checkRestrictionsDict(r):
         # check if there are items impossible to place
         allTypes = []
         okTypes = []
         for area, entry in r.items():
             for itemType, locs in entry.items():
                 if itemType not in allTypes:
                     allTypes.append(itemType)
                 if itemType not in okTypes and len(locs) > 0:
                     okTypes.append(itemType)
         self.log.debug("checkRestrictionsDict. allTypes="+str(allTypes))
         self.log.debug("checkRestrictionsDict. okTypes="+str(okTypes))
         return len(okTypes) == len(allTypes)
     if len(majorLocs) > nLocs:
         while restr is None and i < attempts:
             random.shuffle(majorLocs)
             self.restrictions.setScavengerLocs(majorLocs[:nLocs])
             self.log.debug("initScavenger. attempt "+str(i)+", scavLocs="+getLocListStr(self.restrictions.scavLocs))
             r = self.getRestrictionsDict()
             if checkRestrictionsDict(r):
                 restr = r
             i += 1
     else:
         self.restrictions.setScavengerLocs(majorLocs)
         r = self.getRestrictionsDict()
         if checkRestrictionsDict(r):
             restr = r
     if restr is not None:
         self.log.debug("initScavenger. got list after "+str(i+1)+" attempts")
         # finally, actually do the randomization using a speedrun filler (stepLimit attempts heuristic)
         stepLimit = 50
         self.restrictions.setPlacementRestrictions(restr)
         filler = FillerRandomSpeedrun(self.graphSettings, self.areaGraph, self.restrictions, self.container, endDate=endDate, diffSteps=stepLimit)
         stepCond = filler.createStepCountCondition(stepLimit)
         filler.generateItems(condition=lambda: filler.itemPoolCondition() and stepCond(), vcr=vcr)
         if not filler.itemPoolCondition():
             cont = filler.container
     return cont
Ejemplo n.º 11
0
 def lateMorphInit(self, ap, emptyContainer, services):
     assert self.isLateMorph()
     morph = emptyContainer.getNextItemInPool('Morph')
     assert morph is not None
     locs = services.possibleLocations(morph, ap, emptyContainer, bossesKilled=False)
     self.lateMorphLimit = len(locs)
     self.log.debug('lateMorphInit. {} locs: {}'.format(self.lateMorphLimit, getLocListStr(locs)))
     areas = {}
     for loc in locs:
         areas[loc.GraphArea] = areas.get(loc.GraphArea, 0) + 1
     self.log.debug('lateMorphLimit. areas: {}'.format(areas))
     if len(areas) > 1:
         self.lateMorphForbiddenArea = getAccessPoint(ap).GraphArea
         self.log.debug('lateMorphLimit. forbid start area: {}'.format(self.lateMorphForbiddenArea))
     else:
         self.lateMorphForbiddenArea = None
Ejemplo n.º 12
0
 def __init__(self, graphSettings, locations, services):
     self.sm = SMBoolManager()
     self.settings = services.settings
     self.graphSettings = graphSettings
     self.startAP = graphSettings.startAP
     self.superFun = self.settings.getSuperFun()
     self.container = None
     self.services = services
     self.restrictions = services.restrictions
     self.areaGraph = services.areaGraph
     self.allLocations = locations
     self.locations = self.areaGraph.getAccessibleLocations(
         locations, self.startAP)
     #        print("nLocs Setup: "+str(len(self.locations)))
     self.itemManager = self.settings.getItemManager(
         self.sm, len(self.locations))
     self.forbiddenItems = []
     self.restrictedLocs = []
     self.lastRestricted = []
     self.bossesLocs = sorted(
         ['Draygon', 'Kraid', 'Ridley', 'Phantoon', 'Mother Brain'])
     self.suits = ['Varia', 'Gravity']
     # organized by priority
     self.movementItems = [
         'SpaceJump', 'HiJump', 'SpeedBooster', 'Bomb', 'Grapple',
         'SpringBall'
     ]
     # organized by priority
     self.combatItems = ['ScrewAttack', 'Plasma', 'Wave', 'Spazer']
     # OMG
     self.bossChecks = {
         'Kraid': self.sm.enoughStuffsKraid,
         'Phantoon': self.sm.enoughStuffsPhantoon,
         'Draygon': self.sm.enoughStuffsDraygon,
         'Ridley': self.sm.enoughStuffsRidley,
         'Mother Brain': self.sm.enoughStuffsMotherbrain
     }
     self.okay = lambda: SMBool(True, 0)
     exclude = self.settings.getExcludeItems(self.locations)
     # we have to use item manager only once, otherwise pool will change
     self.itemManager.createItemPool(exclude)
     self.basePool = self.itemManager.getItemPool()[:]
     self.log = utils.log.get('RandoSetup')
     if len(locations) != len(self.locations):
         self.log.debug("inaccessible locations :" + getLocListStr(
             [loc for loc in locations if loc not in self.locations]))
Ejemplo n.º 13
0
    def getPossiblePlacements(self, ap, container, comebackCheck):
        curLocs = self.currentLocations(ap, container)
        self.log.debug('getPossiblePlacements. nCurLocs='+str(len(curLocs)))
        self.log.debug('getPossiblePlacements. curLocs='+getLocListStr(curLocs))
        self.log.debug('getPossiblePlacements. comebackCheck='+str(comebackCheck))
        sm = container.sm
        poolDict = container.getPoolDict()
        itemLocDict = {}
        possibleProg = False
        nonProgList = None
        def getLocList(itemObj):
            nonlocal curLocs
            return self.getPlacementLocs(ap, container, comebackCheck, itemObj, curLocs)
        def getNonProgLocList():
            nonlocal nonProgList
            if nonProgList is None:
                nonProgList = [loc for loc in self.currentLocations(ap, container) if self.fullComebackCheck(container, ap, None, loc, comebackCheck)]
                self.log.debug("nonProgLocList="+str([loc.Name for loc in nonProgList]))
            return [loc for loc in nonProgList if self.restrictions.canPlaceAtLocation(itemObj, loc, container)]
        for itemType,items in sorted(poolDict.items()):
            itemObj = items[0]
            cont = True
            prog = False
            if self.isProgression(itemObj, ap, container):
                cont = False
                prog = True
            elif not possibleProg:
                cont = False
            if cont: # ignore non prog items if a prog item has already been found
                continue
            # check possible locations for this item type
#            self.log.debug('getPossiblePlacements. itemType=' + itemType + ', curLocs='+str([loc.Name for loc in curLocs]))
            locations = getLocList(itemObj) if prog else getNonProgLocList()
            if len(locations) == 0:
                continue
            if prog and not possibleProg:
                possibleProg = True
                itemLocDict = {} # forget all the crap ones we stored just in case
#            self.log.debug('getPossiblePlacements. itemType=' + itemType + ', locs='+str([loc.Name for loc in locations]))
            for item in items:
                itemLocDict[item] = locations
        self.processPlacementRestrictions(ap, container, comebackCheck, itemLocDict, curLocs)
        self.printItemLocDict(itemLocDict)
        self.log.debug('possibleProg='+str(possibleProg))
        return (itemLocDict, possibleProg)
Ejemplo n.º 14
0
 def filterScavAvailable(self, scavAvailable):
     # special case of Space Jump/Plasma :
     #
     # If both are available (i.e. they are both in the list, Dray is dead and Plasma is accessible),
     # and the only way out of draygon/precious we have is CF, we must pick up space before plasma.
     # Indeed, we cannot CF exit a second time, we need Draygon for that.
     #
     # This is kind of a global logic issue, but it only ever causes trouble in scavenger when pickup
     # order is forced in-game. Indeed in other cases, even if the solver or rando can get/place something
     # outside draygon's lair before space jump, the player can pick up space jump loc first anyway.
     if not any(loc.Name == "Space Jump"
                for loc in scavAvailable) or not any(
                    loc.Name == "Plasma Beam" for loc in scavAvailable):
         return scavAvailable
     # check that Draygon CF is known
     k = self.smbm.knowsDraygonRoomCrystalFlash()
     if k.bool == False or k.difficulty > self.maxDiff:
         return scavAvailable
     self.log.debug("Space/Plasma special. Scav list: " +
                    getLocListStr(scavAvailable))
     # check that Draygon CF is required by removing it from known tech
     self.smbm.changeKnows("DraygonRoomCrystalFlash", SMBool(False))
     # (check only AP path to remove current loc/AP constraints,
     # indeed we could have picked up any minor loc after draygon
     # and be anywhere)
     self.areaGraph.resetCache()
     path = self.areaGraph.accessPath(self.smbm, "Draygon Room Bottom",
                                      "Toilet Top", self.maxDiff)
     self.log.debug("Space/Plasma special. Path:" + str(path))
     isRequired = (path is None)
     self.smbm.restoreKnows('DraygonRoomCrystalFlash')
     self.areaGraph.resetCache()
     if isRequired == True:
         self.log.debug("Space/Plasma special. Filtering out plasma")
         # filter out plasma until space jump is placed
         return [loc for loc in scavAvailable if loc.Name != 'Plasma Beam']
     return scavAvailable
Ejemplo n.º 15
0
 def getLocList():
     curLocs = self.currentLocations(ap, container)
     self.log.debug('onlyBossesLeft, curLocs=' + getLocListStr(curLocs))
     return self.getPlacementLocs(ap, container,
                                  ComebackCheckType.JustComeback, None,
                                  curLocs)
Ejemplo n.º 16
0
 def getProgressionItemLocations(self):
     self.log.debug("Final Scavenger list: {}".format(
         getLocListStr(self.solver.scavOrder)))
     return [
         self.container.getItemLoc(loc) for loc in self.solver.scavOrder
     ]
Ejemplo n.º 17
0
 def setScavengerLocs(self, scavLocs):
     self.scavLocs = scavLocs
     self.log.debug("scavLocs=" + getLocListStr(scavLocs))
     self.scavItemTypes = [loc.VanillaItemType for loc in scavLocs]