Handle fits with invalid ships by removing and deleting them when loaded.
This commit is contained in:
@@ -185,6 +185,12 @@ def getFit(lookfor, eager=None):
|
||||
fit = saveddata_session.query(Fit).options(*eager).filter(Fit.ID == fitID).first()
|
||||
else:
|
||||
raise TypeError("Need integer as argument")
|
||||
|
||||
if fit.isInvalid:
|
||||
with sd_lock:
|
||||
removeInvalid([fit])
|
||||
return None
|
||||
|
||||
return fit
|
||||
|
||||
@cachedQuery(Fleet, 1, "fleetID")
|
||||
@@ -244,9 +250,10 @@ def getFitsWithShip(shipID, ownerID=None, where=None, eager=None):
|
||||
filter = processWhere(filter, where)
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
fits = saveddata_session.query(Fit).options(*eager).filter(filter).all()
|
||||
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).all())
|
||||
else:
|
||||
raise TypeError("ShipID must be integer")
|
||||
|
||||
return fits
|
||||
|
||||
def getBoosterFits(ownerID=None, where=None, eager=None):
|
||||
@@ -264,7 +271,8 @@ def getBoosterFits(ownerID=None, where=None, eager=None):
|
||||
filter = processWhere(filter, where)
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
fits = saveddata_session.query(Fit).options(*eager).filter(filter).all()
|
||||
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).all())
|
||||
|
||||
return fits
|
||||
|
||||
def countAllFits():
|
||||
@@ -295,7 +303,8 @@ def countFitsWithShip(shipID, ownerID=None, where=None, eager=None):
|
||||
def getFitList(eager=None):
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
fits = saveddata_session.query(Fit).options(*eager).all()
|
||||
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).all())
|
||||
|
||||
return fits
|
||||
|
||||
def getFleetList(eager=None):
|
||||
@@ -385,7 +394,8 @@ def searchFits(nameLike, where=None, eager=None):
|
||||
filter = processWhere(Fit.name.like(nameLike, escape="\\"), where)
|
||||
eager = processEager(eager)
|
||||
with sd_lock:
|
||||
fits = saveddata_session.query(Fit).options(*eager).filter(filter).all()
|
||||
fits = removeInvalid(saveddata_session.query(Fit).options(*eager).filter(filter).all())
|
||||
|
||||
return fits
|
||||
|
||||
def getSquadsIDsWithFitID(fitID):
|
||||
@@ -406,6 +416,16 @@ def getProjectedFits(fitID):
|
||||
else:
|
||||
raise TypeError("Need integer as argument")
|
||||
|
||||
def removeInvalid(fits):
|
||||
invalids = [f for f in fits if f.isInvalid]
|
||||
|
||||
if invalids:
|
||||
map(fits.remove, invalids)
|
||||
map(saveddata_session.delete, invalids)
|
||||
saveddata_session.commit()
|
||||
|
||||
return fits
|
||||
|
||||
def add(stuff):
|
||||
with sd_lock:
|
||||
saveddata_session.add(stuff)
|
||||
|
||||
@@ -27,6 +27,7 @@ from math import sqrt, log, asinh
|
||||
from eos.types import Drone, Cargo, Ship, Character, State, Slot, Module, Implant, Booster, Skill
|
||||
from eos.saveddata.module import State
|
||||
from eos.saveddata.mode import Mode
|
||||
import eos.db
|
||||
import time
|
||||
|
||||
try:
|
||||
@@ -47,7 +48,12 @@ class Fit(object):
|
||||
|
||||
PEAK_RECHARGE = 0.25
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, ship=None, name=""):
|
||||
"""Initialize a fit from the program"""
|
||||
# use @mode.setter's to set __attr and IDs. This will set mode as well
|
||||
self.ship = ship
|
||||
|
||||
self.__invalid = False
|
||||
self.__modules = HandledModuleList()
|
||||
self.__drones = HandledDroneCargoList()
|
||||
self.__cargo = HandledDroneCargoList()
|
||||
@@ -58,23 +64,37 @@ class Fit(object):
|
||||
self.__projectedDrones = HandledProjectedDroneList()
|
||||
self.__character = None
|
||||
self.__owner = None
|
||||
self.shipID = None
|
||||
|
||||
self.projected = False
|
||||
self.name = ""
|
||||
self.fleet = None
|
||||
self.boostsFits = set()
|
||||
self.gangBoosts = None
|
||||
self.name = name
|
||||
self.timestamp = time.time()
|
||||
self.ecmProjectedStr = 1
|
||||
self.modeID = None
|
||||
|
||||
self.build()
|
||||
|
||||
@reconstructor
|
||||
def init(self):
|
||||
"""Initialize a drone from the database and validate"""
|
||||
self.__ship = None
|
||||
self.__mode = None
|
||||
self.__invalid = False
|
||||
|
||||
if self.shipID:
|
||||
# if item does not exist, set invalid
|
||||
item = eos.db.getItem(self.shipID)
|
||||
if item is None or item.category.name != "Ship":
|
||||
self.__invalid = True
|
||||
else:
|
||||
self.__ship = Ship(item)
|
||||
|
||||
if self.modeID and self.__ship:
|
||||
item = eos.db.getItem(self.modeID)
|
||||
# Don't need to verify if it's a proper item, as checkModeItem assures this
|
||||
self.__mode = self.ship.checkModeItem(item)
|
||||
|
||||
self.build()
|
||||
|
||||
def build(self):
|
||||
from eos import db
|
||||
self.__extraDrains = []
|
||||
self.__ehp = None
|
||||
self.__weaponDPS = None
|
||||
@@ -99,11 +119,6 @@ class Fit(object):
|
||||
self.ecmProjectedStr = 1
|
||||
self.extraAttributes = ModifiedAttributeDict(self)
|
||||
self.extraAttributes.original = self.EXTRA_ATTRIBUTES
|
||||
self.ship = Ship(db.getItem(self.shipID)) if self.shipID is not None else None
|
||||
if self.ship is not None:
|
||||
self.mode = self.ship.checkModeItem(db.getItem(self.modeID) if self.modeID else None)
|
||||
else:
|
||||
self.mode = None
|
||||
|
||||
@property
|
||||
def targetResists(self):
|
||||
@@ -127,13 +142,17 @@ class Fit(object):
|
||||
self.__ehp = None
|
||||
self.__effectiveTank = None
|
||||
|
||||
@property
|
||||
def isInvalid(self):
|
||||
return self.__invalid
|
||||
|
||||
@property
|
||||
def mode(self):
|
||||
return self._mode
|
||||
return self.__mode
|
||||
|
||||
@mode.setter
|
||||
def mode(self, mode):
|
||||
self._mode = mode
|
||||
self.__mode = mode
|
||||
self.modeID = mode.item.ID if mode is not None else None
|
||||
|
||||
@property
|
||||
|
||||
@@ -19,36 +19,24 @@
|
||||
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
||||
from eos.effectHandlerHelpers import HandledItem
|
||||
import eos.db
|
||||
|
||||
class Mode(ItemAttrShortcut, HandledItem):
|
||||
|
||||
def __init__(self, item):
|
||||
|
||||
if item.group.name != "Ship Modifiers":
|
||||
raise ValueError('Passed item "%s" (category: (%s)) is not a Ship Modifier'%(item.name, item.category.name))
|
||||
|
||||
self.__item = item
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
|
||||
if not isinstance(item, int):
|
||||
self.__buildOriginal()
|
||||
|
||||
def __fetchItemInfo(self):
|
||||
import eos.db
|
||||
self.__item = eos.db.getItem(self.__item)
|
||||
self.__buildOriginal()
|
||||
|
||||
def __buildOriginal(self):
|
||||
self.__itemModifiedAttributes.original = self.item.attributes
|
||||
|
||||
@property
|
||||
def item(self):
|
||||
if isinstance(self.__item, int):
|
||||
self.__fetchItemInfo()
|
||||
|
||||
return self.__item
|
||||
|
||||
@property
|
||||
def itemModifiedAttributes(self):
|
||||
if isinstance(self.__item, int):
|
||||
self.__fetchItemInfo()
|
||||
|
||||
return self.__itemModifiedAttributes
|
||||
|
||||
# @todo: rework to fit only on t3 dessy
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
from eos.modifiedAttributeDict import ModifiedAttributeDict, ItemAttrShortcut
|
||||
from eos.effectHandlerHelpers import HandledItem
|
||||
from eos.saveddata.mode import Mode
|
||||
import eos.db
|
||||
|
||||
class Ship(ItemAttrShortcut, HandledItem):
|
||||
def __init__(self, item):
|
||||
@@ -28,33 +29,18 @@ class Ship(ItemAttrShortcut, HandledItem):
|
||||
raise ValueError('Passed item "%s" (category: (%s)) is not under Ship category'%(item.name, item.category.name))
|
||||
|
||||
self.__item = item
|
||||
self.__modeItems = self.__getModeItems()
|
||||
self.__itemModifiedAttributes = ModifiedAttributeDict()
|
||||
self.__modeItems = self._getModeItems()
|
||||
if not isinstance(item, int):
|
||||
self.__buildOriginal()
|
||||
self.__itemModifiedAttributes.original = self.item.attributes
|
||||
|
||||
self.commandBonus = 0
|
||||
|
||||
def __fetchItemInfo(self):
|
||||
import eos.db
|
||||
self.__item = eos.db.getItem(self.__item)
|
||||
self.__buildOriginal()
|
||||
|
||||
def __buildOriginal(self):
|
||||
self.__itemModifiedAttributes.original = self.item.attributes
|
||||
|
||||
@property
|
||||
def item(self):
|
||||
if isinstance(self.__item, int):
|
||||
self.__fetchItemInfo()
|
||||
|
||||
return self.__item
|
||||
|
||||
@property
|
||||
def itemModifiedAttributes(self):
|
||||
if isinstance(self.__item, int):
|
||||
self.__fetchItemInfo()
|
||||
|
||||
return self.__itemModifiedAttributes
|
||||
|
||||
def clear(self):
|
||||
@@ -96,21 +82,17 @@ class Ship(ItemAttrShortcut, HandledItem):
|
||||
def modes(self):
|
||||
return [Mode(item) for item in self.__modeItems] if self.__modeItems else None
|
||||
|
||||
def _getModeItems(self):
|
||||
def __getModeItems(self):
|
||||
"""
|
||||
Returns a list of valid mode items for ship. Note that this returns the
|
||||
valid Item objects, not the Mode objects. Returns None if not a
|
||||
t3 dessy
|
||||
"""
|
||||
# @todo: is there a better way to determine this that isn't hardcoded groupIDs?
|
||||
if self.item.groupID != 1305:
|
||||
if self.item.group.name != "Tactical Destroyer":
|
||||
return None
|
||||
|
||||
modeGroupID = 1306
|
||||
import eos.db
|
||||
|
||||
items = []
|
||||
g = eos.db.getGroup(modeGroupID, eager=("items.icon", "items.attributes"))
|
||||
g = eos.db.getGroup("Ship Modifiers", eager=("items.icon", "items.attributes"))
|
||||
for item in g.items:
|
||||
# Rely on name detection because race is not reliable
|
||||
if item.name.lower().startswith(self.item.name.lower()):
|
||||
|
||||
223
eos/slotFill.py
223
eos/slotFill.py
@@ -1,223 +0,0 @@
|
||||
#===============================================================================
|
||||
# Copyright (C) 2010 Diego Duclos
|
||||
#
|
||||
# This file is part of eos.
|
||||
#
|
||||
# eos is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# eos is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with eos. If not, see <http://www.gnu.org/licenses/>.
|
||||
#===============================================================================
|
||||
|
||||
from eos.types import Slot, Fit, Module, State
|
||||
import random
|
||||
import copy
|
||||
import math
|
||||
import bisect
|
||||
import itertools
|
||||
import time
|
||||
|
||||
class SlotFill(object):
|
||||
def __init__(self, original, modules, attributeWeights=None, propertyWeights=None, specificWeights=None, defaultState = State.ACTIVE):
|
||||
self.original = original
|
||||
self.attributeWeights = attributeWeights or {}
|
||||
self.propertyWeights = propertyWeights or {}
|
||||
self.specificWeights = specificWeights or []
|
||||
self.state = State.ACTIVE
|
||||
self.modules = map(self.__newModule, modules)
|
||||
|
||||
def __newModule(self, item):
|
||||
m = Module(item)
|
||||
m.state = self.state
|
||||
return m
|
||||
|
||||
def __getMetaParent(self, item):
|
||||
metaGroup = item.metaGroup
|
||||
return item if metaGroup is None else metaGroup.parent
|
||||
|
||||
def fitness(self, fit, chromosome):
|
||||
modList = fit.modules
|
||||
modAttr = fit.ship.getModifiedItemAttr
|
||||
|
||||
modList.extend(chromosome)
|
||||
fit.clear()
|
||||
fit.calculateModifiedAttributes()
|
||||
|
||||
if not fit.fits:
|
||||
del modList[-len(chromosome):]
|
||||
return 0
|
||||
|
||||
weight = 0
|
||||
for attr, value in self.attributeWeights.iteritems():
|
||||
weight += modAttr(attr) * (value if value >= 0 else 1.0 / -value)
|
||||
|
||||
for prop, value in self.propertyWeights.iteritems():
|
||||
weight += getattr(fit, prop) * (value if value >= 0 else 1.0 / -value)
|
||||
|
||||
for specific in self.specificWeights:
|
||||
weight += specific(fit)
|
||||
|
||||
totalVars = (fit.ship.getModifiedItemAttr("powerOutput"),
|
||||
fit.ship.getModifiedItemAttr("cpuOutput"),
|
||||
fit.ship.getModifiedItemAttr('upgradeCapacity'))
|
||||
|
||||
usedVars = (fit.pgUsed, fit.cpuUsed, fit.calibrationUsed)
|
||||
|
||||
total = 0
|
||||
used = 0
|
||||
for tv, uv in zip(totalVars, usedVars):
|
||||
if uv > tv:
|
||||
del modList[-len(chromosome):]
|
||||
return 0
|
||||
|
||||
del modList[-len(chromosome):]
|
||||
|
||||
|
||||
return weight
|
||||
|
||||
|
||||
def run(self, elite = 0.05, crossoverChance = 0.8, slotMutationChance = 0.5, typeMutationChance = 0.5):
|
||||
#Use a copy of the original for all our calcs. We don't want to damage it
|
||||
fit = copy.deepcopy(self.original)
|
||||
fit.unfill()
|
||||
|
||||
#First of all, lets check the number of slots we got to play with
|
||||
chromLength = -1
|
||||
slotAmounts = {}
|
||||
for type in Slot.getTypes():
|
||||
slot = Slot.getValue(type)
|
||||
amount = fit.getSlotsFree(slot)
|
||||
if amount > 0:
|
||||
slotAmounts[slot] = amount
|
||||
|
||||
chromLength += amount
|
||||
|
||||
if not slotAmounts:
|
||||
#Nothing to do, joy
|
||||
return
|
||||
|
||||
slotModules = {}
|
||||
metaModules = {}
|
||||
|
||||
for slotType in slotAmounts:
|
||||
slotModules[slotType] = modules = []
|
||||
|
||||
for module in self.modules:
|
||||
#Store the variations of each base for ease and speed
|
||||
metaParent = self.__getMetaParent(module.item)
|
||||
metaList = metaModules.get(metaParent)
|
||||
if metaList is None:
|
||||
metaList = metaModules[metaParent] = []
|
||||
metaList.append(module)
|
||||
|
||||
#Sort stuff by slotType for ease and speed
|
||||
slot = module.slot
|
||||
if slot in slotModules:
|
||||
slotModules[slot].append(module)
|
||||
|
||||
for slotType, modules in slotModules.iteritems():
|
||||
if len(modules) == 0:
|
||||
chromLength -= slotAmounts[slotType]
|
||||
del slotAmounts[slotType]
|
||||
|
||||
#Now, we need an initial set, first thing to do is decide how big that set will be
|
||||
setSize = 10
|
||||
|
||||
#Grab some variables locally for performance improvements
|
||||
rchoice = random.choice
|
||||
rrandom = random.random
|
||||
rrandint = random.randint
|
||||
bbisect = bisect.bisect
|
||||
ccopy = copy.copy
|
||||
|
||||
#Get our list for storage of our chromosomes
|
||||
chromosomes = []
|
||||
|
||||
# Helpers
|
||||
weigher = lambda chromosome: (self.fitness(fit, chromosome), chromosome)
|
||||
keyer = lambda info: info[0]
|
||||
|
||||
eliteCutout = int(math.floor(setSize * (1 - elite)))
|
||||
lastEl = setSize - 1
|
||||
|
||||
#Generate our initial set entirely randomly
|
||||
#Subtelies to take in mind:
|
||||
# * modules of the same slotType are kept together for easy cross-overing
|
||||
state = self.state
|
||||
for _ in xrange(setSize):
|
||||
chrom = []
|
||||
for type, amount in slotAmounts.iteritems():
|
||||
for _ in xrange(amount):
|
||||
chrom.append(rchoice(slotModules[type]))
|
||||
|
||||
chromosomes.append(weigher(chrom))
|
||||
|
||||
#Sort our initial set
|
||||
chromosomes.sort(key=keyer)
|
||||
currentGeneration = chromosomes
|
||||
|
||||
#Yield the best result from our initial set, this is gonna be pretty bad
|
||||
yield currentGeneration[lastEl]
|
||||
|
||||
#Setup's done, now we can actualy apply our genetic algorithm to optimize all this
|
||||
while True:
|
||||
moo = time.time()
|
||||
#First thing we do, we're gonna be elitair
|
||||
#Grab the top x%, we'll put em in the next generation
|
||||
nextGeneration = []
|
||||
for i in xrange(lastEl, eliteCutout - 1, -1):
|
||||
nextGeneration.append(currentGeneration[i])
|
||||
|
||||
#Figure out our ratios to do our roulette wheel
|
||||
fitnessList = map(keyer, currentGeneration)
|
||||
totalFitness = float(sum(fitnessList))
|
||||
|
||||
curr = 0
|
||||
ratios = []
|
||||
for fitness in fitnessList:
|
||||
curr += fitness
|
||||
ratios.append(curr / (totalFitness or 1))
|
||||
|
||||
t = 0
|
||||
#Do our pairing
|
||||
for _ in xrange(0, eliteCutout):
|
||||
# Crossover chance
|
||||
mother = currentGeneration[bbisect(ratios, rrandom())][1]
|
||||
father = currentGeneration[bbisect(ratios, rrandom())][1]
|
||||
if rrandom() <= crossoverChance:
|
||||
crosspoint = rrandint(0, chromLength)
|
||||
luke = mother[:crosspoint] + father[crosspoint:]
|
||||
else:
|
||||
luke = father
|
||||
|
||||
#Chance for slot mutation
|
||||
if rrandom() <= slotMutationChance:
|
||||
target = rrandint(0, chromLength)
|
||||
mod = luke[target]
|
||||
luke[target] = rchoice(slotModules[mod.slot])
|
||||
|
||||
if rrandom() <= typeMutationChance:
|
||||
#Mutation of an item to another one of the same type
|
||||
target = rrandint(0, chromLength)
|
||||
mod = luke[target]
|
||||
vars = metaModules[self.__getMetaParent(mod.item)]
|
||||
luke[target] = rchoice(vars)
|
||||
|
||||
tt = time.time()
|
||||
nextGeneration.append(weigher(luke))
|
||||
t += time.time() - tt
|
||||
|
||||
print "time spent weighing: ", t
|
||||
|
||||
nextGeneration.sort(key=keyer)
|
||||
currentGeneration = nextGeneration
|
||||
print "total time spent this iteration:", time.time() - moo
|
||||
yield currentGeneration[lastEl]
|
||||
@@ -149,8 +149,8 @@ class Fit(object):
|
||||
return fit.modules[pos]
|
||||
|
||||
def newFit(self, shipID, name=None):
|
||||
fit = eos.types.Fit()
|
||||
fit.ship = eos.types.Ship(eos.db.getItem(shipID))
|
||||
ship = eos.types.Ship(eos.db.getItem(shipID))
|
||||
fit = eos.types.Fit(ship)
|
||||
fit.name = name if name is not None else "New %s" % fit.ship.item.name
|
||||
fit.damagePattern = self.pattern
|
||||
fit.targetResists = self.targetResists
|
||||
|
||||
Reference in New Issue
Block a user