mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
Added Super Metroid support (#46)
Varia Randomizer based implementation LttPClient -> SNIClient
This commit is contained in:
135
worlds/sm/variaRandomizer/rando/Filler.py
Normal file
135
worlds/sm/variaRandomizer/rando/Filler.py
Normal file
@@ -0,0 +1,135 @@
|
||||
|
||||
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
|
||||
print('\n%d step(s) in %dms' % (self.nSteps, int((date-self.startDate)*1000)))
|
||||
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
|
Reference in New Issue
Block a user