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
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)
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
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 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() 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