136 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			136 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Python
		
	
	
	
 | 
						|
import utils.log, copy, time, random
 | 
						|
 | 
						|
from logic.cache import RequestCache
 | 
						|
from rando.RandoServices import RandoServices
 | 
						|
from rando.Choice import ItemThenLocChoice
 | 
						|
from rando.RandoServices import ComebackCheckType
 | 
						|
from rando.ItemLocContainer import ItemLocation, getItemLocationsStr
 | 
						|
from utils.parameters import infinity
 | 
						|
from logic.helpers import diffValue2txt
 | 
						|
from graph.graph_utils import GraphUtils
 | 
						|
 | 
						|
# base class for fillers. a filler responsibility is to fill a given
 | 
						|
# ItemLocContainer while a certain condition is fulfilled (usually
 | 
						|
# item pool is not empty).
 | 
						|
# entry point is generateItems
 | 
						|
class Filler(object):
 | 
						|
    def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity):
 | 
						|
        self.startAP = startAP
 | 
						|
        self.cache = RequestCache()
 | 
						|
        self.graph = graph
 | 
						|
        self.services = RandoServices(graph, restrictions, self.cache)
 | 
						|
        self.restrictions = restrictions
 | 
						|
        self.settings = restrictions.settings
 | 
						|
        self.endDate = endDate
 | 
						|
        self.baseContainer = emptyContainer
 | 
						|
        self.maxDiff = self.settings.maxDiff
 | 
						|
        self.log = utils.log.get('Filler')
 | 
						|
 | 
						|
    # reinit algo state
 | 
						|
    def initFiller(self):
 | 
						|
        self.ap = self.startAP
 | 
						|
        self.initContainer()
 | 
						|
        self.nSteps = 0
 | 
						|
        self.errorMsg = ""
 | 
						|
        self.settings.maxDiff = self.maxDiff
 | 
						|
        self.startDate = time.process_time()
 | 
						|
 | 
						|
    # sets up container initial state
 | 
						|
    def initContainer(self):
 | 
						|
        self.container = copy.copy(self.baseContainer)
 | 
						|
 | 
						|
    # default continuation condition: item pool is not empty
 | 
						|
    def itemPoolCondition(self):
 | 
						|
        return not self.container.isPoolEmpty()
 | 
						|
 | 
						|
    # factory for step count condition
 | 
						|
    def createStepCountCondition(self, n):
 | 
						|
        return lambda: self.nSteps < n
 | 
						|
 | 
						|
    # calls step while condition is fulfilled and we did not hit runtime limit
 | 
						|
    # condition: continuation condition
 | 
						|
    # vcr: debug VCR object
 | 
						|
    # shall return (stuck, itemLoc dict list, progression itemLoc dict list)
 | 
						|
    def generateItems(self, condition=None, vcr=None):
 | 
						|
        self.vcr = vcr
 | 
						|
        self.initFiller()
 | 
						|
        if condition is None:
 | 
						|
            condition = self.itemPoolCondition
 | 
						|
        isStuck = False
 | 
						|
        date = self.startDate
 | 
						|
        while condition() and not isStuck and date <= self.endDate:
 | 
						|
            isStuck = not self.step()
 | 
						|
            if not isStuck:
 | 
						|
                self.nSteps += 1
 | 
						|
            date = time.process_time()
 | 
						|
        if condition() or date > self.endDate:
 | 
						|
            isStuck = True
 | 
						|
            if date > self.endDate:
 | 
						|
                self.errorMsg = "Exceeded time limit of "+str(self.settings.runtimeLimit_s) +" seconds"
 | 
						|
            else:
 | 
						|
                self.errorMsg = "STUCK !\n"+self.container.dump()
 | 
						|
        else:
 | 
						|
            # check if some locations are above max diff and add relevant message
 | 
						|
            locs = self.container.getUsedLocs(lambda loc: loc.difficulty.difficulty > self.maxDiff)
 | 
						|
            aboveMaxDiffStr = '[ ' + ' ; '.join([loc.Name + ': ' + diffValue2txt(loc.difficulty.difficulty) for loc in locs]) + ' ]'
 | 
						|
            if aboveMaxDiffStr != '[  ]':
 | 
						|
                self.errorMsg += "\nMaximum difficulty could not be applied everywhere. Affected locations: {}".format(aboveMaxDiffStr)
 | 
						|
            isStuck = False
 | 
						|
 | 
						|
        if self.vcr != None:
 | 
						|
            self.vcr.dump()
 | 
						|
        return isStuck, self.container.itemLocations, self.getProgressionItemLocations()
 | 
						|
 | 
						|
    # helper method to collect in item/location with logic. updates self.ap and VCR
 | 
						|
    def collect(self, itemLoc, container=None, pickup=True):
 | 
						|
        containerArg = container
 | 
						|
        if container is None:
 | 
						|
            container = self.container
 | 
						|
        location = itemLoc.Location
 | 
						|
        item = itemLoc.Item
 | 
						|
        pickup &= location.restricted is None or location.restricted == False
 | 
						|
        self.ap = self.services.collect(self.ap, container, itemLoc, pickup=pickup)
 | 
						|
        self.log.debug("AP="+self.ap)
 | 
						|
        if self.vcr is not None and containerArg is None:
 | 
						|
            self.vcr.addLocation(location.Name, item.Type)
 | 
						|
 | 
						|
    # called by generateItems at the end to knows which particulier
 | 
						|
    # item/locations were progression, if the info is available
 | 
						|
    def getProgressionItemLocations(self):
 | 
						|
        return []
 | 
						|
 | 
						|
    # performs a fill step. can be multiple item/locations placement,
 | 
						|
    # not necessarily just one.
 | 
						|
    # return True if ok, False if stuck
 | 
						|
    def step(self):
 | 
						|
        pass
 | 
						|
 | 
						|
# very simple front fill algorithm with no rollback and no "softlock checks" (== dessy algorithm)
 | 
						|
class FrontFiller(Filler):
 | 
						|
    def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity):
 | 
						|
        super(FrontFiller, self).__init__(startAP, graph, restrictions, emptyContainer, endDate)
 | 
						|
        self.choice = ItemThenLocChoice(restrictions)
 | 
						|
        self.stdStart = GraphUtils.isStandardStart(self.startAP)
 | 
						|
 | 
						|
    def isEarlyGame(self):
 | 
						|
        n = 2 if self.stdStart else 3
 | 
						|
        return len(self.container.currentItems) <= n
 | 
						|
 | 
						|
    # one item/loc per step
 | 
						|
    def step(self, onlyBossCheck=False):
 | 
						|
        self.cache.reset()
 | 
						|
        if not self.services.can100percent(self.ap, self.container):
 | 
						|
            comebackCheck = ComebackCheckType.ComebackWithoutItem if not self.isEarlyGame() else ComebackCheckType.NoCheck
 | 
						|
            (itemLocDict, isProg) = self.services.getPossiblePlacements(self.ap, self.container, comebackCheck)
 | 
						|
        else:
 | 
						|
            (itemLocDict, isProg) = self.services.getPossiblePlacementsNoLogic(self.container)
 | 
						|
        itemLoc = self.choice.chooseItemLoc(itemLocDict, isProg)
 | 
						|
        if itemLoc is None:
 | 
						|
            if onlyBossCheck == False and self.services.onlyBossesLeft(self.ap, self.container):
 | 
						|
                self.settings.maxDiff = infinity
 | 
						|
                return self.step(onlyBossCheck=True)
 | 
						|
            return False
 | 
						|
        self.collect(itemLoc)
 | 
						|
        return True
 |