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:
248
worlds/sm/variaRandomizer/rando/ItemLocContainer.py
Normal file
248
worlds/sm/variaRandomizer/rando/ItemLocContainer.py
Normal file
@@ -0,0 +1,248 @@
|
||||
|
||||
import copy, utils.log
|
||||
|
||||
from logic.smbool import SMBool, smboolFalse
|
||||
from logic.smboolmanager import SMBoolManager
|
||||
from collections import Counter
|
||||
|
||||
class ItemLocation(object):
|
||||
__slots__ = ( 'Item', 'Location', 'Accessible' )
|
||||
|
||||
def __init__(self, Item=None, Location=None, accessible=True):
|
||||
self.Item = Item
|
||||
self.Location = Location
|
||||
self.Accessible = accessible
|
||||
|
||||
def json(self):
|
||||
return {'Item': self.Item.json(), 'Location': self.Location.json()}
|
||||
|
||||
def getItemListStr(items):
|
||||
return str(dict(Counter(["%s/%s" % (item.Type,item.Class) for item in items])))
|
||||
|
||||
def getLocListStr(locs):
|
||||
return str([loc.Name for loc in locs])
|
||||
|
||||
def getItemLocStr(itemLoc):
|
||||
return itemLoc.Item.Type + " at " + itemLoc.Location.Name
|
||||
|
||||
def getItemLocationsStr(itemLocations):
|
||||
return str([getItemLocStr(il) for il in itemLocations])
|
||||
|
||||
class ContainerSoftBackup(object):
|
||||
def __init__(self, container):
|
||||
self.itemLocations = container.itemLocations[:]
|
||||
self.itemPool = container.itemPool[:]
|
||||
self.unusedLocations = container.unusedLocations[:]
|
||||
self.currentItems = container.currentItems[:]
|
||||
|
||||
def restore(self, container, resetSM=True):
|
||||
# avoid costly deep copies of locations
|
||||
container.itemLocations = self.itemLocations[:]
|
||||
container.itemPool = self.itemPool[:]
|
||||
container.unusedLocations = self.unusedLocations[:]
|
||||
container.currentItems = self.currentItems[:]
|
||||
if resetSM:
|
||||
container.sm.resetItems()
|
||||
container.sm.addItems([it.Type for it in container.currentItems])
|
||||
|
||||
# Holds items yet to place (itemPool), locations yet to fill (unusedLocations),
|
||||
# placed items/locations (itemLocations).
|
||||
# If logic is needed, also holds a SMBoolManager (sm) and collected items so far
|
||||
# (collectedItems)
|
||||
class ItemLocContainer(object):
|
||||
def __init__(self, sm, itemPool, locations):
|
||||
self.sm = sm
|
||||
self.itemLocations = []
|
||||
self.unusedLocations = locations
|
||||
self.currentItems = []
|
||||
self.itemPool = itemPool
|
||||
self.itemPoolBackup = None
|
||||
self.unrestrictedItems = set()
|
||||
self.log = utils.log.get('ItemLocContainer')
|
||||
self.checkConsistency()
|
||||
|
||||
def checkConsistency(self):
|
||||
assert len(self.unusedLocations) == len(self.itemPool), "Item({})/Locs({}) count mismatch".format(len(self.itemPool), len(self.unusedLocations))
|
||||
|
||||
def __eq__(self, rhs):
|
||||
eq = self.currentItems == rhs.currentItems
|
||||
eq &= getLocListStr(self.unusedLocations) == getLocListStr(rhs.unusedLocations)
|
||||
eq &= self.itemPool == rhs.itemPool
|
||||
eq &= getItemLocationsStr(self.itemLocations) == getItemLocationsStr(rhs.itemLocations)
|
||||
|
||||
return eq
|
||||
|
||||
def __copy__(self):
|
||||
locs = copy.copy(self.unusedLocations)
|
||||
# we don't copy restriction state on purpose: it depends on
|
||||
# outside context we don't want to bring to the copy
|
||||
ret = ItemLocContainer(SMBoolManager(self.sm.player, self.sm.maxDiff),
|
||||
self.itemPoolBackup[:] if self.itemPoolBackup != None else self.itemPool[:],
|
||||
locs)
|
||||
ret.currentItems = self.currentItems[:]
|
||||
ret.unrestrictedItems = copy.copy(self.unrestrictedItems)
|
||||
ret.itemLocations = [ ItemLocation(
|
||||
il.Item,
|
||||
copy.copy(il.Location)
|
||||
) for il in self.itemLocations ]
|
||||
ret.sm.addItems([item.Type for item in ret.currentItems])
|
||||
return ret
|
||||
|
||||
# create a new container based on slice predicates on items and
|
||||
# locs. both predicates must result in a consistent container
|
||||
# (same number of unused locations and not placed items)
|
||||
def slice(self, itemPoolCond, locPoolCond):
|
||||
assert self.itemPoolBackup is None, "Cannot slice a constrained container"
|
||||
locs = self.getLocs(locPoolCond)
|
||||
items = self.getItems(itemPoolCond)
|
||||
cont = ItemLocContainer(self.sm, items, locs)
|
||||
cont.currentItems = self.currentItems
|
||||
cont.itemLocations = self.itemLocations
|
||||
return copy.copy(cont)
|
||||
|
||||
# transfer collected items/locations to another container
|
||||
def transferCollected(self, dest):
|
||||
dest.currentItems = self.currentItems[:]
|
||||
dest.sm = SMBoolManager(self.sm.player, self.sm.maxDiff)
|
||||
dest.sm.addItems([item.Type for item in dest.currentItems])
|
||||
dest.itemLocations = copy.copy(self.itemLocations)
|
||||
dest.unrestrictedItems = copy.copy(self.unrestrictedItems)
|
||||
|
||||
# reset collected items/locations. if reassignItemLocs is True,
|
||||
# will re-fill itemPool and unusedLocations as they were before
|
||||
# collection
|
||||
def resetCollected(self, reassignItemLocs=False):
|
||||
self.currentItems = []
|
||||
if reassignItemLocs == False:
|
||||
self.itemLocations = []
|
||||
else:
|
||||
while len(self.itemLocations) > 0:
|
||||
il = self.itemLocations.pop()
|
||||
self.itemPool.append(il.Item)
|
||||
self.unusedLocations.append(il.Location)
|
||||
self.unrestrictedItems = set()
|
||||
self.sm.resetItems()
|
||||
|
||||
def dump(self):
|
||||
return "ItemPool(%d): %s\nLocPool(%d): %s\nCollected: %s" % (len(self.itemPool), getItemListStr(self.itemPool), len(self.unusedLocations), getLocListStr(self.unusedLocations), getItemListStr(self.currentItems))
|
||||
|
||||
# temporarily restrict item pool to items fulfilling predicate
|
||||
def restrictItemPool(self, predicate):
|
||||
assert self.itemPoolBackup is None, "Item pool already restricted"
|
||||
self.itemPoolBackup = self.itemPool
|
||||
self.itemPool = [item for item in self.itemPoolBackup if predicate(item)]
|
||||
self.log.debug("restrictItemPool: "+getItemListStr(self.itemPool))
|
||||
|
||||
# remove a placed restriction
|
||||
def unrestrictItemPool(self):
|
||||
assert self.itemPoolBackup is not None, "No pool restriction to remove"
|
||||
self.itemPool = self.itemPoolBackup
|
||||
self.itemPoolBackup = None
|
||||
self.log.debug("unrestrictItemPool: "+getItemListStr(self.itemPool))
|
||||
|
||||
def removeLocation(self, location):
|
||||
if location in self.unusedLocations:
|
||||
self.unusedLocations.remove(location)
|
||||
|
||||
def removeItem(self, item):
|
||||
self.itemPool.remove(item)
|
||||
if self.itemPoolBackup is not None:
|
||||
self.itemPoolBackup.remove(item)
|
||||
|
||||
# collect an item at a location. if pickup is True, also affects logic (sm) and collectedItems
|
||||
def collect(self, itemLocation, pickup=True):
|
||||
item = itemLocation.Item
|
||||
location = itemLocation.Location
|
||||
if not location.restricted:
|
||||
self.unrestrictedItems.add(item.Type)
|
||||
if pickup == True:
|
||||
self.currentItems.append(item)
|
||||
self.sm.addItem(item.Type)
|
||||
self.removeLocation(location)
|
||||
self.itemLocations.append(itemLocation)
|
||||
self.removeItem(item)
|
||||
|
||||
def isPoolEmpty(self):
|
||||
return len(self.itemPool) == 0
|
||||
|
||||
def getNextItemInPool(self, t):
|
||||
return next((item for item in self.itemPool if item.Type == t), None)
|
||||
|
||||
def getNextItemInPoolMatching(self, predicate):
|
||||
return next((item for item in self.itemPool if predicate(item) == True), None)
|
||||
|
||||
def hasItemTypeInPool(self, t):
|
||||
return any(item.Type == t for item in self.itemPool)
|
||||
|
||||
def hasItemInPool(self, predicate):
|
||||
return any(predicate(item) == True for item in self.itemPool)
|
||||
|
||||
def hasItemCategoryInPool(self, cat):
|
||||
return any(item.Category == cat for item in self.itemPool)
|
||||
|
||||
def getNextItemInPoolFromCategory(self, cat):
|
||||
return next((item for item in self.itemPool if item.Category == cat), None)
|
||||
|
||||
def getAllItemsInPoolFromCategory(self, cat):
|
||||
return [item for item in self.itemPool if item.Category == cat]
|
||||
|
||||
def countItemTypeInPool(self, t):
|
||||
return sum(1 for item in self.itemPool if item.Type == t)
|
||||
|
||||
def countItems(self, predicate):
|
||||
return sum(1 for item in self.itemPool if predicate(item) == True)
|
||||
|
||||
# gets the items pool in the form of a dicitionary whose keys are item types
|
||||
# and values list of items of this type
|
||||
def getPoolDict(self):
|
||||
poolDict = {}
|
||||
for item in self.itemPool:
|
||||
if item.Type not in poolDict:
|
||||
poolDict[item.Type] = []
|
||||
poolDict[item.Type].append(item)
|
||||
return poolDict
|
||||
|
||||
def getLocs(self, predicate):
|
||||
return [loc for loc in self.unusedLocations if predicate(loc) == True]
|
||||
|
||||
def getItems(self, predicate):
|
||||
return [item for item in self.itemPool if predicate(item) == True]
|
||||
|
||||
def getUsedLocs(self, predicate):
|
||||
return [il.Location for il in self.itemLocations if predicate(il.Location) == True]
|
||||
|
||||
def getItemLoc(self, loc):
|
||||
for il in self.itemLocations:
|
||||
if il.Location == loc:
|
||||
return il
|
||||
|
||||
def getCollectedItems(self, predicate):
|
||||
return [item for item in self.currentItems if predicate(item) == True]
|
||||
|
||||
def hasUnrestrictedLocWithItemType(self, itemType):
|
||||
return itemType in self.unrestrictedItems
|
||||
|
||||
def getLocsForSolver(self):
|
||||
locs = []
|
||||
for il in self.itemLocations:
|
||||
loc = il.Location
|
||||
self.log.debug("getLocsForSolver: {}".format(loc.Name))
|
||||
# filter out restricted locations
|
||||
if loc.restricted:
|
||||
self.log.debug("getLocsForSolver: restricted, remove {}".format(loc.Name))
|
||||
continue
|
||||
loc.itemName = il.Item.Type
|
||||
locs.append(loc)
|
||||
return locs
|
||||
|
||||
def cleanLocsAfterSolver(self):
|
||||
# restricted locs can have their difficulty set, which can cause them to be reported in the
|
||||
# post randomization warning message about locs with diff > max diff.
|
||||
for il in self.itemLocations:
|
||||
loc = il.Location
|
||||
if loc.restricted and loc.difficulty == True:
|
||||
loc.difficulty = smboolFalse
|
||||
|
||||
def getDistinctItems(self):
|
||||
itemTypes = {item.Type for item in self.itemPool}
|
||||
return [self.getNextItemInPool(itemType) for itemType in itemTypes]
|
||||
Reference in New Issue
Block a user