Commits

Neil Muller committed 652ad60

Add script for extracting some interesting info from the TWD database

  • Participants
  • Parent commits f93802a

Comments (0)

Files changed (1)

TWDA/twd_stats.py

+#!/usr/bin/python
+
+"""Script to extract 'useful' stats from the TWD"""
+
+import sys
+import optparse
+try:
+    from sutekh.core.SutekhObjects import Clan, IKeyword
+    from sutekh.SutekhUtility import is_crypt_card
+    from twd_utils import (start_twd_db, select_years,
+            card_sets_with_progress, get_twd_url, sTWDParPrefix)
+except ImportError:
+    print 'Failed to import sutekh & twd_utils - is PYTHONPATH set correctly?'
+    sys.exit(1)
+
+
+def parse_options(aArgs):
+    """Handle option parsing"""
+    sHelpText = "usage: %prog [year] [year2] ... [options]\n\n" + \
+            "Finds stats about underused cards in the TWDA. "
+    oOptParser = optparse.OptionParser(usage=sHelpText,
+            version="%prog 0.1")
+    oOptParser.add_option("-n", "--number", dest="number", default=5,
+            help="Number of crypt cards from a single clan required to qualify"
+            " as a card set of that clan")
+    oOptParser.add_option("-s", "--super", dest="sup", default=4,
+            help="Number of copies of a single crypt card required to be"
+            " considered a 'superstar' deck")
+    oOptParser.add_option("-d", "--details", dest="details",
+            action="store_true", default=False,
+            help="List deck names for each superstar")
+    oOptParser.add_option("-u", "--urls", dest="urls",
+            action="store_true", default=False,
+            help="List deck urls for each superstar")
+    oOptParser.add_option("-m", "--multiclan", action="store_true",
+            dest="multi", default=False, help="Count cards as multi-clan for"
+            " all clans above X (default is only to do so if there's no"
+            " clear maximum clan")
+    (oOpts, aRemaining) = oOptParser.parse_args(aArgs)
+    return oOpts, aRemaining[1:]
+
+
+def find_clan_stats(aParCS, iNum, bMult):
+    """Find the clan stats"""
+
+    dClans = {}
+    for sClan in [x.name for x in Clan.select()] + ['Imbued']:
+        dClans[sClan] = 0
+    dMult = {}
+    for oCS in card_sets_with_progress(79):
+        if oCS.parent.id not in aParCS:
+            continue
+        aCrypt = [x.abstractCard for x in oCS.cards
+                if is_crypt_card(x.abstractCard)]
+        dCSClans = {}
+        for oCard in aCrypt:
+            if oCard.clan:
+                sClan = [x.name for x in oCard.clan][0]
+            elif oCard.creed:
+                sClan = 'Imbued'
+            else:
+                raise RuntimeError('Unknown clan / creed')
+            dCSClans.setdefault(sClan, 0)
+            dCSClans[sClan] += 1
+        iMax = 0
+        aClans = []
+        for sClan, iClanNum in dCSClans.iteritems():
+            if iClanNum > iMax:
+                iMax = iClanNum
+                if not bMult and iClanNum >= iNum:
+                    aClans = [sClan]
+            elif iClanNum == iMax and not bMult and iClanNum >= iNum:
+                aClans.append(sClan)
+            if bMult and iClanNum >= iNum:
+                dClans[sClan] += 1  # One more deck
+                aClans.append(sClan)
+        if not bMult and iMax >= iNum:
+            if len(aClans) == 1:
+                dClans[aClans[0]] += 1
+            else:
+                for sClan in aClans:
+                    dClans[sClan] += 1
+        if len(aClans) > 1:
+            sSet = ', '.join(aClans)
+            dMult.setdefault(sSet, 0)
+            dMult[sSet] += 1
+    return dClans, dMult
+
+
+def get_super_star(oCardSet, iSuper):
+    """Extract the crypt numbers from the card set"""
+    oNonUnique = IKeyword('non-unique')
+    aCrypt = [x.abstractCard for x in oCardSet.cards
+            if is_crypt_card(x.abstractCard)]
+    dCrypt = {}
+    for oCard in aCrypt:
+        sName = oCard.name
+        sName = sName.replace(' (Advanced)', '')
+        # We skip non-unique and 'Anarch Convert' from consideration
+        if oNonUnique in oCard.keywords:
+            continue
+        if sName == 'Anarch Convert':
+            continue
+        dCrypt.setdefault(sName, 0)
+        dCrypt[sName] += 1
+    aTotals = sorted(dCrypt.items(), key=lambda x: -x[1])
+    iMax = aTotals[0][1]
+    if iMax < iSuper:
+        return None
+    if len(aTotals) > 1 and aTotals[1][1] == iMax:
+        # Not superstar, multiple vampires have that number
+        return None
+    return aTotals[0][0]
+
+
+def find_super_stars_by_year(aParCS, iSuper):
+    """Divide super star decks by year"""
+    dSuperStarByYear = {}
+    for oCS in card_sets_with_progress(79):
+        if oCS.parent.id not in aParCS:
+            continue
+        sName = get_super_star(oCS, iSuper)
+        if sName:
+            sYear = oCS.parent.name.replace(sTWDParPrefix, '')
+            dSuperStarByYear.setdefault(sYear, 0)
+            dSuperStarByYear[sYear] += 1
+    return dSuperStarByYear
+
+
+def find_super_star_stats(aParCS, iSuper, bDetails, bUrls):
+    dSuperStar = {}
+    dDetails = {}
+    dUrls = {}
+    for oCS in card_sets_with_progress(79):
+        if oCS.parent.id not in aParCS:
+            continue
+        # We define a superstar as follows
+        # unique (excludes Hermana & tupdog decks)
+        # iSuper or more copies in the crypt
+        # More copies than anyone else, except for non-unique or
+        # anarch converts
+        sName = get_super_star(oCS, iSuper)
+        if not sName:
+            continue
+        dSuperStar.setdefault(sName, 0)
+        dSuperStar[sName] += 1
+        if bDetails:
+            dDetails.setdefault(sName, [])
+            dDetails[sName].append(oCS.name)
+        if bUrls:
+            dUrls.setdefault(sName, [])
+            sUrl = get_twd_url(oCS.annotations)
+            if sUrl:
+                dUrls[sName].append(sUrl)
+    return dSuperStar, dDetails, dUrls
+
+
+def print_dict(dInfo, sType='Unknown'):
+    print 'Stats for %s' % sType
+    print '============'
+    print
+    try:
+        aInfo = sorted(dInfo.items(), key=lambda x: (-x[1], x[0]))
+        for sName, iNum in aInfo:
+            if iNum > 1:
+                sPlural = 'decks'
+            else:
+                sPlural = 'deck'
+            print ' %45s : %3d %s' % (sName, iNum, sPlural)
+    except TypeError:
+        for sName, aDetails in sorted(dInfo.items()):
+            print ' %40s :' % sName
+            print '\n'.join(['%50s' % x.encode('utf8') for x in aDetails])
+    print
+
+
+if __name__ == "__main__":
+    if not start_twd_db():
+        print 'Environment not initialised'
+        sys.exit(1)
+    oOpts, aYears = parse_options(sys.argv)
+    aParCS, iLowYear, iHighYear = select_years(aYears)
+    if not aParCS:
+        print 'Empty year card set list'
+        sys.exit(1)
+    dClans, dMulti = find_clan_stats(aParCS, int(oOpts.number), oOpts.multi)
+    dSuperStars, dDetails, dUrls = \
+            find_super_star_stats(aParCS, int(oOpts.sup), oOpts.details,
+            oOpts.urls)
+    dSuperStarsByYear = find_super_stars_by_year(aParCS, int(oOpts.sup))
+    print_dict(dClans, 'Clans')
+    print_dict(dMulti, 'Multi-clan')
+    print_dict(dSuperStars, 'Superstars')
+    if dDetails:
+        print_dict(dDetails, 'Superstar card set names')
+    if dUrls:
+        print_dict(dUrls, 'Superstar card set urls')
+    print_dict(dSuperStarsByYear, 'Superstars by Year')