예제 #1
0
    def checkPool(self, forbidden=None):
        self.log.debug("checkPool. forbidden=" + str(forbidden) +
                       ", self.forbiddenItems=" + str(self.forbiddenItems))
        if not self.graphSettings.isMinimizer(
        ) and not self.settings.isPlandoRando() and len(
                self.allLocations) > len(self.locations):
            # invalid graph with looped areas
            self.log.debug(
                "checkPool: not all areas are connected, but minimizer param is off / not a plando rando"
            )
            return False
        ret = True
        if forbidden is not None:
            pool = self.getItemPool(forbidden)
        else:
            pool = self.getItemPool()
        # get restricted locs
        totalAvailLocs = []
        comeBack = {}
        try:
            container = ItemLocContainer(self.sm, pool, self.locations)
        except AssertionError as e:
            # invalid graph altogether
            self.log.debug(
                "checkPool: AssertionError when creating ItemLocContainer: {}".
                format(e))
            return False
        # restrict item pool in chozo: game should be finishable with chozo items only
        contPool = []
        if self.restrictions.isChozo():
            container.restrictItemPool(isChozoItem)
            missile = container.getNextItemInPool('Missile')
            if missile is not None:
                # add missile (if zeb skip not known)
                contPool.append(missile)
        contPool += [item for item in pool if item in container.itemPool]
        # give us everything and beat every boss to see what we can access
        self.disableBossChecks()
        self.sm.resetItems()
        self.sm.addItems([item.Type
                          for item in contPool])  # will add bosses as well
        poolDict = container.getPoolDict()
        self.log.debug('pool={}'.format(
            sorted([(t, len(poolDict[t])) for t in poolDict])))
        locs = self.services.currentLocations(self.startAP,
                                              container,
                                              post=True)
        self.areaGraph.useCache(True)
        for loc in locs:
            ap = loc.accessPoint
            if ap not in comeBack:
                # we chose Golden Four because it is always there.
                # Start APs might not have comeback transitions
                # possible start AP issues are handled in checkStart
                comeBack[ap] = self.areaGraph.canAccess(
                    self.sm, ap, 'Golden Four', self.settings.maxDiff)
            if comeBack[ap]:
                totalAvailLocs.append(loc)
        self.areaGraph.useCache(False)
        self.lastRestricted = [
            loc for loc in self.locations if loc not in totalAvailLocs
        ]
        self.log.debug("restricted=" +
                       str([loc.Name for loc in self.lastRestricted]))

        # check if all inter-area APs can reach each other
        interAPs = [
            ap for ap in self.areaGraph.getAccessibleAccessPoints(self.startAP)
            if not ap.isInternal() and not ap.isLoop()
        ]
        for startAp in interAPs:
            availAccessPoints = self.areaGraph.getAvailableAccessPoints(
                startAp, self.sm, self.settings.maxDiff)
            for ap in interAPs:
                if not ap in availAccessPoints:
                    self.log.debug(
                        "checkPool: ap {} non accessible from {}".format(
                            ap.Name, startAp.Name))
                    ret = False
        if not ret:
            self.log.debug("checkPool. inter-area APs check failed")
        # cleanup
        self.sm.resetItems()
        self.restoreBossChecks()
        # check if we can reach/beat all bosses
        if ret:
            for loc in self.lastRestricted:
                if loc.Name in self.bossesLocs:
                    ret = False
                    self.log.debug("unavail Boss: " + loc.Name)
            if ret:
                # revive bosses
                self.sm.addItems([
                    item.Type for item in contPool if item.Category != 'Boss'
                ])
                maxDiff = self.settings.maxDiff
                # see if phantoon doesn't block himself, and if we can reach draygon if she's alive
                ret = self.areaGraph.canAccess(self.sm, self.startAP, 'PhantoonRoomIn', maxDiff)\
                      and self.areaGraph.canAccess(self.sm, self.startAP, 'DraygonRoomIn', maxDiff)
                if ret:
                    # see if we can beat bosses with this equipment (infinity as max diff for a "onlyBossesLeft" type check
                    beatableBosses = sorted([
                        loc.Name for loc in self.services.currentLocations(
                            self.startAP, container, diff=infinity)
                        if loc.isBoss()
                    ])
                    self.log.debug("checkPool. beatableBosses=" +
                                   str(beatableBosses))
                    ret = beatableBosses == Bosses.Golden4()
                    if ret:
                        # check that we can then kill mother brain
                        self.sm.addItems(Bosses.Golden4())
                        beatableMotherBrain = [
                            loc.Name for loc in self.services.currentLocations(
                                self.startAP, container, diff=infinity)
                            if loc.Name == 'Mother Brain'
                        ]
                        ret = len(beatableMotherBrain) > 0
                        self.log.debug(
                            "checkPool. beatable Mother Brain={}".format(ret))
                else:
                    self.log.debug('checkPool. locked by Phantoon or Draygon')
                self.log.debug('checkPool. boss access sanity check: ' +
                               str(ret))

        if self.restrictions.isChozo():
            # last check for chozo locations: don't put more restricted chozo locations than removed chozo items
            # (we cannot rely on removing ammo/energy in fillRestrictedLocations since it is already the bare minimum in chozo pool)
            # FIXME something to do there for ultra sparse, it gives us up to 3 more spots for nothing items
            restrictedLocs = self.restrictedLocs + [
                loc for loc in self.lastRestricted
                if loc not in self.restrictedLocs
            ]
            nRestrictedChozo = sum(1 for loc in restrictedLocs
                                   if loc.isChozo())
            nNothingChozo = sum(
                1 for item in pool
                if 'Chozo' in item.Class and item.Category == 'Nothing')
            ret &= nRestrictedChozo <= nNothingChozo
            self.log.debug('checkPool. nRestrictedChozo=' +
                           str(nRestrictedChozo) + ', nNothingChozo=' +
                           str(nNothingChozo))
        self.log.debug('checkPool. result: ' + str(ret))
        return ret
예제 #2
0
    def checkPool(self, forbidden=None):
        self.log.debug("checkPool. forbidden=" + str(forbidden) + ", self.forbiddenItems=" + str(self.forbiddenItems))
        if not self.graphSettings.isMinimizer() and not self.settings.isPlandoRando() and len(self.allLocations) > len(self.locations):
            # invalid graph with looped areas
            self.log.debug("checkPool: not all areas are connected, but minimizer param is off / not a plando rando")
            return False
        ret = True
        if forbidden is not None:
            pool = self.getItemPool(forbidden)
        else:
            pool = self.getItemPool()
        # get restricted locs
        totalAvailLocs = []
        comeBack = {}
        try:
            container = ItemLocContainer(self.sm, pool, self.locations)
        except AssertionError as e:
            # invalid graph altogether
            self.log.debug("checkPool: AssertionError when creating ItemLocContainer: {}".format(e))
            return False
        # restrict item pool in chozo: game should be finishable with chozo items only
        contPool = []
        if self.restrictions.isChozo():
            container.restrictItemPool(isChozoItem)
            missile = container.getNextItemInPool('Missile')
            if missile is not None:
                # add missile (if zeb skip not known)
                contPool.append(missile)
        contPool += [item for item in pool if item in container.itemPool]
        # give us everything and beat every boss to see what we can access
        self.disableBossChecks()
        self.sm.resetItems()
        self.sm.addItems([item.Type for item in contPool]) # will add bosses as well
        self.log.debug('pool={}'.format(getItemListStr(container.itemPool)))
        locs = self.services.currentLocations(self.startAP, container, post=True)
        self.areaGraph.useCache(True)
        for loc in locs:
            ap = loc.accessPoint
            if ap not in comeBack:
                # we chose Golden Four because it is always there.
                # Start APs might not have comeback transitions
                # possible start AP issues are handled in checkStart
                comeBack[ap] = self.areaGraph.canAccess(self.sm, ap, 'Golden Four', self.settings.maxDiff)
            if comeBack[ap]:
                totalAvailLocs.append(loc)
        self.areaGraph.useCache(False)
        self.lastRestricted = [loc for loc in self.locations if loc not in totalAvailLocs]
        self.log.debug("restricted=" + str([loc.Name for loc in self.lastRestricted]))
        # check if objectives are compatible with accessible APs
        startAP = self.areaGraph.accessPoints[self.startAP]
        availAPs = [ap.Name for ap in self.areaGraph.getAvailableAccessPoints(startAP, self.sm, self.settings.maxDiff)]
        self.log.debug("availAPs="+str(availAPs))
        for goal in Objectives.activeGoals:
            n, aps = goal.escapeAccessPoints
            if len(aps) == 0:
                continue
            escAPs = [ap for ap in aps if ap in availAPs]
            self.log.debug("escAPs="+str(escAPs))
            if len(escAPs) < n:
                self.log.debug("checkPool. goal '"+goal.name+"' impossible to complete due to area layout")
                ret = False
                continue
            for ap in escAPs:
                if not self.areaGraph.canAccess(self.sm, ap, "Golden Four", self.settings.maxDiff):
                    self.log.debug("checkPool. goal '"+goal.name+"' impossible to complete due to area layout")
                    ret = False
                    break
        # check if all inter-area APs can reach each other
        if ret:
            interAPs = [ap for ap in self.areaGraph.getAccessibleAccessPoints(self.startAP) if not ap.isInternal() and not ap.isLoop()]
            for startAp in interAPs:
                availAccessPoints = self.areaGraph.getAvailableAccessPoints(startAp, self.sm, self.settings.maxDiff)
                for ap in interAPs:
                    if not ap in availAccessPoints:
                        self.log.debug("checkPool: ap {} non accessible from {}".format(ap.Name, startAp.Name))
                        ret = False
            if not ret:
                self.log.debug("checkPool. inter-area APs check failed")
        # cleanup
        self.sm.resetItems()
        self.restoreBossChecks()
        # check if we can reach/beat all bosses
        if ret:
            # always add G4 to mandatory bosses, even if not required by objectives
            mandatoryBosses = set(Objectives.getMandatoryBosses() + Bosses.Golden4())

            for loc in self.lastRestricted:
                if loc.Name in self.bossesLocs:
                    ret = False
                    self.log.debug("unavail Boss: " + loc.Name)
            if ret:
                # revive bosses
                self.sm.addItems([item.Type for item in contPool if item.Category != 'Boss'])
                maxDiff = self.settings.maxDiff
                # see if phantoon doesn't block himself, and if we can reach draygon if she's alive
                ret = self.areaGraph.canAccess(self.sm, self.startAP, 'PhantoonRoomIn', maxDiff)\
                      and self.areaGraph.canAccess(self.sm, self.startAP, 'DraygonRoomIn', maxDiff)
                if ret:
                    # see if we can beat bosses with this equipment (infinity as max diff for a "onlyBossesLeft" type check
                    beatableBosses = sorted([loc.BossItemType for loc in self.services.currentLocations(self.startAP, container, diff=infinity) if loc.isBoss()])
                    self.log.debug("checkPool. beatableBosses="+str(beatableBosses))
                    self.log.debug("checkPool. mandatoryBosses: {}".format(mandatoryBosses))
                    ret = mandatoryBosses.issubset(set(beatableBosses)) and Objectives.checkLimitObjectives(beatableBosses)
                    if ret:
                        # check that we can then kill mother brain
                        self.sm.addItems(Bosses.Golden4() + Bosses.miniBosses())
                        beatableMotherBrain = [loc.Name for loc in self.services.currentLocations(self.startAP, container, diff=infinity) if loc.Name == 'Mother Brain']
                        ret = len(beatableMotherBrain) > 0
                        self.log.debug("checkPool. beatable Mother Brain={}".format(ret))
                else:
                    self.log.debug('checkPool. locked by Phantoon or Draygon')
                self.log.debug('checkPool. boss access sanity check: '+str(ret))

        if self.restrictions.isChozo() or self.restrictions.isScavenger():
            # in chozo or scavenger, we cannot put other items than NoEnergy in the restricted locations,
            # we would be forced to put majors in there, which can make seed generation fail:
            # don't put more restricted major locations than removed major items
            # FIXME something to do there for chozo/ultra sparse, it gives us up to 3 more spots for nothing items
            restrictedLocs = self.restrictedLocs + [loc for loc in self.lastRestricted if loc not in self.restrictedLocs]
            nRestrictedMajor = sum(1 for loc in restrictedLocs if self.restrictions.isLocMajor(loc))
            nNothingMajor = sum(1 for item in pool if self.restrictions.isItemMajor(item) and item.Category == 'Nothing')
            ret &= nRestrictedMajor <= nNothingMajor
            self.log.debug('checkPool. nRestrictedMajor='+str(nRestrictedMajor)+', nNothingMajor='+str(nNothingMajor))
        self.log.debug('checkPool. result: '+str(ret))
        return ret