Rezzo64

BuilderAnalyzer Updated main.py

Oct 21st, 2021
315
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 109.72 KB | None | 0 0
  1. ### Written by vapicuno, 2019/06/13 v6
  2. ### python 3.5
  3. ### Takes in fin which is in showdown teambuilder format
  4. ### Spits out sets list by gen, builder by gen, stats by gen, and combined builder.  
  5. ### Sorting order is determined by parameters that can be set
  6.  
  7. from itertools import combinations
  8. import copy
  9. import urllib.request
  10. import json
  11. import math
  12. import operator
  13.  
  14. ######## PARAMETERS FOR TUNING TO YOUR LIKING ########
  15.  
  16. #### --- REPLACE WITH YOUR BUILDER --- ####
  17. fin = 'my_builder.txt'
  18.  
  19. ### DOWNLOAD LATEST POKEDEX
  20. downloadPokedex = False
  21.  
  22. #### METAGAME PARAMETERS
  23. allGenerations = True
  24. generation = ['gen1ou, gen3ou']
  25.  
  26. ### UNFINISHED TEAMS
  27. anomalyThreshold = 0
  28. includeIncompleteTeams = True
  29.  
  30. #### SETS PARAMETERS (will only affect sets list, not sorted builder)
  31. ### --- SET COMBINING
  32. EVthreshold = 40
  33. IVthreshold = 999
  34. combineMoves = 2
  35. ### --- MOVE SORT
  36. sortMovesByFrequency = -1
  37. sortMovesByAlphabetical = 0
  38. ### --- DISPLAY
  39. showShiny = True
  40. showIVs = False
  41. showNicknames = True
  42. ignoreSetsFraction = [1/8,1/16,1/32,0]
  43. showStatisticsInSets = True
  44. printArchetypeLabel = False
  45.  
  46. #### CORE STATISTICS PARAMETERS
  47. maxCoreNum = 4
  48. usageWeight = [1,1.5,1.5,1.5,2,2]
  49. importantItems = ['Choice Band','Choice Scarf','Choice Specs','Assault Vest','Rocky Helmet','Z']
  50. natureEVmodifier = 120
  51. movePairSynergyThreshold = 1/6
  52. movePairInTripletSynergyThreshold = 1/6
  53. moveTripletSynergyThreshold = 1/6
  54. moveProbThreshold = 0.2
  55. moveProbInTripletThreshold = 0.1
  56. moveCountThreshold = 2
  57. sumMoveProbThreshold = 0.8
  58. sumMoveProbTripletThreshold = 0.8
  59. namingIgnoreCategoryNum = 2
  60. namingExclusionMoveThreshold = 1/4 * 0.25
  61. namingMinMoveProb = 1/4 * 0.8
  62. namingExclusionCatThreshold = 0.1
  63. showMissingMonCores = False
  64. showMissingSetCores = False
  65. maxMissingMonCores = 2
  66. maxMissingSetCores = 2
  67.  
  68. ### ARCHETYPE STATISTICS PARAMETERS
  69. analyzeArchetypes = True
  70. exponent = 1.6
  71. gammaSpectral = 2
  72. numArchetypesRange = 15
  73. numArchetypes = 8
  74. gammaArchetypes = 0.7
  75.  
  76. #### COMBINED BUILDER PARAMETERS
  77. sortBuilder = True
  78. ### --- METAGAME-SORTING
  79. sortGenByFrequency = -1
  80. sortGenByAlphabetical = 0
  81. ### --- FOLDER SORTING WITHIN GENERATION
  82. sortFolderByFrequency = 0 # -1
  83. sortFolderByAlphabetical = 0
  84. ### --- TEAM SORTING WITHIN FOLDER
  85. sortTeamsByArchetype = 0
  86. gammaTeamAssignment = 2
  87. metricArchetypes = 1
  88. sortTeamsByLeadFrequencyTeamPreview = 0
  89. sortTeamsByLeadFrequencyNoTeamPreview = -1
  90. sortTeamsByCore = -1
  91. sortTeamsByAlphabetical = 0
  92. coreNumber = 2
  93. ### --- POKEMON SORTING WITHIN TEAMS
  94. sortMonsByFrequency = -1
  95. sortMonsByColor = False
  96. gamma = 1
  97.  
  98. ######## PARAMETERS END HERE ########
  99.  
  100. if analyzeArchetypes:
  101.     import numpy as np
  102.     import skfuzzy as fuzz
  103.     import matplotlib.pyplot as plt
  104.  
  105. if downloadPokedex:
  106.     print('Beginning pokedex download...')
  107.     urlPokedex = 'https://raw.githubusercontent.com/smogon/pokemon-showdown/master/data/pokedex.ts'
  108.     urllib.request.urlretrieve(urlPokedex, 'pokedex.ts')
  109.     print('Beginning items download...')
  110.     urlItems = 'https://raw.githubusercontent.com/smogon/pokemon-showdown/master/data/items.ts'
  111.     urllib.request.urlretrieve(urlItems, 'items.ts')
  112.     print('Beginning moves download...')
  113.     urlMoves = 'https://raw.githubusercontent.com/smogon/pokemon-showdown/master/data/moves.ts'
  114.     urllib.request.urlretrieve(urlMoves, 'moves.ts')
  115.     print('Beginning abilities download...')
  116.     urlAbilities = 'https://raw.githubusercontent.com/smogon/pokemon-showdown/master/data/abilities.ts'
  117.     urllib.request.urlretrieve(urlAbilities, 'abilities.ts')
  118.    
  119.  
  120. pokedexFile = open('pokedex.ts')
  121. pokedexStr = pokedexFile.read()
  122. itemsFile = open('items.ts')
  123. itemsStr = itemsFile.read()
  124. movesFile = open('moves.ts')
  125. movesStr = movesFile.read()
  126. abilitiesFile = open('abilities.ts')
  127. abilitiesStr = abilitiesFile.read()
  128. if sortMonsByColor:
  129.     colorsFile = open('colors.js')
  130.     colorsStr = colorsFile.read()
  131.     colors = json.loads(colorsStr)
  132.  
  133. ## Extracts set from text and puts it into a dict
  134. def ExtractSet(setText,inputFormatDense,pokedexStr,itemsStr,abilitiesStr,movesStr):
  135.     setDict = {}
  136.     # for statistics
  137.     setDict['SharedMoves1'] = dict()
  138.     setDict['SharedMoves2'] = dict()
  139.     # initialize gender, nickname, item, name, moves and EVs
  140.     setDict['Gender'] = ''
  141.     setDict['Nickname'] = ''
  142.     setDict['Item'] = ''
  143.     setDict['Ability'] = ''
  144.     setDict['Nature'] = ''
  145.     setDict['Shiny'] = False
  146.     setDict['Moveset'] = list()
  147.     setDict['EVs'] = [0,0,0,0,0,0]
  148.     setDict['IVs'] = [31,31,31,31,31,31]
  149.     setDict['Level'] = 100
  150.     setDict['Happiness'] = 255
  151.     setDict['AlternateForm'] = ''
  152.    
  153.     if inputFormatDense:
  154.         indexDelimiter2 = setText.find('|')
  155.         setDict['Nickname'] = setText[0:indexDelimiter2]
  156.        
  157.         indexDelimiter1 = indexDelimiter2
  158.         indexDelimiter2 = setText.find('|', indexDelimiter1+1)
  159.         if indexDelimiter1 + 1 < indexDelimiter2:
  160.             parseName = setText[indexDelimiter1+1:indexDelimiter2]
  161.             indexNameKey = pokedexStr.find(parseName+': ')
  162.             if indexNameKey == -1: # Alternate Form
  163.                 indexNameKey = pokedexStr.find(parseName)
  164.                 if indexNameKey == -1:
  165.                     print('Warning: Pokemon name of '.encode('utf-8') + parseName.encode('utf-8','ignore') + ' not found'.encode('utf-8'))
  166.                 indexSpecies = pokedexStr.rfind('species: ',0,indexNameKey)
  167.                 indexNameKeyBase2 = pokedexStr.rfind('{',0,indexSpecies) - 2
  168.                 indexNameKeyBase1 = pokedexStr.rfind('\t',0,indexNameKeyBase2) + 1
  169.                 nameKeyBase = pokedexStr[indexNameKeyBase1:indexNameKeyBase2]
  170.                 # Assume that Alternate Forms are Base Form + '-' + Descriptor
  171.                 setDict['AlternateForm'] = parseName[len(nameKeyBase):].capitalize()
  172.             else:
  173.                 indexSpecies = pokedexStr.find('species: ',indexNameKey)
  174.             indexName1 = pokedexStr.find('"',indexSpecies)
  175.             indexName2 = pokedexStr.find('"',indexName1+1)
  176.             setDict['Name'] = pokedexStr[indexName1+1:indexName2]
  177.             if setDict['AlternateForm'] != '':
  178.                 setDict['Name'] += '-' + setDict['AlternateForm']
  179.         else:
  180.             setDict['Name'] = setDict['Nickname']
  181.             setDict['Nickname'] = ''
  182.             indexName1 = pokedexStr.find(setDict['Name'])
  183.             if indexName1 == -1:
  184.                 indexHyphen = setDict['Name'].find('-')
  185.                 indexName1 = pokedexStr.find('"'+setDict['Name'][0:indexHyphen]+'"')
  186.                 if indexName1 == -1:
  187.                     print('Warning: Pokemon base form of '.encode('utf-8') + setDict['Name'].encode('utf-8','ignore') + ' not found'.encode('utf-8'))
  188.                 setDict['AlternateForm'] = setDict['Name'][indexHyphen+1:]
  189.            
  190.         indexDelimiter1 = indexDelimiter2
  191.         indexDelimiter2 = setText.find('|', indexDelimiter1+1)
  192.         if indexDelimiter1 + 1 < indexDelimiter2:
  193.             parseItem = setText[indexDelimiter1+1:indexDelimiter2]
  194.             indexItemKey = itemsStr.find('"'+parseItem+'": ')
  195.             indexItemName = itemsStr.find('name: ',indexItemKey)
  196.             indexItem1 = itemsStr.find('"',indexItemName)
  197.             indexItem2 = itemsStr.find('"',indexItem1+1)
  198.             setDict['Item'] = itemsStr[indexItem1+1:indexItem2]
  199.        
  200.         indexDelimiter1 = indexDelimiter2
  201.         indexDelimiter2 = setText.find('|', indexDelimiter1+1)
  202.         parseAbility = setText[indexDelimiter1+1:indexDelimiter2]
  203.         if parseAbility == '':
  204.             parseAbility == '0'
  205.         if len(parseAbility) <= 1:
  206.             if 'indexNameKey' in locals():
  207.                 indexNameAbilities = indexNameKey
  208.                 if setDict['AlternateForm'] != '':
  209.                     indexAbilities = pokedexStr.rfind('abilities: ',0,indexNameAbilities)
  210.                 else:
  211.                     indexAbilities = pokedexStr.find('abilities: ',indexNameAbilities)
  212.             else:
  213.                 indexNameAbilities = pokedexStr.find('"'+setDict['Name']+'"')
  214.                 if indexNameAbilities == -1:
  215.                     indexHyphen = setDict['Name'].find('-')
  216.                     if indexHyphen == -1:
  217.                         print('Warning: Pokemon base form of '.encode('utf-8') + setDict['Name'].encode('utf-8','ignore') + ' not found'.encode('utf-8'))
  218.                     indexNameAbilities = pokedexStr.find('"'+setDict['Name'][0:indexHyphen]+'"')
  219.                 indexAbilities = pokedexStr.find('abilities: ',indexNameAbilities)
  220.             indexAbility = pokedexStr.find(parseAbility+': ',indexAbilities)
  221.             indexAbility1 = pokedexStr.find('"',indexAbility)
  222.             indexAbility2 = pokedexStr.find('"',indexAbility1+1)
  223.             setDict['Ability'] = pokedexStr[indexAbility1+1:indexAbility2]
  224.         elif parseAbility == 'none':
  225.             setDict['Ability'] = 'none'
  226.         else:
  227.             indexAbilityKey = abilitiesStr.find('"'+parseAbility+'": ')
  228.             if indexAbilityKey == -1:
  229.                 setDict['Ability'] = parseAbility
  230.             else:
  231.                 indexAbility = abilitiesStr.find('name: ',indexAbilityKey)
  232.                 indexAbility1 = abilitiesStr.find('"',indexAbility)
  233.                 indexAbility2 = abilitiesStr.find('"',indexAbility1+1)
  234.                 setDict['Ability'] = abilitiesStr[indexAbility1+1:indexAbility2]
  235.        
  236.         indexDelimiter1 = indexDelimiter2
  237.         indexDelimiter2 = setText.find('|', indexDelimiter1+1)
  238.         if indexDelimiter1 + 1 < indexDelimiter2:
  239.             indexMove1 = indexDelimiter1
  240.             while indexMove1 < indexDelimiter2:
  241.                 indexMove2 = setText.find(',',indexMove1+1,indexDelimiter2)
  242.                 if indexMove2 == -1:
  243.                     indexMove2 = indexDelimiter2
  244.                 parseMove = setText[indexMove1+1:indexMove2]
  245.                 indexMoveKey = movesStr.find('"'+parseMove+'": ')
  246.                 if indexMoveKey == -1:
  247.                     indexMove1 = indexMove2
  248.                     continue
  249.                 indexMoveName = movesStr.find('name: ',indexMoveKey)
  250.                 indexMoveName1 = movesStr.find('"',indexMoveName)
  251.                 indexMoveName2 = movesStr.find('"',indexMoveName1+1)
  252.                 setDict['Moveset'].append(movesStr[indexMoveName1+1:indexMoveName2])
  253.                 indexMove1 = indexMove2
  254.        
  255.         indexDelimiter1 = indexDelimiter2
  256.         indexDelimiter2 = setText.find('|', indexDelimiter1+1)
  257.         if indexDelimiter1 + 1 < indexDelimiter2:
  258.             setDict['Nature'] = setText[indexDelimiter1+1:indexDelimiter2]
  259.        
  260.         indexDelimiter1 = indexDelimiter2
  261.         indexDelimiter2 = setText.find('|', indexDelimiter1+1)
  262.         if indexDelimiter1 + 2 < indexDelimiter2: # 2 takes into account sometimes storing 0 EVs as single 0
  263.             indexEV1 = indexDelimiter1
  264.             for n in range(0,6):
  265.                 if n < 5:
  266.                     indexEV2 = setText.find(',',indexEV1+1,indexDelimiter2)
  267.                 else:
  268.                     indexEV2 = indexDelimiter2
  269.                 EV = setText[indexEV1+1:indexEV2]
  270.                 if EV == '':
  271.                     EV = 0
  272.                 setDict['EVs'][n] = int(EV)
  273.                 indexEV1 = indexEV2
  274.        
  275.         indexDelimiter1 = indexDelimiter2
  276.         indexDelimiter2 = setText.find('|', indexDelimiter1+1)
  277.         if indexDelimiter1 + 1 < indexDelimiter2:
  278.             setDict['Gender'] = setText[indexDelimiter1+1:indexDelimiter2]
  279.        
  280.         indexDelimiter1 = indexDelimiter2
  281.         indexDelimiter2 = setText.find('|', indexDelimiter1+1)
  282.         if indexDelimiter1 + 1 < indexDelimiter2:
  283.             indexIV1 = indexDelimiter1
  284.             for n in range(0,6):
  285.                 if n < 5:
  286.                     indexIV2 = setText.find(',',indexIV1+1,indexDelimiter2)
  287.                 else:
  288.                     indexIV2 = indexDelimiter2
  289.                 IV = setText[indexIV1+1:indexIV2]
  290.                 if IV == '':
  291.                     IV = 31
  292.                 setDict['IVs'][n] = int(IV)
  293.                 indexIV1 = indexIV2
  294.            
  295.         indexDelimiter1 = indexDelimiter2
  296.         indexDelimiter2 = setText.find('|', indexDelimiter1+1)
  297.         if indexDelimiter1 + 1 < indexDelimiter2:
  298.             setDict['Shiny'] = (setText[indexDelimiter1+1:indexDelimiter2] == 'S')
  299.        
  300.         indexDelimiter1 = indexDelimiter2
  301.         indexDelimiter2 = setText.find('|', indexDelimiter1+1)
  302.         if indexDelimiter1 + 1 < indexDelimiter2:
  303.             level = setText[indexDelimiter1+1:indexDelimiter2]
  304.             if level == '':
  305.                 setDict['Level'] = 100
  306.             else:
  307.                 setDict['Level'] = int(level)
  308.            
  309.         indexDelimiter1 = indexDelimiter2
  310.         happiness = setText[indexDelimiter1+1:]
  311.         if indexDelimiter1 + 1 < indexDelimiter2:
  312.             if level == '':
  313.                 setDict['Happiness'] = 255
  314.             else:
  315.                 setDict['Happiness'] = int(happiness)
  316.     else:
  317.         stat2index = {
  318.         'HP' : 0,
  319.         'Atk' : 1,
  320.         'Def' : 2,
  321.         'SpA' : 3,
  322.         'SpD' : 4,
  323.         'Spe' : 5
  324.         }
  325.         pos2 = max(setText.rfind(' (F) @ '),setText.rfind(' (F)  \n'))
  326.         if pos2 > -1:
  327.             setDict['Gender'] = 'F'
  328.         else:
  329.             pos2 = max(setText.rfind(' (M) @ '),setText.rfind(' (M)  \n'))
  330.             if pos2 > -1:
  331.                 setDict['Gender'] = 'M'
  332.             else:
  333.                 pos2 = setText.rfind(' @ ')
  334.                 if pos2 == -1:
  335.                     pos2 = setText.find('  \n')
  336.         if setText[pos2-1] == ')':
  337.             pos2 = pos2 - 1;
  338.             pos1 = pos2 - 1;
  339.             while setText[pos1] != '(':
  340.                 pos1 = pos1 - 1;
  341.             pos1 = pos1 + 1;
  342.             setDict['Nickname'] = setText[:pos1-2]
  343.         else:
  344.             pos1 = 0;
  345.         posItem = setText.find(' @ ',pos2) + 3
  346.         setDict['Name'] = setText[pos1:pos2]
  347.         if posItem != -1:
  348.             setDict['Item'] = setText[posItem:setText.find('  \n')]
  349.  
  350.         # obtain shiny status
  351.         posShiny = setText.find('\nShiny: Yes')
  352.         setDict['Shiny'] = (posShiny > -1)
  353.         # obtain current moves
  354.         nlindex = list() # newline indices
  355.         lenset = len(setText)
  356.         posMove1 = lenset
  357.         while setText.rfind('- ',0,posMove1) > -1:
  358.             posMove1 = setText.rfind('- ',0,posMove1)
  359.             posMove2 = setText.find('\n',posMove1)
  360.             setDict['Moveset'].insert(0,setText[posMove1+2:posMove2-2]) # Extract moves
  361.         partition = posMove1;
  362.  
  363.         # obtain current EVs
  364.         EVindex = list()
  365.         if setText.find('\nEVs: ') > -1:
  366.             EVindex.append(setText.find('\nEVs: ')+5)
  367.             EVindex.append(setText.find('\n',EVindex[0]))
  368.             EVspaces = [EVindex[0]]
  369.             while EVspaces[-1] < EVindex[1]-1:
  370.                 EVspaces.append(setText.find(' ',EVspaces[-1]+1))
  371.             for e in range(0,int(len(EVspaces)/3)):
  372.                 EVs = int(setText[EVspaces[3*e]+1:EVspaces[3*e+1]])
  373.                 stat = setText[EVspaces[3*e+1]+1:EVspaces[3*e+2]]
  374.                 setDict['EVs'][stat2index[stat]] = EVs
  375.         # obtain current IVs
  376.         IVindex = list()
  377.         if setText.find('\nIVs: ') > -1:
  378.             IVindex.append(setText.find('\nIVs: ')+5)
  379.             IVindex.append(setText.find('\n',IVindex[0]))
  380.             IVspaces = [IVindex[0]]
  381.             while IVspaces[-1] < IVindex[1]-1:
  382.                 IVspaces.append(setText.find(' ',IVspaces[-1]+1))
  383.             for e in range(0,int(len(IVspaces)/3)):
  384.                 IVs = int(setText[IVspaces[3*e]+1:IVspaces[3*e+1]])
  385.                 stat = setText[IVspaces[3*e+1]+1:IVspaces[3*e+2]]
  386.                 setDict['IVs'][stat2index[stat]] = IVs
  387.         # obtain Nature
  388.         setDict['Nature'] = ''
  389.         posNature2 = setText.find('Nature  \n') - 1
  390.         if posNature2 > -1:
  391.             posNature1 = setText.rfind('\n',0,posNature2) + 1
  392.             setDict['Nature'] = setText[posNature1:posNature2]
  393.         # obtain current ability
  394.         posAbility1 = setText.find('\nAbility: ') + 10
  395.         posAbility2 = setText.find('\n',posAbility1) - 2
  396.         setDict['Ability'] = setText[posAbility1:posAbility2]
  397.         # obtain current level
  398.         setDict['Level'] = 100
  399.         if setText.find('\nLevel: ') > -1:
  400.             posLevel1 = setText.find('\nLevel: ') + 8
  401.             posLevel2 = setText.find('\n',posLevel1)
  402.             setDict['Level'] = int(setText[posLevel1:posLevel2])
  403.         # obtain current happiness
  404.         setDict['Happiness'] = 255
  405.         if setText.find('\nHappiness: ') > -1:
  406.             posHappiness1 = setText.find('\nHappiness: ') + 12
  407.             posHappiness2 = setText.find('\n',posHappiness1)
  408.             setDict['Happiness'] = int(setText[posHappiness1:posHappiness2])
  409.         # obtain current item
  410.         setDict['Item'] = ''
  411.         postemp = setText.find(setDict['Name']) + len(setDict['Name'])
  412.         if setText.find(' @ ',postemp) > -1:
  413.             posItem1 = setText.find(' @ ',postemp) + 3
  414.             posItem2 = setText.find('\n',posItem1) - 2
  415.             setDict['Item'] = setText[posItem1:posItem2]
  416.  
  417.     indexMegaStone = setDict['Item'].find('ite')
  418.     if indexMegaStone > -1:
  419.         if indexMegaStone == len(setDict['Item']) - 3:
  420.             if setDict['Item'] != 'Eviolite':
  421.                 indexMega = setDict['Name'].find('-Mega')
  422.                 if indexMega == -1:
  423.                     setDict['Name'] = setDict['Name'] + '-Mega'
  424.         elif indexMegaStone == len(setDict['Item']) - 5:
  425.             if setDict['Name'] == 'Charizard':
  426.                 setDict['Name'] = 'Charizard-Mega-' + setDict['Item'][-1]          
  427.     return setDict
  428.  
  429. def PrintSet(setDict,moveFrequency,showShiny,showIVs,showNicknames,sortMovesByAlphabetical,sortMovesByFrequency):
  430.     index2stat = {
  431.     0 : 'HP',
  432.     1 : 'Atk',
  433.     2 : 'Def',
  434.     3 : 'SpA',
  435.     4 : 'SpD',
  436.     5 : 'Spe'
  437.     }
  438.     setText = ''
  439.     if setDict['Nickname'] != '' and showNicknames:
  440.         setText += setDict['Nickname']
  441.         setText += ' ('
  442.     setText += setDict['Name']
  443.     if setDict['Nickname'] != '' and showNicknames:
  444.         setText += ')'
  445.     if setDict['Gender'] != '':
  446.         setText += ' ('
  447.         setText += setDict['Gender']
  448.         setText += ')'
  449.     if setDict['Item'] != '':
  450.         setText += ' @ '
  451.         setText += setDict['Item']
  452.     setText += '  \nAbility: '
  453.     setText += setDict['Ability']
  454.     if setDict['Level'] != 100:
  455.         setText += '  \nLevel: '
  456.         setText += str(int(setDict['Level']))
  457.     if setDict['Shiny'] == True and showShiny:
  458.         setText += ' \nShiny: Yes'
  459.     if setDict['Happiness'] != 255:
  460.         setText += '  \nHappiness: '
  461.         setText += str(int(setDict['Happiness']))
  462.     if sum(setDict['EVs']) > 0:
  463.         setText += '  \nEVs: '
  464.         for n in range(6):
  465.             if setDict['EVs'][n] > 0:
  466.                 setText += str(int(setDict['EVs'][n])) + ' ' + index2stat[n] + ' / '
  467.         if setText[-3:] == ' / ':
  468.             setText = setText[:-3]
  469.     if setDict['Nature'] != '':
  470.         setText += '  \n'
  471.         setText += setDict['Nature']
  472.         setText += ' Nature'
  473.     if 31*6 - sum(setDict['IVs']) > 0.5:
  474.         sharedMoves1 = list(setDict['SharedMoves1'].keys())
  475.         sharedMoves2 = list(setDict['SharedMoves2'].keys())
  476.         sharedMoves = sharedMoves1 + sharedMoves2
  477.         hasHP = 0
  478.         for m in sharedMoves:
  479.             if m.find('Hidden Power') > -1:
  480.                 hasHP += 1
  481.         if showIVs or hasHP == 0:
  482.             setText += '  \nIVs: '
  483.             for n in range(6):
  484.                 if setDict['IVs'][n] < 31:
  485.                     setText += str(int(setDict['IVs'][n])) + ' ' + index2stat[n] + ' / '
  486.             if setText[-3:] == ' / ':
  487.                 setText = setText[:-3]
  488.     setText += '  \n'
  489.     moveList = list(setDict['Moveset'])
  490.     def safeMoveFrequency(name,move,moveFrequency):
  491.         if name in moveFrequency:
  492.             if move in moveFrequency[name]:
  493.                 return moveFrequency[name][move]
  494.         return 0
  495.     if sortMovesByAlphabetical != 0:
  496.         moveList.sort(reverse=ToBool(sortMovesByAlphabetical))
  497.     if sortMovesByFrequency != 0:
  498.         moveList.sort(key=lambda k:safeMoveFrequency(setDict['Name'],k,moveFrequency), reverse=ToBool(sortMovesByFrequency))
  499.     for m in moveList:
  500.         if m in setDict['SharedMoves2']:
  501.             moves2 = list(setDict['SharedMoves2'].keys())
  502.             moves2Sorted = sorted(moves2, key=lambda k:sum(list(setDict['SharedMoves2'][k].values())), reverse=True)
  503.             moveText = moves2Sorted[0]
  504.             for mm in range(1,len(moves2Sorted)):
  505.                 moveText += ' / '
  506.                 moveText += moves2Sorted[mm]
  507.         elif m in setDict['SharedMoves1']:
  508.             moves1 = list(setDict['SharedMoves1'].keys())
  509.             moves1Sorted = sorted(moves1, key=lambda k:setDict['SharedMoves1'][k], reverse=True)
  510.             moveText = moves1Sorted[0]
  511.             for mm in range(1,len(moves1Sorted)):
  512.                 moveText += ' / '
  513.                 moveText += moves1Sorted[mm]
  514.         else:
  515.             moveText = m
  516.         setText += '- '
  517.         setText += moveText
  518.         setText += '  \n'
  519.     return setText
  520.  
  521. ### Takes a string s and outputs a number that increases with alphanumeric order
  522. ### set reverse to True if intending to arrange by descending order
  523. def OrdString(s,reverse):
  524.     if not reverse:
  525.         return s
  526.     ordList = [ord(c) for c in s]
  527.     reverseOrdList = [chr(1114111-o) for o in ordList]
  528.     return ''.join(reverseOrdList)
  529.  
  530. ### Takes a list and returns an list of absolute values.  Acts like np.abs
  531. def AbsList(l):
  532.     return [abs(i) for i in l]
  533.  
  534. ### Takes two lists and subtracts
  535. def SubtractLists(l1,l2):
  536.     if len(l1) != len(l2):
  537.         print('Error: Lists not same size')
  538.         return
  539.     lenl = len(l1)
  540.     l = list()
  541.     for n in range(0,lenl):
  542.         l.append(l1[n]-l2[n])
  543.     return l
  544.  
  545. def ToBool(n):
  546.     return bool(n-1)
  547.  
  548. analyzeTeams = True
  549. numTeamsGen = dict()
  550. foutTemplate = fin[:fin.rfind('.')]
  551.  
  552. setListIncomplete = list()
  553. teamListIncomplete = list()
  554.  
  555. ## Determine parse format
  556. f = open(fin, encoding='utf-8', errors='ignore')
  557. line = f.readline()
  558. inputFormatDense = False
  559. while line:
  560.     if line[0:3] == '===':
  561.         inputFormatDense = False
  562.         break
  563.     if line[0:3] == 'gen':
  564.         inputFormatDense = True
  565.         break
  566.     line = f.readline()
  567. f.close()
  568.  
  569. if allGenerations:
  570.     generation = list()
  571.     f = open(fin, encoding='utf-8', errors='ignore')
  572.     line = f.readline()
  573.     if inputFormatDense:
  574.         while line:
  575.             if line.find(']') > -1:
  576.                 g = line[0:line.find(']')]
  577.                 if g not in generation:
  578.                     generation.append(g)
  579.             line = f.readline()
  580.     else:
  581.         while line:
  582.             if line.find('=== [') > -1:
  583.                 if line[0:5] == '=== [' and line[-4:-1] == '===':
  584.                     g = line[5:line.find(']')]
  585.                     if g not in generation:
  586.                         generation.append(g)
  587.             line = f.readline()
  588.     f.close()
  589.  
  590. for gen in generation:
  591.     teamPreview = (int(gen[3]) >= 5)
  592.     setList = list()
  593.     teamList = list()
  594.     folderCount = {'':1}
  595.     rightGen = False if analyzeTeams else True
  596.     f = open(fin, encoding='utf-8', errors='ignore')
  597.     line = f.readline()
  598.     lineCount = 0
  599.     if inputFormatDense:
  600.         while line:
  601.             lineCount += 1
  602.             indexVert = line.find('|')
  603.             indexRight = line.find(']')
  604.             indexRight2 = line.find(']')
  605.             if analyzeTeams:
  606.                 if line[0:3] == 'gen':
  607.                     if line.find(gen + ']') > -1:
  608.                         if len(teamList) > 0:
  609.                             teamList[-1]['Index'][1] = len(setList)
  610.                         teamList.append({'Index': [len(setList),len(setList)],
  611.                                          'Name': line[1+len(gen):indexVert],
  612.                                          'Gen': gen,
  613.                                          'Folder': '',
  614.                                          'Score': [0,0,0,0,0,0],
  615.                                          'Line': lineCount,
  616.                                          'Anomalies': 0})
  617.                         if line.find('/') > -1:
  618.                             teamList[-1]['Folder'] = line[1+len(gen):line.find('/')+1]
  619.                             teamList[-1]['Name'] = line[line.find('/')+1:indexVert]
  620.                             if teamList[-1]['Folder'] not in folderCount:
  621.                                 folderCount[teamList[-1]['Folder']] = 1
  622.                             else:
  623.                                 folderCount[teamList[-1]['Folder']] += 1
  624.                         indexRight = indexVert # deals with team name delimiter as | instead of ]
  625.                         while indexRight > -1 and indexRight < len(line)-2:
  626.                             indexRight2 = line.find(']',indexRight+1)
  627.                             setList.append(ExtractSet(line[indexRight+1:indexRight2],inputFormatDense,pokedexStr,itemsStr,abilitiesStr,movesStr)) # also covers index of -1 for \n
  628.                             indexRight = indexRight2
  629.             line = f.readline()
  630.         f.close()
  631.     else:
  632.         buffer = line
  633.         lineStatus = 0; # 0 = importable not found, 1 if found
  634.         while line:
  635.             lineCount += 1
  636.             if analyzeTeams:
  637.                 if line.find('===') > -1:
  638.                     if line[0:3] == '===' and line[-4:-1] == '===':
  639.                         if line.find('[' + gen +  ']') > -1:
  640.                             if len(teamList) > 0:
  641.                                 teamList[-1]['Index'][1] = len(setList)
  642.                             teamList.append({'Index': [len(setList),len(setList)],
  643.                                              'Gen': gen,
  644.                                              'Name': line[7+len(gen):-5],
  645.                                              'Folder': '',
  646.                                              'Score': [0,0,0,0,0,0],
  647.                                              'Line': lineCount,
  648.                                              'Anomalies': 0})
  649.                             if line.find('/') > -1:
  650.                                 teamList[-1]['Folder'] = line[7+len(gen):line.find('/')+1]
  651.                                 teamList[-1]['Name'] = line[line.find('/')+1:-5]
  652.                                 if teamList[-1]['Folder'] not in folderCount:
  653.                                     folderCount[teamList[-1]['Folder']] = 1
  654.                                 else:
  655.                                     folderCount[teamList[-1]['Folder']] += 1
  656.                             rightGen = True
  657.                         else:
  658.                             rightGen = False
  659.                             line = f.readline()
  660.                             continue
  661.                 elif not rightGen:
  662.                     line = f.readline()
  663.                     continue
  664.  
  665.             if lineStatus == 0: # If importable has not been found
  666.                 mark = line.find('Ability:') # Use Ability to find importable set
  667.                 if mark == -1:
  668.                     mark = line.find('Level: ')
  669.                 if mark == -1:
  670.                     mark = line.find('Shiny: ')
  671.                 if mark == -1:
  672.                     mark = line.find('EVs: ')
  673.                 if mark == -1:
  674.                     mark = line.find('Nature  \n')
  675.                     if mark > -1:
  676.                         mark = 0
  677.                 if mark == -1:
  678.                     mark = line.find('IVs: ')
  679.                 if mark == -1:
  680.                     mark = line.find('-')
  681.                 if buffer == '\n':
  682.                     mark == -1
  683.                 if mark == 0:
  684.                     lineStatus = 1
  685.                     montxt = buffer
  686.                 buffer = line
  687.             if lineStatus == 1: # If importable has been found
  688.                 if line == '\n': # If linebreak has been found
  689.                     setList.append(ExtractSet(montxt,inputFormatDense,pokedexStr,itemsStr,abilitiesStr,movesStr)) # Save as a dict
  690.                     lineStatus = 0
  691.                 else:
  692.                     montxt = montxt + line # Add remaining importable lines
  693.             line = f.readline()
  694.         f.close()
  695.         if lineStatus == 1: # If importable has been found
  696.             setList.append(ExtractSet(montxt+'  \n',inputFormatDense,pokedexStr,itemsStr,abilitiesStr,movesStr)) # Save as a dict
  697.             lineStatus = 0
  698.     if analyzeTeams:
  699.         teamList[-1]['Index'][1] = len(setList)
  700.         if len(setList) == teamList[-1]['Index'][0]:
  701.             teamList.pop()
  702.         numTeamsGen[gen] = len(teamList)
  703.    
  704.     ## Determine if team is complete
  705.    
  706.     if gen.find('1v1') > 0:
  707.         maxMons = 1
  708.     elif gen.find('metronome') > 0:
  709.         maxMons = 2
  710.     else:
  711.         maxMons = 6
  712.     for n in range(len(teamList)):
  713.         teamList[n]['Anomalies'] = 0
  714.         if teamList[n]['Index'][1] - teamList[n]['Index'][0] < maxMons:
  715.             teamList[n]['Anomalies'] += 6
  716.         for s in setList[teamList[n]['Index'][0]:teamList[n]['Index'][1]]:
  717.             if int(gen[3]) > 2 and sum(s['EVs']) < 508 - (400/s['Level']) and gen.find('letsgo') == -1:
  718.                 teamList[n]['Anomalies'] += 1
  719.             if len(s['Moveset']) < 4 and s['Name'] != 'Ditto':
  720.                 teamList[n]['Anomalies'] += 1          
  721.         # if team is incomplete, copy team to teamListIncomplete and setListIncomplete
  722.         if teamList[n]['Anomalies'] > anomalyThreshold:
  723.             teamListIncomplete.append(copy.deepcopy(teamList[n]))
  724.             teamListIncomplete[-1]['Index'][0] = len(setListIncomplete)
  725.             setListIncomplete.extend(copy.deepcopy(setList[teamList[n]['Index'][0]:teamList[n]['Index'][1]]))
  726.             teamListIncomplete[-1]['Index'][1] = len(setListIncomplete)
  727.        
  728.     ## Find cores and leads
  729.    
  730.     if analyzeTeams:
  731.         coreList = [dict(),dict(),dict(),dict(),dict(),dict()]
  732.         coreCount = [0]*6
  733.         multiplicity = [0]*6
  734.         leadList = dict()
  735.         for n in range(len(teamList)):
  736.             if teamList[n]['Anomalies'] > anomalyThreshold:
  737.                 inc = 0 # increment
  738.             else:
  739.                 inc = 1
  740.             for p in range(6):
  741.                 core = combinations([s['Name'] for s in setList[teamList[n]['Index'][0]:teamList[n]['Index'][1]]],p+1)
  742.                 for c in core:
  743.                     cSort = tuple(sorted(c))
  744.                     if cSort in coreList[p]:
  745.                         coreList[p][cSort] += inc
  746.                     else:
  747.                         coreList[p][cSort] = inc
  748.             if not teamPreview:
  749.                 if setList[teamList[n]['Index'][0]]['Name'] in leadList:
  750.                     leadList[setList[teamList[n]['Index'][0]]['Name']] += inc
  751.                 else:
  752.                     leadList[setList[teamList[n]['Index'][0]]['Name']] = inc
  753.  
  754.         # NEW FEATURE Find antisynergistic pairs and triplets
  755.         if showMissingMonCores:
  756.             for p in range(1,maxMissingMonCores):
  757.                 core = combinations([c[0] for c in coreList[0]],p+1)
  758.                 for c in core:
  759.                     cSort = tuple(sorted(c))
  760.                     if cSort not in coreList[p]:
  761.                         coreList[p][cSort] = 0
  762.  
  763.  
  764.         for p in range(6):
  765.             coreCount[p] = sum(coreList[p].values())
  766.             multiplicity[p] = math.factorial(p+1)
  767.         ## Calculate multivariate pointwise mutual information
  768.         mpmiList = [dict(),dict(),dict(),dict(),dict(),dict()]
  769.         for p in range(6):
  770.             for c in coreList[p]:
  771.                 if coreList[p][c] != 0:
  772.                     mpmiList[p][c] = 0;
  773.                     for q in range(p+1):
  774.                         for d in combinations(c,q+1):
  775.                             dSort = tuple(sorted(d))
  776.                             if coreList[q][dSort] != 0:
  777.                                 # sign convention: positive if synergetic
  778.                                 mpmiList[p][c] += (-1)**(q) * (-math.log(coreList[q][dSort]/coreCount[q]/multiplicity[q],2))
  779.                             else:
  780.                                 mpmiList[p][c] = 0
  781.                 else: # NEW FEATURE
  782.                     mpmiList[p][c] = -100
  783.                     for m in c:
  784.                         if coreList[0][(m,)] > 0:
  785.                             mpmiList[p][c] += -math.log(coreList[0][(m,)]/coreCount[0]/multiplicity[0],2)
  786.         ## Sort builder
  787.        
  788.         if sortBuilder:
  789.             if sortMonsByFrequency != 0 or sortMonsByColor:
  790.                 off = 1 - teamPreview
  791.                 def DominantHue(s,gamma,colors,hueOffset):
  792.                     if s['Name'] in colors:
  793.                         colorDict = colors[s['Name']]
  794.                         numColors = len(colorDict['Counts'])
  795.                         weights = [colorDict['Counts'][i]*(colorDict['HSV'][i][1]+colorDict['HSV'][i][2])**gamma for i in range(0,numColors)]
  796.                         dominantIndex = weights.index(max(weights))
  797.                         return (colorDict['HSV'][dominantIndex][0] - hueOffset) % 1.0
  798.                     else:
  799.                         return 0
  800.                 def SetSortKey(x):
  801.                     keyList = list()
  802.                     if sortMonsByFrequency != 0:
  803.                         keyList.append(sortMonsByFrequency*coreList[0][(x['Name'],)])
  804.                     if sortMonsByColor:
  805.                         keyList.append(DominantHue(x,gamma,colors,hueOffset))
  806.                     return tuple(keyList)
  807.                 for n in range(len(teamList)):
  808.                     if teamList[n]['Anomalies'] > anomalyThreshold:
  809.                         continue
  810.                     teamSets = setList[teamList[n]['Index'][0]+off:teamList[n]['Index'][1]]
  811.                     if sortMonsByColor:
  812.                         hueOffset = 0
  813.                         if off == 0:
  814.                             dominantHueList = [DominantHue(s,gamma,colors,hueOffset) for s in teamSets]
  815.                             dominantHueList.sort()
  816.                             dominantHueDifferenceList = []
  817.                             for i in range(0,len(dominantHueList)-1):
  818.                                 dominantHueDifferenceList.append(dominantHueList[i+1] - dominantHueList[i])
  819.                             dominantHueDifferenceList.append(1 + dominantHueList[0] - dominantHueList[-1])
  820.                             indexMaxHueDiff = dominantHueDifferenceList.index(max(dominantHueDifferenceList))
  821.                             hueOffset = (dominantHueList[indexMaxHueDiff] + dominantHueDifferenceList[indexMaxHueDiff]/2) % 1.0
  822.                         elif off == 1:
  823.                             hueOffset = (DominantHue(setList[teamList[n]['Index'][0]],gamma,colors,hueOffset) - 0.1) % 1.0
  824.  
  825.                     teamSets.sort(key=SetSortKey)
  826.                     setList[teamList[n]['Index'][0]+off:teamList[n]['Index'][1]] = teamSets
  827.                     hueOffset = 0
  828.                    
  829.             ## Score the teams
  830.  
  831.             for n in range(len(teamList)):
  832.                 if teamList[n]['Anomalies'] > anomalyThreshold:
  833.                     continue
  834.                 for p in range(6):
  835.                     core = combinations([s['Name'] for s in setList[teamList[n]['Index'][0]:teamList[n]['Index'][1]]],p+1)
  836.                     numCombinations = 0
  837.                     for c in core:
  838.                         cSort = tuple(sorted(c))
  839.                         teamList[n]['Score'][p] += coreList[p][cSort]
  840.                         numCombinations += 1
  841.                     teamList[n]['Score'][p] = teamList[n]['Score'][p] / len(teamList) # / numCombinations
  842.                        
  843.  
  844.     ## Extracts finished sets
  845.    
  846.     # Make set list excluding incomplete teams
  847.     setListComplete = list()
  848.     for n in range(len(teamList)):
  849.         if teamList[n]['Anomalies'] > anomalyThreshold:
  850.             continue
  851.         setListComplete.extend(setList[teamList[n]['Index'][0]:teamList[n]['Index'][1]])
  852.    
  853.     setListNameSorted = sorted(copy.deepcopy(setListComplete), key=lambda x:x['Name']); # Alphabetical-sorted list of sets in dict format
  854.     setListLen = len(setListNameSorted)
  855.     setListNameSortedIndex = sorted(range(setListLen), key=lambda x:setListComplete[x]['Name'])
  856.  
  857.  
  858.     ## Categorize sets for synergy analysis
  859.     # Aggregate by Item or EVs, then store moves
  860.     categoryDict = dict()
  861.     natureDict = {
  862.         'Hardy': [0,0,0,0,0,0],
  863.         'Lonely': [0,1,-1,0,0,0],
  864.         'Brave': [0,1,0,0,0,-1],
  865.         'Adamant': [0,1,0,-1,0,0],
  866.         'Naughty': [0,1,0,0,-1,0],
  867.         'Bold': [0,-1,1,0,0,0],
  868.         'Docile': [0,0,0,0,0,0],
  869.         'Relaxed': [0,0,1,0,0,-1],
  870.         'Impish': [0,0,1,-1,0,0],
  871.         'Lax': [0,0,1,0,-1,0],
  872.         'Timid': [0,-1,0,0,0,1],
  873.         'Hasty': [0,0,-1,0,0,1],
  874.         'Serious': [0,0,0,0,0,0],
  875.         'Jolly': [0,0,0,-1,0,1],
  876.         'Naive': [0,0,0,0,-1,1],
  877.         'Modest': [0,-1,0,1,0,0],
  878.         'Mild': [0,0,-1,1,0,0],
  879.         'Quiet': [0,0,0,1,0,-1],
  880.         'Bashful': [0,0,0,0,0,0],
  881.         'Rash': [0,0,0,1,-1,0],
  882.         'Calm': [0,-1,0,0,1,0],
  883.         'Gentle': [0,0,-1,0,1,0],
  884.         'Sassy': [0,0,0,0,1,-1],
  885.         'Careful': [0,0,0,-1,1,0],
  886.         'Quirky': [0,0,0,0,0,0]
  887.     }
  888.     for n in range(setListLen):
  889.         currentSetDict = setListNameSorted[n]
  890.         if currentSetDict['Name'] not in categoryDict:
  891.             # print(currentSetDict['Name'])
  892.             categoryDict[currentSetDict['Name']] = dict()
  893.             categoryDict[currentSetDict['Name']]['Count'] = 0
  894.         categoryDict[currentSetDict['Name']]['Count'] += 1
  895.         # define first category
  896.         if currentSetDict['Item'] in importantItems or (currentSetDict['Item'].find('Z') == len(currentSetDict['Item'])-1 and len(currentSetDict['Item']) > 0 and 'Z' in importantItems):
  897.             category1 = currentSetDict['Item']
  898.         else:
  899.             currentEVs = copy.deepcopy(currentSetDict['EVs'])
  900.             if currentSetDict['Nature'] != '':
  901.                 natureModifier = natureDict[currentSetDict['Nature']]
  902.                 if max(natureModifier) == 1:
  903.                     increaseStat = natureModifier.index(1)
  904.                     decreaseStat = natureModifier.index(-1)
  905.                     currentEVs[increaseStat] += natureEVmodifier
  906.                     currentEVs[decreaseStat] -= natureEVmodifier
  907.             highestEVIndex1 = currentEVs.index(max(currentEVs))
  908.             tempEVs = copy.deepcopy(currentEVs)
  909.             del tempEVs[highestEVIndex1]
  910.             highestEVIndex2 = tempEVs.index(max(tempEVs))
  911.             if highestEVIndex2 >= highestEVIndex1:
  912.                 highestEVIndex2 += 1
  913.             category1 = tuple(sorted([highestEVIndex1,highestEVIndex2]))
  914.         if category1 not in categoryDict[currentSetDict['Name']]:
  915.             # if currentSetDict['Name'] == 'Tyranitar' and category1 == 'Choice Band':
  916.             #     print('Yoohoo')
  917.             categoryDict[currentSetDict['Name']][category1] = {
  918.                 'ActualCount': [dict(),dict(),dict()],
  919.                 'PriorProb': [dict(),dict()],
  920.                 'ActualProb': [dict(),dict()],
  921.                 'SumProb': [dict(),dict()],
  922.                 'MutualInfo': [dict(),dict()],
  923.                 'TotalCount': [0,0,0],
  924.                 'Count': 0 # category
  925.             }
  926.         categoryDict[currentSetDict['Name']][category1]['Count'] += 1
  927.         incMove = 1
  928.         ## Generate first and second order move statistics
  929.         for p in range(3):
  930.             moveCore = combinations(currentSetDict['Moveset'],p+1)
  931.             for c in moveCore:
  932.                 cSort = tuple(sorted(c))
  933.                 if cSort in categoryDict[currentSetDict['Name']][category1]['ActualCount'][p]:
  934.                     categoryDict[currentSetDict['Name']][category1]['ActualCount'][p][cSort] += incMove
  935.                 else:
  936.                     categoryDict[currentSetDict['Name']][category1]['ActualCount'][p][cSort] = incMove
  937.                 categoryDict[currentSetDict['Name']][category1]['TotalCount'][p] += 1
  938.  
  939.     for name in categoryDict:
  940.         # print(name)
  941.         for cat in categoryDict[name]:
  942.             if cat == 'Count' or cat in importantItems:
  943.                 continue
  944.             ## Calculate uncorrelated probabilities
  945.             movePairs = combinations([c[0] for c in categoryDict[name][cat]['ActualCount'][0]],2)
  946.             totalMoveCount = categoryDict[name][cat]['TotalCount'][0]
  947.             totalPairCount = categoryDict[name][cat]['TotalCount'][1]
  948.             movePairCandidates = []
  949.             for c in movePairs:
  950.                 cSort = tuple(sorted(c))
  951.                 probMove = [categoryDict[name][cat]['ActualCount'][0][(m,)] / totalMoveCount for m in cSort]
  952.                 categoryDict[name][cat]['PriorProb'][0][cSort] = probMove[0]*probMove[1]*(1/(1-probMove[0])+1/(1-probMove[1]))
  953.                 if cSort in categoryDict[name][cat]['ActualCount'][1]:
  954.                     categoryDict[name][cat]['ActualProb'][0][cSort] = categoryDict[name][cat]['ActualCount'][1][cSort] / totalPairCount
  955.                 else:
  956.                     categoryDict[name][cat]['ActualProb'][0][cSort] = 0
  957.                 categoryDict[name][cat]['MutualInfo'][0][cSort] = categoryDict[name][cat]['ActualProb'][0][cSort] / categoryDict[name][cat]['PriorProb'][0][cSort]
  958.                 categoryDict[name][cat]['SumProb'][0][cSort] = sum(probMove) - categoryDict[name][cat]['ActualProb'][0][cSort] #adjusted by PIE
  959.                 if categoryDict[name][cat]['MutualInfo'][0][cSort] < movePairSynergyThreshold:
  960.                     if min(probMove) > (1/4) * moveProbThreshold:
  961.                         if min([categoryDict[name][cat]['ActualCount'][0][(m,)] for m in cSort]) > moveCountThreshold:
  962.                             movePairCandidates.append(cSort)
  963.             if len(movePairCandidates) > 0:
  964.                 chosenCore = max(movePairCandidates,key=lambda x:categoryDict[name][cat]['SumProb'][0][x])
  965.                 if categoryDict[name][cat]['SumProb'][0][chosenCore] > (1/4)*sumMoveProbThreshold:
  966.                     categoryDict[name][cat]['SplitMoves'] = chosenCore
  967.                 else:
  968.                     # print("Stage 1")
  969.                     moveTriplets = combinations([c[0] for c in categoryDict[name][cat]['ActualCount'][0]],3)
  970.                     totalTripletCount = categoryDict[name][cat]['TotalCount'][2]
  971.                     moveTripletCandidates = []
  972.                     for c in moveTriplets:
  973.                         # print("Stage 2")
  974.                         cSort = tuple(sorted(c))
  975.                         probMove = [categoryDict[name][cat]['ActualCount'][0][(m,)] / totalMoveCount for m in cSort]
  976.                         categoryDict[name][cat]['PriorProb'][1][cSort] = probMove[0]*probMove[1]*probMove[2]*(
  977.                             1/(1-probMove[0])/(1-probMove[0]-probMove[1]) +
  978.                             1/(1-probMove[1])/(1-probMove[1]-probMove[0]) +
  979.                             1/(1-probMove[0])/(1-probMove[0]-probMove[2]) +
  980.                             1/(1-probMove[2])/(1-probMove[2]-probMove[0]) +
  981.                             1/(1-probMove[1])/(1-probMove[1]-probMove[2]) +
  982.                             1/(1-probMove[2])/(1-probMove[2]-probMove[1]))
  983.                         if cSort in categoryDict[name][cat]['ActualCount'][2]:
  984.                             categoryDict[name][cat]['ActualProb'][1][cSort] = categoryDict[name][cat]['ActualCount'][2][cSort] / totalTripletCount
  985.                         else:
  986.                             categoryDict[name][cat]['ActualProb'][1][cSort] = 0
  987.                         categoryDict[name][cat]['MutualInfo'][1][cSort] = categoryDict[name][cat]['ActualProb'][1][cSort] / categoryDict[name][cat]['PriorProb'][1][cSort]
  988.                         subMovePairs = combinations(cSort,2)
  989.                         categoryDict[name][cat]['SumProb'][1][cSort] = sum(probMove) - sum([categoryDict[name][cat]['ActualProb'][0][tuple(sorted(d))] for d in subMovePairs]) + categoryDict[name][cat]['ActualProb'][1][cSort]
  990.                         subMovePairs = combinations(cSort,2)
  991.                         if categoryDict[name][cat]['MutualInfo'][1][cSort] < moveTripletSynergyThreshold:
  992.                             allPairsInCandidates = True
  993.                             for d in subMovePairs:
  994.                                 if categoryDict[name][cat]['MutualInfo'][0][tuple(sorted(d))] > movePairInTripletSynergyThreshold:
  995.                                     allPairsInCandidates = False
  996.                             if allPairsInCandidates:
  997.                                 if min(probMove) > (1/4) * moveProbInTripletThreshold:
  998.                                     if min([categoryDict[name][cat]['ActualCount'][0][(m,)] for m in cSort]) > moveCountThreshold:
  999.                                         moveTripletCandidates.append(cSort)
  1000.                     if len(moveTripletCandidates) > 0:
  1001.                         chosenCore = max(moveTripletCandidates,key=lambda x:categoryDict[name][cat]['SumProb'][1][x])
  1002.                         if categoryDict[name][cat]['SumProb'][1][chosenCore] > (1/4)*sumMoveProbTripletThreshold:
  1003.                             # print("Stage 4")
  1004.                             # print(name)
  1005.                             # print(cat)
  1006.                             # print(chosenCore)
  1007.                             # print(categoryDict[name][cat]['SumProb'][1][chosenCore])
  1008.                             categoryDict[name][cat]['SplitMoves'] = chosenCore
  1009.                                
  1010.  
  1011.     def shortenMove(move):
  1012.         # Shorten Move
  1013.         if move.find('Hidden Power') > -1:
  1014.             move = move.replace('Hidden Power','HP')
  1015.             move = move.replace('[','')
  1016.             move = move.replace(']','')
  1017.             return move
  1018.         move = move.replace('-',' ')
  1019.         if move.find(' ') > -1 or move.find('-') > -1:
  1020.             initials = ""
  1021.             moveSplit = move.split()
  1022.             for word in moveSplit:  # go through each name
  1023.                 initials += word[0].upper()  # append the initial
  1024.             move = initials    
  1025.         return move        
  1026.  
  1027.     ## Generate category nicknames
  1028.     index2stat = {
  1029.     0 : 'HP',
  1030.     1 : 'Atk',
  1031.     2 : 'Def',
  1032.     3 : 'SpA',
  1033.     4 : 'SpD',
  1034.     5 : 'Spe'
  1035.     }
  1036.     categoryNics = dict()
  1037.     for name in categoryDict:
  1038.         movesCutDict = dict()
  1039.         for cat in categoryDict[name]:
  1040.             if cat == 'Count':
  1041.                 continue
  1042.             movesCutDict[cat] = dict()
  1043.             # Find a move that describes the set well
  1044.             totalMoveCount = categoryDict[name][cat]['TotalCount'][0]
  1045.             for c in categoryDict[name][cat]['ActualCount'][0]:
  1046.                 prob = categoryDict[name][cat]['ActualCount'][0][c] / totalMoveCount
  1047.                 if prob > namingExclusionMoveThreshold:
  1048.                     move = c[0]
  1049.                     movesCutDict[cat][move] = prob
  1050.         for cat in categoryDict[name]:
  1051.             if cat == 'Count':
  1052.                 continue
  1053.             fullCat = (name,cat)
  1054.             if cat in importantItems  or (isinstance(cat,str) and (cat.find('Z') == len(cat)-1 and len(cat) > 0 and 'Z' in importantItems)):
  1055.                 cat = cat.replace(' Berry', '')
  1056.                 cat = cat.replace('Choice ', '')
  1057.                 cat = cat.replace('Assault Vest', 'AV')
  1058.                 categoryNics[fullCat] = cat + ' ' + name
  1059.                 continue
  1060.             else:
  1061.                 statStr = index2stat[cat[0]] + '/' + index2stat[cat[1]]
  1062.                 # Find a move that describes the set well
  1063.                 currentMoveSet = set(movesCutDict[cat].keys())
  1064.                 differenceSet = set(movesCutDict[cat].keys())
  1065.                 diffTaken = False
  1066.                 for dat in categoryDict[name]:
  1067.                     if dat == 'Count':
  1068.                         continue
  1069.                     if dat == cat:
  1070.                         continue
  1071.                     if categoryDict[name][dat]['Count'] / categoryDict[name]['Count'] < namingExclusionCatThreshold:
  1072.                         continue
  1073.                     differenceSet = differenceSet.difference(set(movesCutDict[dat].keys()))
  1074.                     diffTaken = True
  1075.                 descriptiveMove = ''
  1076.                 if diffTaken:
  1077.                     ignoreNum = 1
  1078.                     while ignoreNum <= namingIgnoreCategoryNum and descriptiveMove == '':
  1079.                         if differenceSet: #len(categoryDict[name]) > 2: # account for Count
  1080.                             maxMove = max(differenceSet,key=lambda x:movesCutDict[cat][x])
  1081.                             if movesCutDict[cat][maxMove] > namingMinMoveProb:
  1082.                                 descriptiveMove = shortenMove(maxMove)
  1083.                         if (not differenceSet) or (differenceSet and (movesCutDict[cat][maxMove] <= namingMinMoveProb)):
  1084.                             # try to find move that intersects with at most one other category
  1085.                             categorySet = set(categoryDict[name].keys())
  1086.                             categorySet.remove('Count')
  1087.                             categorySet.remove(cat)
  1088.                             if ignoreNum > len(categorySet) - 1:
  1089.                                 break
  1090.                             categoryCombs1 = combinations(categorySet,len(categorySet)-ignoreNum)
  1091.                             selectedMoves = dict()
  1092.                             diffTaken = False
  1093.                             for c in categoryCombs1:
  1094.                                 differenceSet = set(movesCutDict[cat].keys())
  1095.                                 for dat in c:
  1096.                                     if categoryDict[name][dat]['Count'] / categoryDict[name]['Count'] < namingExclusionCatThreshold:
  1097.                                         continue
  1098.                                     differenceSet = differenceSet.difference(set(movesCutDict[dat].keys()))
  1099.                                     diffTaken = True
  1100.                                 if diffTaken and differenceSet:
  1101.                                     maxMove = max(differenceSet,key=lambda x:movesCutDict[cat][x])
  1102.                                     selectedMoves[maxMove] = movesCutDict[cat][maxMove]
  1103.                             if selectedMoves:
  1104.                                 maxMove1 = max(selectedMoves,key=lambda x:movesCutDict[cat][x])
  1105.                                 if movesCutDict[cat][maxMove1] > namingMinMoveProb:
  1106.                                     descriptiveMove = shortenMove(maxMove1)
  1107.                         ignoreNum += 1
  1108.  
  1109.                 if 'SplitMoves' in categoryDict[name][cat]:
  1110.                     for m in range(len(categoryDict[name][cat]['SplitMoves'])):
  1111.                         move = categoryDict[name][cat]['SplitMoves'][m]
  1112.                         fullCat = (name,cat,move)
  1113.                         move = shortenMove(move)
  1114.                         if descriptiveMove == '':
  1115.                             categoryNics[fullCat] = statStr + ' ' + move + ' ' + name
  1116.                         else:
  1117.                             categoryNics[fullCat] = statStr + ' ' + move + ' ' + descriptiveMove + ' ' + name
  1118.                 else:
  1119.                     if len(categoryDict[name]) <= 2: # account for Count
  1120.                         categoryNics[fullCat] = name
  1121.                         continue
  1122.                     if descriptiveMove == '':
  1123.                         categoryNics[fullCat] = statStr + ' ' + name
  1124.                     else:
  1125.                         categoryNics[fullCat] = statStr + ' ' + descriptiveMove + ' ' + name
  1126.  
  1127.  
  1128.     ## List Categories
  1129.     catLeadList = dict()
  1130.     catCoreList = [dict(),dict(),dict(),dict(),dict(),dict()]
  1131.     for n in range(len(teamList)):
  1132.         if teamList[n]['Anomalies'] > anomalyThreshold:
  1133.             inc = 0 # increment
  1134.             continue
  1135.         else:
  1136.             inc = 1
  1137.         ## Find the category of each mon
  1138.         categoryList = list()
  1139.         monIndex = 0
  1140.         for s in setList[teamList[n]['Index'][0]:teamList[n]['Index'][1]]:
  1141.             category = [s['Name']]
  1142.             if s['Item'] in importantItems or (s['Item'].find('Z') == len(s['Item'])-1 and len(s['Item']) > 0 and 'Z' in importantItems):
  1143.                 category.append(s['Item'])
  1144.             else:
  1145.                 currentEVs = copy.deepcopy(s['EVs'])
  1146.                 if s['Nature'] != '':
  1147.                     natureModifier = natureDict[s['Nature']]
  1148.                     if max(natureModifier) == 1:
  1149.                         increaseStat = natureModifier.index(1)
  1150.                         decreaseStat = natureModifier.index(-1)
  1151.                         currentEVs[increaseStat] += natureEVmodifier
  1152.                         currentEVs[decreaseStat] -= natureEVmodifier
  1153.                 highestEVIndex1 = currentEVs.index(max(currentEVs))
  1154.                 tempEVs = copy.deepcopy(currentEVs)
  1155.                 del tempEVs[highestEVIndex1]
  1156.                 highestEVIndex2 = tempEVs.index(max(tempEVs))
  1157.                 if highestEVIndex2 >= highestEVIndex1:
  1158.                     highestEVIndex2 += 1
  1159.                 highestEVs = tuple(sorted([highestEVIndex1,highestEVIndex2]))
  1160.                 category.append(highestEVs)
  1161.                 # check category dict
  1162.                 if 'SplitMoves' in categoryDict[s['Name']][highestEVs]:
  1163.                     numSplit = len(categoryDict[s['Name']][highestEVs]['SplitMoves'])
  1164.                     if numSplit == 2:
  1165.                         move0InSet = (categoryDict[s['Name']][highestEVs]['SplitMoves'][0] in s['Moveset'])
  1166.                         move1InSet = (categoryDict[s['Name']][highestEVs]['SplitMoves'][1] in s['Moveset'])
  1167.                         if move0InSet and not move1InSet:
  1168.                             category.append(categoryDict[s['Name']][highestEVs]['SplitMoves'][0])
  1169.                         elif move1InSet and not move0InSet:
  1170.                             category.append(categoryDict[s['Name']][highestEVs]['SplitMoves'][1])
  1171.                         else:
  1172.                             monIndex += 1
  1173.                             continue
  1174.                     elif numSplit == 3:
  1175.                         move0InSet = (categoryDict[s['Name']][highestEVs]['SplitMoves'][0] in s['Moveset'])
  1176.                         move1InSet = (categoryDict[s['Name']][highestEVs]['SplitMoves'][1] in s['Moveset'])
  1177.                         move2InSet = (categoryDict[s['Name']][highestEVs]['SplitMoves'][2] in s['Moveset'])
  1178.                         if move0InSet and (not move1InSet) and (not move2InSet):
  1179.                             category.append(categoryDict[s['Name']][highestEVs]['SplitMoves'][0])
  1180.                         elif move1InSet and (not move0InSet) and (not move2InSet):
  1181.                             category.append(categoryDict[s['Name']][highestEVs]['SplitMoves'][1])
  1182.                         elif move2InSet and (not move0InSet) and (not move1InSet):
  1183.                             category.append(categoryDict[s['Name']][highestEVs]['SplitMoves'][2])
  1184.                         else:
  1185.                             monIndex += 1
  1186.                             continue
  1187.             if monIndex == 0 and not teamPreview:
  1188.                 if tuple(category) in catLeadList:
  1189.                     catLeadList[tuple(category)] += inc
  1190.                 else:
  1191.                     catLeadList[tuple(category)] = inc
  1192.             monIndex += 1
  1193.             categoryList.append(tuple(category))
  1194.         if 'Categories' not in teamList[n]:
  1195.             teamList[n]['Categories'] = [tuple(category)]
  1196.         else:
  1197.             teamList[n]['Categories'].append(tuple(category))
  1198.  
  1199.         for p in range(6):
  1200.             catCore = combinations(categoryList,p+1)
  1201.             for c in catCore:
  1202.                 cSort = tuple(sorted(c, key=lambda x:x[0]))
  1203.                 if cSort in catCoreList[p]:
  1204.                     catCoreList[p][cSort] += inc
  1205.                 else:
  1206.                     catCoreList[p][cSort] = inc
  1207.  
  1208.     # NEW FEATURE Find antisynergistic pairs and triplets
  1209.     if showMissingSetCores:
  1210.         for p in range(1,maxMissingMonCores):
  1211.             catCore = combinations([c[0] for c in catCoreList[0]],p+1)
  1212.             for c in catCore:
  1213.                 cSort = tuple(sorted(c, key=lambda x:x[0]))
  1214.                 monSet = set([cat[0] for cat in cSort])
  1215.                 if len(monSet) < p+1:
  1216.                     continue
  1217.                 if cSort not in catCoreList[p]:
  1218.                     catCoreList[p][cSort] = 0
  1219.        
  1220.  
  1221.     catCoreCount = [0]*6
  1222.     catMultiplicity = [0]*6
  1223.     for p in range(6):
  1224.         catCoreCount[p] = sum(catCoreList[p].values())
  1225.         catMultiplicity[p] = math.factorial(p+1)
  1226.     ## Calculate multivariate pointwise mutual information
  1227.     mpmiCatList = [dict(),dict(),dict(),dict(),dict(),dict()]
  1228.     for p in range(6):
  1229.         for c in catCoreList[p]:
  1230.             if catCoreList[p][c] != 0:
  1231.                 mpmiCatList[p][c] = 0
  1232.                 for q in range(p+1):
  1233.                     for d in combinations(c,q+1):
  1234.                         dSort = tuple(sorted(d, key=lambda x:x[0]))
  1235.                         if catCoreList[q][dSort] != 0:
  1236.                             # sign convention: positive if synergetic
  1237.                             mpmiCatList[p][c] += (-1)**(q) * (-math.log(catCoreList[q][dSort]/catCoreCount[q]/catMultiplicity[q],2))
  1238.                         else:
  1239.                             mpmiCatList[p][c] = 0
  1240.             else: # NEW FEATURE
  1241.                 mpmiCatList[p][c] = -100
  1242.                 for m in c:
  1243.                     mpmiCatList[p][c] -= -math.log(catCoreList[0][(m,)]/catCoreCount[0]/catMultiplicity[0],2)
  1244.            
  1245.  
  1246.     ## Perform spectral clustering
  1247.     if analyzeArchetypes:
  1248.         catIndivList = [c[0] for c in catCoreList[0].keys()]
  1249.         numCats = len(catIndivList)
  1250.         W = np.zeros((numCats,numCats))
  1251.         D = np.zeros((numCats,numCats))
  1252.         Dinv = np.zeros((numCats,numCats))
  1253.         DinvRoot = np.zeros((numCats,numCats))
  1254.         for ii in range(numCats):
  1255.             for jj in range(numCats):
  1256.                 core = tuple(sorted([catIndivList[ii],catIndivList[jj]],key=lambda x:x[0]))
  1257.                 if core in catCoreList[1]:
  1258.                     # W[ii,jj] = 2.0**catCoreList[1][core]
  1259.                     # W[ii,jj] = 2.0**mpmiCatList[1][core] * catCoreList[1][core]**gammaSpectral
  1260.                     W[ii,jj] = catCoreList[1][core] ** gammaSpectral
  1261.                     # W[ii,jj] = 2.0**mpmiCatList[1][core]
  1262.         for ii in range(numCats):
  1263.             D[ii,ii] = np.sum(W[ii,:])
  1264.             Dinv[ii,ii] = 1/np.sum(W[ii,:])
  1265.             DinvRoot[ii,ii] = 1/math.sqrt(np.sum(W[ii,:]))
  1266.         L = D - W # unnormalized laplacian
  1267.         Lsym = DinvRoot @ L @ DinvRoot
  1268.         Lrw = np.matmul(Dinv,L)
  1269.         clusterMethod = 2
  1270.         if clusterMethod == 0:
  1271.             eigenval,v = np.linalg.eigh(L)
  1272.         elif clusterMethod == 1:
  1273.             eigenval,v = np.linalg.eigh(Lrw)
  1274.         elif clusterMethod == 2:
  1275.             eigenval,v = np.linalg.eigh(Lsym)
  1276.  
  1277.         fpcs = []
  1278.         for nCenters in range(2,numArchetypesRange):
  1279.             vk = np.zeros((numCats,numCats))
  1280.             if clusterMethod == 2:
  1281.                 for ii in range(numCats):
  1282.                     vk[ii,0:nCenters] = v[ii,0:nCenters] / math.sqrt(sum(v[ii,0:nCenters]**2))
  1283.             else:
  1284.                 vk = v
  1285.             cntr, u, u0, d, jm, p, fpc = fuzz.cluster.cmeans(vk[:,0:nCenters].transpose(), nCenters, exponent, error=0.005, maxiter=1000, init=None)
  1286.             fpcs.append(fpc)
  1287.         fig, ax = plt.subplots()
  1288.         ax.plot(np.r_[2:15], fpcs)
  1289.         ax.set_xlabel("Number of centers")
  1290.         ax.set_ylabel("Fuzzy partition coefficient")
  1291.         plt.show()
  1292.         vk = np.zeros((numCats,numArchetypes))
  1293.         if clusterMethod == 2:
  1294.             for ii in range(numCats):
  1295.                 vk[ii,:] = v[ii,0:numArchetypes] / math.sqrt(sum(v[ii,0:numArchetypes]**2))
  1296.         else:
  1297.             vk = v[:,0:numArchetypes]
  1298.         cntr, pMat, pMat0, d, jm, p, fpc = fuzz.cluster.cmeans(vk.transpose(), numArchetypes, exponent, error=0.005, maxiter=1000, init=None)
  1299.  
  1300.     ## Aggregates sets by EV equivalence
  1301.  
  1302.     setListEVcombined = list() # list of dicts of full sets
  1303.     chosenMon = '';
  1304.     chosenMonIdxEVcombined = 0;
  1305.     for n in range(setListLen):
  1306.         currentSetDict = setListNameSorted[n]
  1307.         currentCount = 1
  1308.  
  1309.         # determine set equivalence except EVs and IVs
  1310.         matchIndex = -1; # matching set index
  1311.         nn = chosenMonIdxEVcombined # iterator
  1312.         while (matchIndex < 0 and nn < len(setListEVcombined)):
  1313.             if currentSetDict['Name'] == chosenMon:  
  1314.                 if (currentSetDict['Nature'] == setListEVcombined[nn]['Nature'] and
  1315.                 currentSetDict['Ability'] == setListEVcombined[nn]['Ability'] and
  1316.                 currentSetDict['Level'] == setListEVcombined[nn]['Level'] and
  1317.                 currentSetDict['Happiness'] == setListEVcombined[nn]['Happiness'] and
  1318.                 currentSetDict['Item'] == setListEVcombined[nn]['Item'] and
  1319.                 set(currentSetDict['Moveset']) == set(setListEVcombined[nn]['Moveset'])):
  1320.                     if sum(AbsList(SubtractLists(currentSetDict['EVs'],setListEVcombined[nn]['EVs']))) <= EVthreshold*2:
  1321.                         if sum(AbsList(SubtractLists(currentSetDict['IVs'],setListEVcombined[nn]['IVs']))) <= IVthreshold:
  1322.                             matchIndex = nn
  1323.                             if currentCount <= setListEVcombined[nn]['SubCountEV']:
  1324.                                 setListEVcombined[nn]['CountEV'] += currentCount
  1325.                             else:
  1326.                                 setListEVcombined[nn]['CountEV'] += currentCount
  1327.                                 setListEVcombined[nn]['SubCountEV'] = currentCount
  1328.                                 setListEVcombined[nn]['EVs'] = currentSetDict['EVs'],
  1329.                                 setListEVcombined[nn]['IVs'] = currentSetDict['IVs'],
  1330.                                 setListEVcombined[nn]['Index'] = n
  1331.             else:
  1332.                 chosenMonIdxEVcombined = len(setListEVcombined)
  1333.                 chosenMon = currentSetDict['Name']
  1334.             nn += 1
  1335.         if n == 0:
  1336.             chosenMonIdxEVcombined = len(setListEVcombined)
  1337.             chosenMon = currentSetDict['Name']
  1338.         if matchIndex < 0:
  1339.             currentSetDict['SubCountEV'] = currentCount;
  1340.             currentSetDict['CountEV'] = currentCount;
  1341.             currentSetDict['Index'] = n;
  1342.             setListEVcombined.append(currentSetDict)
  1343.  
  1344.     ## Sorting
  1345.     setListEVcombinedRank = [0]*len(setListEVcombined)
  1346.     for ii in range(0,len(setListEVcombined)):
  1347.         name = setListEVcombined[ii]['Name']
  1348.         count = setListEVcombined[ii]['CountEV']
  1349.         frontIdent1 = ord(name[0])
  1350.         frontIdent2 = ord(name[1])
  1351.         posSeparator = max(name.find('-'),name.find(' '))
  1352.         if posSeparator > -1:
  1353.             backIdent1 = ord(name[posSeparator+1])
  1354.             if posSeparator + 2 < len(name):
  1355.                 backIdent2 = ord(name[posSeparator+2])
  1356.             else:
  1357.                 backIdent2 = 0
  1358.         else:
  1359.             backIdent1 = ord(name[-2])
  1360.             backIdent2 = ord(name[-1])
  1361.         setListEVcombinedRank[ii] = (frontIdent1*2**24 + frontIdent2*2**16 + backIdent1*2**8 + backIdent2)*2**10 - count
  1362.     setListEVsorted = [setListEVcombined[i[0]] for i in sorted(enumerate(setListEVcombinedRank), key=lambda x:x[1])]
  1363.  
  1364.     ## Count move frequency
  1365.     moveFrequency = dict()
  1366.     monFrequency = dict()
  1367.     for ii in range(0,len(setListEVsorted)):
  1368.         if not (setListEVsorted[ii]['Name'] in moveFrequency):
  1369.             moveFrequency[setListEVsorted[ii]['Name']] = dict()
  1370.             monFrequency[setListEVsorted[ii]['Name']] = setListEVsorted[ii]['CountEV']
  1371.         else:
  1372.             monFrequency[setListEVsorted[ii]['Name']] += setListEVsorted[ii]['CountEV']
  1373.         for m in setListEVsorted[ii]['Moveset']:
  1374.             if not (m in moveFrequency[setListEVsorted[ii]['Name']]):
  1375.                 moveFrequency[setListEVsorted[ii]['Name']][m] = setListEVsorted[ii]['CountEV']
  1376.             else:
  1377.                 moveFrequency[setListEVsorted[ii]['Name']][m] += setListEVsorted[ii]['CountEV']
  1378.  
  1379.     len(setListEVsorted)
  1380.     ## Aggregates sets by move equivalence up to one slot
  1381.  
  1382.     setListMoves1Combined = list() # list of dicts of full sets
  1383.     chosenMon = '';
  1384.     chosenMonIdxMoves1Combined = 0;
  1385.     for n in range(len(setListEVsorted)):
  1386.         currentSetDict = setListEVsorted[n]
  1387.         currentSetDict['SharedMoves1'] = dict()
  1388.         currentCount = setListEVsorted[n]['CountEV']
  1389.         currentSetDict['CountMoves'] = setListEVsorted[n]['CountEV']
  1390.         # determine set equivalence except EVs and IVs
  1391.         matchIndex = -1; # matching set index
  1392.         nn = chosenMonIdxMoves1Combined # iterator
  1393.         while (matchIndex < 0 and nn < len(setListMoves1Combined) and combineMoves >= 1):
  1394.             if currentSetDict['Name'] == chosenMon:
  1395.                 if(currentSetDict['Nature'] == setListMoves1Combined[nn]['Nature'] and
  1396.                 currentSetDict['Ability'] == setListMoves1Combined[nn]['Ability'] and
  1397.                 currentSetDict['Level'] == setListMoves1Combined[nn]['Level'] and
  1398.                 currentSetDict['Happiness'] == setListMoves1Combined[nn]['Happiness'] and
  1399.                 currentSetDict['Item'] == setListMoves1Combined[nn]['Item']):
  1400.                     if sum(AbsList(SubtractLists(currentSetDict['EVs'],setListMoves1Combined[nn]['EVs']))) <= EVthreshold*2:
  1401.                         if sum(AbsList(SubtractLists(currentSetDict['IVs'],setListMoves1Combined[nn]['IVs']))) <= IVthreshold:
  1402.                             # Ensure two movesets are equivalent except for one move
  1403.                             moveMatches1 = 0
  1404.                             moveMatches2 = 0
  1405.                             for move in currentSetDict['Moveset']:
  1406.                                 if move in setListMoves1Combined[nn]['Moveset']:
  1407.                                     moveMatches1 += 1
  1408.                                 else:
  1409.                                     nonCommonMove1 = move
  1410.                             for move in setListMoves1Combined[nn]['Moveset']:
  1411.                                 if move in currentSetDict['Moveset']:
  1412.                                     moveMatches2 += 1
  1413.                                 else:
  1414.                                     nonCommonMove2 = move
  1415.                             if len(setListMoves1Combined[nn]['Moveset']) == len(currentSetDict['Moveset']):
  1416.                                 if moveMatches1 == moveMatches2:
  1417.                                     if moveMatches1 + 1 == len(setListMoves1Combined[nn]['Moveset']):
  1418.                                         # Moves are guaranteed to be in order of popularity by previous construction
  1419.                                         if bool(setListMoves1Combined[nn]['SharedMoves1']):
  1420.                                             if nonCommonMove2 in setListMoves1Combined[nn]['SharedMoves1']:
  1421.                                                 setListMoves1Combined[nn]['SharedMoves1'][nonCommonMove1] = currentCount
  1422.                                                 matchIndex = nn  
  1423.                                                 setListMoves1Combined[nn]['CountMoves'] += currentCount
  1424.                                         else:
  1425.                                             setListMoves1Combined[nn]['SharedMoves1'][nonCommonMove2] = setListMoves1Combined[nn]['CountMoves']
  1426.                                             setListMoves1Combined[nn]['SharedMoves1'][nonCommonMove1] = currentCount
  1427.                                             matchIndex = nn
  1428.                                             setListMoves1Combined[nn]['CountMoves'] += currentCount
  1429.             else:
  1430.                 chosenMonIdxMoves1Combined = len(setListMoves1Combined)
  1431.                 chosenMon = currentSetDict['Name']
  1432.             nn += 1
  1433.         if n == 0:
  1434.             chosenMonIdxMoves1Combined = len(setListMoves1Combined)
  1435.             chosenMon = currentSetDict['Name']
  1436.         if matchIndex < 0:
  1437.             setListMoves1Combined.append(currentSetDict)
  1438.  
  1439.     ## Sorting
  1440.     setListMoves1CombinedRank = [0]*len(setListMoves1Combined)
  1441.     for ii in range(0,len(setListMoves1Combined)):
  1442.         name = setListMoves1Combined[ii]['Name']
  1443.         count = setListMoves1Combined[ii]['CountMoves']
  1444.         frontIdent1 = ord(name[0])
  1445.         frontIdent2 = ord(name[1])
  1446.         posSeparator = max(name.find('-'),name.find(' '))
  1447.         if posSeparator > -1:
  1448.             backIdent1 = ord(name[posSeparator+1])
  1449.             if posSeparator + 2 < len(name):
  1450.                 backIdent2 = ord(name[posSeparator+2])
  1451.             else:
  1452.                 backIdent2 = 0
  1453.         else:
  1454.             backIdent1 = ord(name[-2])
  1455.             backIdent2 = ord(name[-1])
  1456.         setListMoves1CombinedRank[ii] = (frontIdent1*2**24 + frontIdent2*2**16 + backIdent1*2**8 + backIdent2)*2**10 - count
  1457.     setListMoves1Sorted = [setListMoves1Combined[i[0]] for i in sorted(enumerate(setListMoves1CombinedRank), key=lambda x:x[1])]
  1458.  
  1459.     ## Aggregates sets by move equivalence up to two slots
  1460.  
  1461.     setListMoves2Combined = list() # list of dicts of full sets
  1462.     chosenMon = '';
  1463.     chosenMonIdxMoves2Combined = 0;
  1464.     for n in range(len(setListMoves1Sorted)):
  1465.         currentSetDict = setListMoves1Sorted[n]
  1466.         currentSetDict['SharedMoves2'] = dict()
  1467.         currentCount = setListMoves1Sorted[n]['CountMoves']
  1468.         currentSetDict['CountMoves'] = setListMoves1Sorted[n]['CountMoves']
  1469.         # determine set equivalence except EVs and IVs
  1470.         matchIndex = -1; # matching set index
  1471.         nn = chosenMonIdxMoves2Combined # iterator
  1472.         while (matchIndex < 0 and nn < len(setListMoves2Combined) and combineMoves >= 2):
  1473.             if currentSetDict['Name'] == chosenMon:
  1474.                 if(currentSetDict['Nature'] == setListMoves2Combined[nn]['Nature'] and
  1475.                 currentSetDict['Ability'] == setListMoves2Combined[nn]['Ability'] and
  1476.                 currentSetDict['Level'] == setListMoves2Combined[nn]['Level'] and
  1477.                 currentSetDict['Happiness'] == setListMoves2Combined[nn]['Happiness'] and
  1478.                 currentSetDict['Item'] == setListMoves2Combined[nn]['Item']):
  1479.                     if sum(AbsList(SubtractLists(currentSetDict['EVs'],setListMoves2Combined[nn]['EVs']))) <= EVthreshold*2:
  1480.                         if sum(AbsList(SubtractLists(currentSetDict['IVs'],setListMoves2Combined[nn]['IVs']))) <= IVthreshold:
  1481.                             moveMatches1 = 0
  1482.                             moveMatches2 = 0
  1483.                             for move in currentSetDict['Moveset']:
  1484.                                 if move in setListMoves2Combined[nn]['Moveset']:
  1485.                                     moveMatches1 += 1
  1486.                                 else:
  1487.                                     nonCommonMove1 = move
  1488.                             for move in setListMoves2Combined[nn]['Moveset']:
  1489.                                 if move in currentSetDict['Moveset']:
  1490.                                     moveMatches2 += 1
  1491.                                 else:
  1492.                                     nonCommonMove2 = move
  1493.                             if len(setListMoves2Combined[nn]['Moveset']) == len(currentSetDict['Moveset']):
  1494.                                 if moveMatches1 == moveMatches2:
  1495.                                     if moveMatches1 + 1 == len(setListMoves2Combined[nn]['Moveset']):
  1496.                                         # Compare equivalence and order of previously slashed moves
  1497.                                         if set(currentSetDict['SharedMoves1'].keys()) == set(setListMoves2Combined[nn]['SharedMoves1'].keys()):
  1498.                                             order1 = [currentSetDict['SharedMoves1'][key] for key in currentSetDict['SharedMoves1'].keys()]
  1499.                                             order2 = [setListMoves2Combined[nn]['SharedMoves1'][key] for key in currentSetDict['SharedMoves1'].keys()]
  1500.                                             r = 0
  1501.                                             corr = True
  1502.                                             while r < len(order1) and corr == True:
  1503.                                                 s = 0
  1504.                                                 while s < r and corr == True:
  1505.                                                     corr = (order1[r] <= order1[s] and order2[r] <= order2[s]) or (order1[r] >= order1[s] and order2[r] >= order2[s])
  1506.                                                     s += 1
  1507.                                                 r += 1
  1508.                                             if corr:
  1509.                                                 if bool(setListMoves2Combined[nn]['SharedMoves2']):
  1510.                                                     if nonCommonMove2 in setListMoves2Combined[nn]['SharedMoves2']:
  1511.                                                         setListMoves2Combined[nn]['SharedMoves2'][nonCommonMove1] = currentSetDict['SharedMoves1']
  1512.                                                         matchIndex = nn  
  1513.                                                         setListMoves2Combined[nn]['CountMoves'] += currentCount
  1514.                                                 else:
  1515.                                                     setListMoves2Combined[nn]['SharedMoves2'][nonCommonMove2] = setListMoves2Combined[nn]['SharedMoves1']
  1516.                                                     setListMoves2Combined[nn]['SharedMoves2'][nonCommonMove1] = currentSetDict['SharedMoves1']
  1517.                                                     matchIndex = nn
  1518.                                                     setListMoves2Combined[nn]['CountMoves'] += currentCount
  1519.             else:
  1520.                 chosenMonIdxMoves2Combined = len(setListMoves2Combined)
  1521.                 chosenMon = currentSetDict['Name']
  1522.             nn += 1
  1523.         if n == 0:
  1524.             chosenMonIdxMoves2Combined = len(setListMoves2Combined)
  1525.             chosenMon = currentSetDict['Name']
  1526.         if matchIndex < 0:
  1527.             setListMoves2Combined.append(currentSetDict)
  1528.  
  1529.     ## Sorting
  1530.     setListMoves2CombinedRank = [0]*len(setListMoves2Combined)
  1531.     for ii in range(0,len(setListMoves2Combined)):
  1532.         name = setListMoves2Combined[ii]['Name']
  1533.         count = setListMoves2Combined[ii]['CountMoves']
  1534.         frontIdent1 = ord(name[0])
  1535.         frontIdent2 = ord(name[1])
  1536.         posSeparator = max(name.find('-'),name.find(' '))
  1537.         if posSeparator > -1:
  1538.             backIdent1 = ord(name[posSeparator+1])
  1539.             if posSeparator + 2 < len(name):
  1540.                 backIdent2 = ord(name[posSeparator+2])
  1541.             else:
  1542.                 backIdent2 = 0
  1543.         else:
  1544.             backIdent1 = ord(name[-2])
  1545.             backIdent2 = ord(name[-1])
  1546.         setListMoves2CombinedRank[ii] = (frontIdent1*2**24 + frontIdent2*2**16 + backIdent1*2**8 + backIdent2)*2**10 - count
  1547.     setListMoves2Sorted = [setListMoves2Combined[i[0]] for i in sorted(enumerate(setListMoves2CombinedRank), key=lambda x:x[1])]
  1548.  
  1549.     ## Print team set statistics to file
  1550.     f1 = open(foutTemplate + '_' + gen + '_usage_sets_statistics' + '.txt','w',encoding='utf-8', errors='ignore')
  1551.     f2 = open(foutTemplate + '_' + gen + '_synergy_sets_statistics' + '.txt','w',encoding='utf-8', errors='ignore')
  1552.     f3 = open(foutTemplate + '_' + gen + '_statistics_legend' + '.txt','w',encoding='utf-8', errors='ignore')
  1553.     totalMons = sum(list(monFrequency.values()))
  1554.     if len(setList) > 0 and len(categoryNics) > 0:
  1555.         maxNameLen = max([len(categoryNics[c]) for c in categoryNics])
  1556.     else:
  1557.         maxNameLen = 18
  1558.    
  1559.     for f in [f1,f2,f3]:
  1560.         if analyzeTeams:
  1561.             f.write('Built from ' + foutTemplate + '.txt\n')
  1562.             f.write('-'*50 + '\n')
  1563.             f.write('Parameters:\n')
  1564.             for w in usageWeight[0:-1]:
  1565.                 f.write('{:.3f}'.format(w))
  1566.                 f.write(', ')
  1567.             f.write('{:.3f}'.format(usageWeight[-1]) + '\n')
  1568.             f.write('importantItems: ')
  1569.             for i in importantItems[0:-1]:
  1570.                 f.write(i)
  1571.                 f.write(', ')
  1572.             f.write(importantItems[-1] + '\n')
  1573.             f.write('movePairSynergyThreshold: ' + '{:.3f}'.format(movePairSynergyThreshold) + '\n')
  1574.             f.write('movePairInTripletSynergyThreshold: ' + '{:.3f}'.format(movePairInTripletSynergyThreshold) + '\n')
  1575.             f.write('moveTripletSynergyThreshold: ' + '{:.3f}'.format(moveTripletSynergyThreshold) + '\n')
  1576.             f.write('moveProbThreshold: ' + '{:.3f}'.format(moveProbThreshold) + '\n')
  1577.             f.write('moveProbInTripletThreshold: ' + '{:.3f}'.format(moveProbInTripletThreshold) + '\n')
  1578.             f.write('moveCountThreshold: ' + str(moveCountThreshold) + '\n')
  1579.             f.write('sumMoveProbThreshold: ' + '{:.3f}'.format(sumMoveProbThreshold) + '\n')
  1580.             f.write('sumMoveProbTripletThreshold: ' + '{:.3f}'.format(sumMoveProbTripletThreshold) + '\n')
  1581.             f.write('namingIgnoreCategoryNum: ' + str(namingIgnoreCategoryNum) + '\n')
  1582.             f.write('namingExclusionMoveThreshold: 1/4 * ' + '{:.3f}'.format(namingExclusionMoveThreshold*4) + '\n')
  1583.             f.write('namingMinMoveProb: 1/4 * ' + '{:.3f}'.format(namingMinMoveProb*4) + '\n')
  1584.             f.write('namingExclusionCatThreshold: ' + '{:.3f}'.format(namingExclusionCatThreshold) + '\n')
  1585.             f.write('natureEVmodifier: ' + str(natureEVmodifier) + '\n')
  1586.             f.write('showMissingMonCores: ' + str(showMissingMonCores) + '\n')
  1587.             f.write('showMissingSetCores: ' + str(showMissingSetCores) + '\n')
  1588.             f.write('maxMissingMonCores: ' + str(maxMissingMonCores) + '\n')
  1589.             f.write('maxMissingSetCores: ' + str(maxMissingMonCores) + '\n')
  1590.             f.write('-'*50 + '\n\n')
  1591.  
  1592.             if f == f1 or f == f2:
  1593.                 if not teamPreview:
  1594.                     leadFrequencySorted = [(l,catLeadList[l]) for l in sorted(catLeadList, key=lambda x:catLeadList[x], reverse=True)]
  1595.                     f.write('Team Lead Arranged by Frequency\n')
  1596.                     f.write(' Counts | Freq (%) | Lead\n')
  1597.                     for (lead,freq) in leadFrequencySorted:
  1598.                         f.write((7-len(str(freq)))*' ' + str(freq))
  1599.                         f.write(' | ')
  1600.                         percentStr = "{:.3f}".format(freq/len(teamList)*100)
  1601.                         f.write((8-len(percentStr))*' ' + percentStr)
  1602.                         f.write(' | ')
  1603.                         f.write(' '*(maxNameLen-len(categoryNics[lead])) + categoryNics[lead])
  1604.                         f.write('\n')
  1605.                     f.write('\n')
  1606.                 for p in range(maxCoreNum):
  1607.                     if f == f1:
  1608.                         coreFrequencySorted = [(c,catCoreList[p][c]) for c in sorted(catCoreList[p], key=lambda x:catCoreList[p][x], reverse=True)]
  1609.                     elif f == f2:
  1610.                         coreFrequencySorted = [(c,catCoreList[p][c]) for c in sorted(catCoreList[p], key=lambda x:catCoreList[p][x]**usageWeight[p]*mpmiCatList[p][x], reverse=True)]
  1611.                     if p == 0:
  1612.                         f.write('Pokemon Arranged by Frequency\n')
  1613.                         f.write(' Counts | Freq (%) | Pokemon\n')
  1614.                     else:
  1615.                         f.write(str(p+1) + '-Cores Arranged by Frequency\n')
  1616.                         f.write(' Counts | Freq (%) | Synergy | Cores\n')
  1617.                     for (core,freq) in coreFrequencySorted:
  1618.                         if freq == 0:
  1619.                             continue
  1620.                         f.write((7-len(str(freq)))*' ' + str(freq))
  1621.                         f.write(' | ')
  1622.                         percentStr = "{:.3f}".format(freq/len(teamList)*100)
  1623.                         f.write((8-len(percentStr))*' ' + percentStr)
  1624.                         f.write(' | ')
  1625.                         if p > 0:
  1626.                             mpmiStr = "{:.2f}".format(mpmiCatList[p][core])
  1627.                             f.write((7-len(mpmiStr))*' ' + mpmiStr)
  1628.                             f.write(' | ')
  1629.                         for q in range(p):
  1630.                             f.write(' '*(maxNameLen-len(categoryNics[core[q]])) + categoryNics[core[q]])
  1631.                             f.write(', ')
  1632.                         f.write(' '*(maxNameLen-len(categoryNics[core[p]])) + categoryNics[core[p]])
  1633.                         f.write('\n')
  1634.                     f.write('\n')
  1635.             elif f == f3:
  1636.                 coreNameSorted = [(c,catCoreList[0][c]) for c in sorted(catCoreList[0], key=lambda x:x[0][0])]
  1637.                 f.write('Sets Legend in Alphabetical Order\n')
  1638.                 f.write(' Counts | Freq (%) | Pokemon\n')
  1639.                 for (core,freq) in coreNameSorted:
  1640.                     if freq == 0:
  1641.                         continue
  1642.                     f.write((7-len(str(freq)))*' ' + str(freq))
  1643.                     f.write(' | ')
  1644.                     percentStr = "{:.3f}".format(freq/len(teamList)*100)
  1645.                     f.write((8-len(percentStr))*' ' + percentStr)
  1646.                     f.write(' | ')
  1647.                     f.write(' '*(maxNameLen-len(categoryNics[core[0]])) + categoryNics[core[0]])
  1648.                     f.write('\n')
  1649.         else:
  1650.             monFrequencySorted = [(mon,monFrequency[mon]) for mon in sorted(monFrequency, key=lambda x:monFrequency[x], reverse=True)]
  1651.             f.write('Pokemon Arranged by Frequency\n')
  1652.             f.write(' Counts | Freq (%) | Pokemon\n')
  1653.             for (mon,freq) in monFrequencySorted:
  1654.                 f.write((7-len(str(freq)))*' ' + str(freq))
  1655.                 f.write(' | ')
  1656.                 percentStr = "{:.3f}".format(freq/totalMons*100)
  1657.                 f.write((8-len(percentStr))*' ' + percentStr)
  1658.                 f.write(' | ')
  1659.                 f.write(mon)
  1660.                 f.write('\n')
  1661.         f.close()
  1662.  
  1663.     ## Print team set statistics to csv
  1664.     f1 = open(foutTemplate + '_' + gen + '_usage_sets_statistics' + '.csv','w',encoding='utf-8', errors='ignore')
  1665.     f2 = open(foutTemplate + '_' + gen + '_synergy_sets_statistics' + '.csv','w',encoding='utf-8', errors='ignore')
  1666.     f3 = open(foutTemplate + '_' + gen + '_statistics_legend' + '.csv','w',encoding='utf-8', errors='ignore')
  1667.     totalMons = sum(list(monFrequency.values()))
  1668.     if len(setList) > 0 and len(categoryNics) > 0:
  1669.         maxNameLen = max([len(categoryNics[c]) for c in categoryNics])
  1670.     else:
  1671.         maxNameLen = 18
  1672.  
  1673.     for f in [f1,f2,f3]:
  1674.         if analyzeTeams:
  1675.             f.write('Built from ' + foutTemplate + '.txt\n')
  1676.             f.write('\n')
  1677.             f.write('Parameters,\n')
  1678.             for w in usageWeight[0:-1]:
  1679.                 f.write('{:.3f}'.format(w))
  1680.                 f.write(',')
  1681.             f.write('{:.3f}'.format(usageWeight[-1]) + '\n')
  1682.             f.write('importantItems,')
  1683.             for i in importantItems[0:-1]:
  1684.                 f.write(i)
  1685.                 f.write(',')
  1686.             f.write(importantItems[-1] + '\n')
  1687.             f.write('movePairSynergyThreshold,' + '{:.3f}'.format(movePairSynergyThreshold) + '\n')
  1688.             f.write('movePairInTripletSynergyThreshold,' + '{:.3f}'.format(movePairInTripletSynergyThreshold) + '\n')
  1689.             f.write('moveTripletSynergyThreshold,' + '{:.3f}'.format(moveTripletSynergyThreshold) + '\n')
  1690.             f.write('moveProbThreshold,' + '{:.3f}'.format(moveProbThreshold) + '\n')
  1691.             f.write('moveProbInTripletThreshold,' + '{:.3f}'.format(moveProbInTripletThreshold) + '\n')
  1692.             f.write('moveCountThreshold,' + str(moveCountThreshold) + '\n')
  1693.             f.write('sumMoveProbThreshold,' + '{:.3f}'.format(sumMoveProbThreshold) + '\n')
  1694.             f.write('sumMoveProbTripletThreshold,' + '{:.3f}'.format(sumMoveProbTripletThreshold) + '\n')
  1695.             f.write('namingIgnoreCategoryNum,' + str(namingIgnoreCategoryNum) + '\n')
  1696.             f.write('namingExclusionMoveThreshold,1/4*' + '{:.3f}'.format(namingExclusionMoveThreshold*4) + '\n')
  1697.             f.write('namingMinMoveProb,1/4*' + '{:.3f}'.format(namingMinMoveProb*4) + '\n')
  1698.             f.write('namingExclusionCatThreshold,' + '{:.3f}'.format(namingExclusionCatThreshold) + '\n')
  1699.             f.write('natureEVmodifier,' + str(natureEVmodifier) + '\n')
  1700.             f.write('showMissingMonCores,' + str(showMissingMonCores) + '\n')
  1701.             f.write('showMissingSetCores,' + str(showMissingSetCores) + '\n')
  1702.             f.write('maxMissingMonCores,' + str(maxMissingMonCores) + '\n')
  1703.             f.write('maxMissingSetCores,' + str(maxMissingMonCores) + '\n')
  1704.             f.write('\n\n')
  1705.  
  1706.             if f == f1 or f == f2:
  1707.                 if not teamPreview:
  1708.                     leadFrequencySorted = [(l,catLeadList[l]) for l in sorted(catLeadList, key=lambda x:catLeadList[x], reverse=True)]
  1709.                     f.write('Team Lead Arranged by Frequency\n')
  1710.                     f.write('Counts,Freq (%),Lead\n')
  1711.                     for (lead,freq) in leadFrequencySorted:
  1712.                         f.write(str(freq))
  1713.                         f.write(',')
  1714.                         percentStr = "{:.3f}".format(freq/len(teamList)*100)
  1715.                         f.write(percentStr)
  1716.                         f.write(',,')
  1717.                         f.write(categoryNics[lead])
  1718.                         f.write('\n')
  1719.                     f.write('\n')
  1720.                 for p in range(maxCoreNum):
  1721.                     if f == f1:
  1722.                         coreFrequencySorted = [(c,catCoreList[p][c]) for c in sorted(catCoreList[p], key=lambda x:catCoreList[p][x], reverse=True)]
  1723.                     elif f == f2:
  1724.                         coreFrequencySorted = [(c,catCoreList[p][c]) for c in sorted(catCoreList[p], key=lambda x:(catCoreList[p][x]+0.001)**usageWeight[p]*mpmiCatList[p][x], reverse=True)]
  1725.                     if p == 0:
  1726.                         f.write('Pokemon Arranged by Frequency\n')
  1727.                         f.write('Counts,Freq (%),,Pokemon\n')
  1728.                     else:
  1729.                         f.write(str(p+1) + '-Cores Arranged by Frequency\n')
  1730.                         f.write('Counts,Freq (%),Synergy,Cores\n')
  1731.                     for (core,freq) in coreFrequencySorted:
  1732.                         if freq == 0 and not showMissingSetCores:
  1733.                             continue
  1734.                         f.write(str(freq))
  1735.                         f.write(',')
  1736.                         percentStr = "{:.3f}".format(freq/len(teamList)*100)
  1737.                         f.write(percentStr)
  1738.                         f.write(',')
  1739.                         if p > 0:
  1740.                             mpmiStr = "{:.2f}".format(mpmiCatList[p][core])
  1741.                             f.write(mpmiStr)
  1742.                         f.write(',')
  1743.                         for q in range(p):
  1744.                             f.write(categoryNics[core[q]])
  1745.                             f.write(',')
  1746.                         f.write(categoryNics[core[p]])
  1747.                         f.write('\n')
  1748.                     f.write('\n')
  1749.             elif f == f3:
  1750.                 coreNameSorted = [(c,catCoreList[0][c]) for c in sorted(catCoreList[0], key=lambda x:x[0][0])]
  1751.                 f.write('Sets Legend in Alphabetical Order\n')
  1752.                 f.write('Counts,Freq (%),Pokemon\n')
  1753.                 for (core,freq) in coreNameSorted:
  1754.                     if freq == 0:
  1755.                         continue
  1756.                     f.write(str(freq))
  1757.                     f.write(',')
  1758.                     percentStr = "{:.3f}".format(freq/len(teamList)*100)
  1759.                     f.write(percentStr)
  1760.                     f.write(',')
  1761.                     f.write(categoryNics[core[0]])
  1762.                     f.write('\n')
  1763.         else:
  1764.             monFrequencySorted = [(mon,monFrequency[mon]) for mon in sorted(monFrequency, key=lambda x:monFrequency[x], reverse=True)]
  1765.             f.write('Pokemon Arranged by Frequency\n')
  1766.             f.write('Counts,Freq (%),Pokemon\n')
  1767.             for (mon,freq) in monFrequencySorted:
  1768.                 f.write(str(freq))
  1769.                 f.write(',')
  1770.                 percentStr = "{:.3f}".format(freq/totalMons*100)
  1771.                 f.write(percentStr)
  1772.                 f.write(',')
  1773.                 f.write(mon)
  1774.                 f.write('\n')
  1775.         f.close()
  1776.  
  1777.     ## Print statistics to file
  1778.     f1 = open(foutTemplate + '_' + gen + '_usage_statistics' + '.txt','w',encoding='utf-8', errors='ignore')
  1779.     f2 = open(foutTemplate + '_' + gen + '_synergy_statistics' + '.txt','w',encoding='utf-8', errors='ignore')
  1780.     totalMons = sum(list(monFrequency.values()))
  1781.     if len(setList) > 0:
  1782.         maxNameLen = max([len(s['Name']) for s in setList])
  1783.     else:
  1784.         maxNameLen = 18
  1785.    
  1786.     for f in [f1,f2]:
  1787.         if analyzeTeams:
  1788.             if not teamPreview:
  1789.                 leadFrequencySorted = [(l,leadList[l]) for l in sorted(leadList, key=lambda x:leadList[x], reverse=True)]
  1790.                 f.write('Team Lead Arranged by Frequency\n')
  1791.                 f.write(' Counts | Freq (%) | Lead\n')
  1792.                 for (lead,freq) in leadFrequencySorted:
  1793.                     f.write((7-len(str(freq)))*' ' + str(freq))
  1794.                     f.write(' | ')
  1795.                     percentStr = "{:.3f}".format(freq/len(teamList)*100)
  1796.                     f.write((8-len(percentStr))*' ' + percentStr)
  1797.                     f.write(' | ')
  1798.                     f.write(' '*(maxNameLen-len(lead)) + lead)
  1799.                     f.write('\n')
  1800.                 f.write('\n')
  1801.             for p in range(maxCoreNum):
  1802.                 if f == f1:
  1803.                     coreFrequencySorted = [(c,coreList[p][c]) for c in sorted(coreList[p], key=lambda x:coreList[p][x], reverse=True)]
  1804.                 elif f == f2:
  1805.                     coreFrequencySorted = [(c,coreList[p][c]) for c in sorted(coreList[p], key=lambda x:coreList[p][x]**usageWeight[p]*mpmiList[p][x], reverse=True)]
  1806.                 if p == 0:
  1807.                     f.write('Pokemon Arranged by Frequency\n')
  1808.                     f.write(' Counts | Freq (%) | Pokemon\n')
  1809.                 else:
  1810.                     f.write(str(p+1) + '-Cores Arranged by Frequency\n')
  1811.                     f.write(' Counts | Freq (%) | Synergy | Cores\n')
  1812.                 for (core,freq) in coreFrequencySorted:
  1813.                     if freq == 0 and not showMissingMonCores:
  1814.                         continue
  1815.                     f.write((7-len(str(freq)))*' ' + str(freq))
  1816.                     f.write(' | ')
  1817.                     percentStr = "{:.3f}".format(freq/len(teamList)*100)
  1818.                     f.write((8-len(percentStr))*' ' + percentStr)
  1819.                     f.write(' | ')
  1820.                     if p > 0:
  1821.                         mpmiStr = "{:.2f}".format(mpmiList[p][core])
  1822.                         f.write((7-len(mpmiStr))*' ' + mpmiStr)
  1823.                         f.write(' | ')
  1824.                     for q in range(p):
  1825.                         f.write(' '*(maxNameLen-len(core[q])) + core[q])
  1826.                         f.write(', ')
  1827.                     f.write(' '*(maxNameLen-len(core[p])) + core[p])
  1828.                     f.write('\n')
  1829.                 f.write('\n')
  1830.         else:
  1831.             monFrequencySorted = [(mon,monFrequency[mon]) for mon in sorted(monFrequency, key=lambda x:monFrequency[x], reverse=True)]
  1832.             f.write('Pokemon Arranged by Frequency\n')
  1833.             f.write(' Counts | Freq (%) | Pokemon\n')
  1834.             for (mon,freq) in monFrequencySorted:
  1835.                 f.write((7-len(str(freq)))*' ' + str(freq))
  1836.                 f.write(' | ')
  1837.                 percentStr = "{:.3f}".format(freq/totalMons*100)
  1838.                 f.write((8-len(percentStr))*' ' + percentStr)
  1839.                 f.write(' | ')
  1840.                 f.write(mon)
  1841.                 f.write('\n')
  1842.         f.close()
  1843.  
  1844.     ## Print archetype statistics to file
  1845.     if analyzeArchetypes:
  1846.         catCount = [(catCoreList[0][(catIndivList[x],)])**gammaArchetypes for x in range(np.shape(pMat)[1])]
  1847.         catCount = np.tile(np.array(catCount),(numArchetypes,1))
  1848.         archetypeStrength = np.sum(pMat*catCount,axis=1)
  1849.         archetypeOrder = sorted(range(numArchetypes),key=lambda x:archetypeStrength[x],reverse=True)
  1850.  
  1851.         f = open(foutTemplate + '_' + gen + '_archetype_statistics' + '.txt','w',encoding='utf-8', errors='ignore')
  1852.         totalMons = sum(list(monFrequency.values()))
  1853.         if len(setList) > 0:
  1854.             maxNameLen = max([len(categoryNics[c]) for c in categoryNics])
  1855.         else:
  1856.             maxNameLen = 18
  1857.  
  1858.         if analyzeTeams:
  1859.             f.write('Built from ' + foutTemplate + '.txt\n')
  1860.             f.write('-'*50 + '\n')
  1861.             f.write('Parameters:\n')
  1862.             for w in usageWeight[0:-1]:
  1863.                 f.write('{:.3f}'.format(w))
  1864.                 f.write(', ')
  1865.             f.write('{:.3f}'.format(usageWeight[-1]) + '\n')
  1866.             f.write('importantItems: ')
  1867.             for i in importantItems[0:-1]:
  1868.                 f.write(i)
  1869.                 f.write(', ')
  1870.             f.write(importantItems[-1] + '\n')
  1871.             f.write('movePairSynergyThreshold: ' + '{:.3f}'.format(movePairSynergyThreshold) + '\n')
  1872.             f.write('movePairInTripletSynergyThreshold: ' + '{:.3f}'.format(movePairInTripletSynergyThreshold) + '\n')
  1873.             f.write('moveTripletSynergyThreshold: ' + '{:.3f}'.format(moveTripletSynergyThreshold) + '\n')
  1874.             f.write('moveProbThreshold: ' + '{:.3f}'.format(moveProbThreshold) + '\n')
  1875.             f.write('moveProbInTripletThreshold: ' + '{:.3f}'.format(moveProbInTripletThreshold) + '\n')
  1876.             f.write('moveCountThreshold: ' + str(moveCountThreshold) + '\n')
  1877.             f.write('sumMoveProbThreshold: ' + '{:.3f}'.format(sumMoveProbThreshold) + '\n')
  1878.             f.write('sumMoveProbTripletThreshold: ' + '{:.3f}'.format(sumMoveProbTripletThreshold) + '\n')
  1879.             f.write('namingIgnoreCategoryNum: ' + str(namingIgnoreCategoryNum) + '\n')
  1880.             f.write('namingExclusionMoveThreshold: 1/4 * ' + '{:.3f}'.format(namingExclusionMoveThreshold*4) + '\n')
  1881.             f.write('namingMinMoveProb: 1/4 * ' + '{:.3f}'.format(namingMinMoveProb*4) + '\n')
  1882.             f.write('namingExclusionCatThreshold: ' + '{:.3f}'.format(namingExclusionCatThreshold) + '\n')
  1883.             f.write('natureEVmodifier: ' + str(natureEVmodifier) + '\n')
  1884.             f.write('showMissingMonCores: ' + str(showMissingMonCores) + '\n')
  1885.             f.write('showMissingSetCores: ' + str(showMissingSetCores) + '\n')
  1886.             f.write('maxMissingMonCores: ' + str(maxMissingMonCores) + '\n')
  1887.             f.write('maxMissingSetCores: ' + str(maxMissingMonCores) + '\n')
  1888.             f.write('analyzeArchetypes: ' + str(analyzeArchetypes) + '\n')
  1889.             f.write('exponent: ' + '{:.3f}'.format(exponent) + '\n')
  1890.             f.write('gammaSpectral: ' + '{:.3f}'.format(gammaSpectral) + '\n')
  1891.             f.write('numArchetypes: ' + str(numArchetypes) + '\n')
  1892.             f.write('gammaArchetypes: ' + '{:.3f}'.format(gammaArchetypes) + '\n')
  1893.             f.write('-'*50 + '\n\n')
  1894.             for ii in archetypeOrder:
  1895.                 f.write('Archetype ' + str(archetypeOrder.index(ii)+1) + '\n')
  1896.                 f.write(' Counts | Freq (%) | Confidence | Pokemon\n')
  1897.                 catFrequencySorted = sorted(range(np.shape(pMat)[1]),key=lambda x:pMat[ii,x]*(catCoreList[0][(catIndivList[x],)])**gammaArchetypes,reverse=True)
  1898.                 for catIndex in catFrequencySorted:
  1899.                     cat = catIndivList[catIndex]
  1900.                     freq = catCoreList[0][(cat,)]
  1901.                     if freq == 0:
  1902.                         continue
  1903.                     f.write((7-len(str(freq)))*' ' + str(freq))
  1904.                     f.write(' | ')
  1905.                     percentStr = "{:.3f}".format(freq/len(teamList)*100)
  1906.                     f.write((8-len(percentStr))*' ' + percentStr)
  1907.                     f.write(' | ')
  1908.                     confStr = "{:.2f}".format(pMat[ii,catIndex])
  1909.                     f.write((10-len(confStr))*' ' + confStr)
  1910.                     f.write(' | ')
  1911.                     f.write(' '*(maxNameLen-len(categoryNics[cat])) + categoryNics[cat])
  1912.                     f.write('\n')
  1913.                 f.write('\n')
  1914.         f.close()
  1915.  
  1916.     ## Print archetype statistics to csv
  1917.     if analyzeArchetypes:
  1918.         f = open(foutTemplate + '_' + gen + '_archetype_statistics' + '.csv','w',encoding='utf-8', errors='ignore')
  1919.         totalMons = sum(list(monFrequency.values()))
  1920.         if len(setList) > 0:
  1921.             maxNameLen = max([len(categoryNics[c]) for c in categoryNics])
  1922.         else:
  1923.             maxNameLen = 18
  1924.  
  1925.         if analyzeTeams:
  1926.             f.write('Built from ' + foutTemplate + '.txt\n')
  1927.             f.write('\n')
  1928.             f.write('Parameters,\n')
  1929.             for w in usageWeight[0:-1]:
  1930.                 f.write('{:.3f}'.format(w))
  1931.                 f.write(',')
  1932.             f.write('{:.3f}'.format(usageWeight[-1]) + '\n')
  1933.             f.write('importantItems,')
  1934.             for i in importantItems[0:-1]:
  1935.                 f.write(i)
  1936.                 f.write(',')
  1937.             f.write(importantItems[-1] + '\n')
  1938.             f.write('movePairSynergyThreshold,' + '{:.3f}'.format(movePairSynergyThreshold) + '\n')
  1939.             f.write('movePairInTripletSynergyThreshold,' + '{:.3f}'.format(movePairInTripletSynergyThreshold) + '\n')
  1940.             f.write('moveTripletSynergyThreshold,' + '{:.3f}'.format(moveTripletSynergyThreshold) + '\n')
  1941.             f.write('moveProbThreshold,' + '{:.3f}'.format(moveProbThreshold) + '\n')
  1942.             f.write('moveProbInTripletThreshold,' + '{:.3f}'.format(moveProbInTripletThreshold) + '\n')
  1943.             f.write('moveCountThreshold,' + str(moveCountThreshold) + '\n')
  1944.             f.write('sumMoveProbThreshold,' + '{:.3f}'.format(sumMoveProbThreshold) + '\n')
  1945.             f.write('sumMoveProbTripletThreshold,' + '{:.3f}'.format(sumMoveProbTripletThreshold) + '\n')
  1946.             f.write('namingIgnoreCategoryNum,' + str(namingIgnoreCategoryNum) + '\n')
  1947.             f.write('namingExclusionMoveThreshold,1/4*' + '{:.3f}'.format(namingExclusionMoveThreshold*4) + '\n')
  1948.             f.write('namingMinMoveProb,1/4*' + '{:.3f}'.format(namingMinMoveProb*4) + '\n')
  1949.             f.write('namingExclusionCatThreshold,' + '{:.3f}'.format(namingExclusionCatThreshold) + '\n')
  1950.             f.write('natureEVmodifier,' + str(natureEVmodifier) + '\n')
  1951.             f.write('showMissingMonCores,' + str(showMissingMonCores) + '\n')
  1952.             f.write('showMissingSetCores,' + str(showMissingSetCores) + '\n')
  1953.             f.write('maxMissingMonCores,' + str(maxMissingMonCores) + '\n')
  1954.             f.write('maxMissingSetCores,' + str(maxMissingMonCores) + '\n')
  1955.             f.write('showMissingMonCores,' + str(showMissingMonCores) + '\n')
  1956.             f.write('showMissingSetCores,' + str(showMissingSetCores) + '\n')
  1957.             f.write('maxMissingMonCores,' + str(maxMissingMonCores) + '\n')
  1958.             f.write('maxMissingSetCores,' + str(maxMissingMonCores) + '\n')
  1959.             f.write('analyzeArchetypes,' + str(analyzeArchetypes) + '\n')
  1960.             f.write('exponent,' + '{:.3f}'.format(exponent) + '\n')
  1961.             f.write('gammaSpectral,' + '{:.3f}'.format(gammaSpectral) + '\n')
  1962.             f.write('numArchetypes,' + str(numArchetypes) + '\n')
  1963.             f.write('gammaArchetypes,' + '{:.3f}'.format(gammaArchetypes) + '\n')
  1964.             f.write('\n\n')
  1965.  
  1966.             for ii in archetypeOrder:
  1967.                 f.write('Archetype ' + str(archetypeOrder.index(ii)+1) + '\n')
  1968.                 f.write('Counts,Freq (%),Confidence,Pokemon\n')
  1969.                 catFrequencySorted = sorted(range(np.shape(pMat)[1]),key=lambda x:pMat[ii,x]*(catCoreList[0][(catIndivList[x],)])**gammaArchetypes,reverse=True)
  1970.                 for catIndex in catFrequencySorted:
  1971.                     cat = catIndivList[catIndex]
  1972.                     freq = catCoreList[0][(cat,)]
  1973.                     if freq == 0:
  1974.                         continue
  1975.                     f.write(str(freq))
  1976.                     f.write(',')
  1977.                     percentStr = "{:.3f}".format(freq/len(teamList)*100)
  1978.                     f.write(percentStr)
  1979.                     f.write(',')
  1980.                     confStr = "{:.2f}".format(pMat[ii,catIndex])
  1981.                     f.write(confStr)
  1982.                     f.write(',')
  1983.                     f.write(categoryNics[cat])
  1984.                     f.write('\n')
  1985.                 f.write('\n')
  1986.         f.close()
  1987.  
  1988.     ## Print builder to file by gen
  1989.     if analyzeTeams and sortBuilder:
  1990.         f = open(foutTemplate + '_' + gen + '_sorted_builder' + '.txt','w',encoding='utf-8', errors='ignore')
  1991.         sortTeamsByLeadFrequency = sortTeamsByLeadFrequencyTeamPreview if teamPreview else sortTeamsByLeadFrequencyNoTeamPreview
  1992.         if analyzeArchetypes:
  1993.             catIndivDictInv = {v: k for k, v in enumerate(catIndivList)}
  1994.             def FindArchetype(x):
  1995.                 totalDeviation = [0]*numArchetypes
  1996.                 for c in x['Categories']:
  1997.                     if c not in catIndivDictInv:
  1998.                         numCatFeatures = len(c)
  1999.                         similarCatList = []
  2000.                         weightsSimilarCatList = []
  2001.                         for d in catIndivDictInv:
  2002.                             if d[0:numCatFeatures] == c:
  2003.                                 similarCatList.append(d)
  2004.                                 weightsSimilarCatList.append(catCoreList[0][(d,)])
  2005.                         weightsSimilarCatList = np.array(weightsSimilarCatList)/sum(weightsSimilarCatList)
  2006.                         # Find average distance
  2007.                         for n in range(numArchetypes):
  2008.                             if metricArchetypes == 0: # using distance
  2009.                                 distList = [np.sqrt(sum((cntr[:,n].transpose()-vk[catIndivDictInv[d],:])**2)) for d in similarCatList]
  2010.                                 averageDist = np.sum(np.array(distList)*weightsSimilarCatList)
  2011.                                 totalDeviation[n] += averageDist**gammaTeamAssignment
  2012.                             elif metricArchetypes == 1: # using prob values
  2013.                                 compProbList = [1 - pMat[n,catIndivDictInv[d]] for d in similarCatList] # complementary probability
  2014.                                 averageCompProb = np.sum(np.array(compProbList)*weightsSimilarCatList)
  2015.                                 totalDeviation[n] += averageCompProb**gammaTeamAssignment
  2016.                     else:
  2017.                         for n in range(numArchetypes):
  2018.                             if metricArchetypes == 0:
  2019.                                 totalDeviation[n] += np.sqrt(sum((cntr[:,n].transpose()-vk[catIndivDictInv[c],:])**2))**gammaTeamAssignment
  2020.                             elif metricArchetypes == 1:
  2021.                                 totalDeviation[n] += 1 - pMat[n,catIndivDictInv[c]]**gammaTeamAssignment
  2022.                 return totalDeviation.index(min(totalDeviation)), totalDeviation
  2023.         ## Define sort key
  2024.         def SortKey(x):
  2025.             keyList = list()
  2026.             if sortFolderByFrequency != 0 or sortFolderByAlphabetical != 0:
  2027.                 if sortFolderByFrequency != 0:
  2028.                     keyList.append(sortFolderByFrequency*folderCount[x['Folder']])
  2029.                 keyList.append(OrdString(x['Folder'].casefold(),ToBool(sortFolderByAlphabetical)))
  2030.            
  2031.             if analyzeArchetypes and sortTeamsByArchetype != 0:
  2032.                 chosenArchetype, dist = FindArchetype(x)
  2033.                 keyList.append(-sortTeamsByArchetype*archetypeOrder.index(chosenArchetype))
  2034.                 keyList.append(-sortTeamsByArchetype*dist)
  2035.  
  2036.             if sortTeamsByLeadFrequency != 0 or sortTeamsByCore != 0 or sortTeamsByAlphabetical != 0:
  2037.                 if sortTeamsByLeadFrequency != 0:
  2038.                     if teamPreview:
  2039.                         keyList.append(sortTeamsByLeadFrequency*coreList[0][(setList[x['Index'][0]]['Name'],)])
  2040.                     else:
  2041.                         keyList.append(sortTeamsByLeadFrequency*leadList[setList[x['Index'][0]]['Name']])
  2042.                     keyList.append(OrdString(setList[x['Index'][0]]['Name'],False))
  2043.                 if sortTeamsByCore != 0:
  2044.                     keyList.append(sortTeamsByCore*x['Score'][coreNumber-1])
  2045.                     keyList.append(OrdString(setList[x['Index'][0]]['Name'],False))
  2046.                 if sortTeamsByAlphabetical != 0:
  2047.                     keyList.append(OrdString(x['Name'].casefold(),ToBool(sortTeamsByAlphabetical)))
  2048.             return tuple(keyList)
  2049.        
  2050.         teamList.sort(key=SortKey)
  2051.        
  2052.         for n in range(len(teamList)):
  2053.             if teamList[n]['Anomalies'] > anomalyThreshold:
  2054.                 continue
  2055.             f.write('=== [' + gen + '] ')
  2056.             f.write(teamList[n]['Folder'])
  2057.             if analyzeArchetypes and printArchetypeLabel:
  2058.                 chosenArchetype, dist = FindArchetype(teamList[n])
  2059.                 f.write('Archetype ' + str(archetypeOrder.index(chosenArchetype)+1) + ' ')
  2060.             f.write(teamList[n]['Name'])
  2061.             f.write(' ===\n\n')
  2062.             for i in range(teamList[n]['Index'][0],teamList[n]['Index'][1]):
  2063.                 f.write(PrintSet(setList[i],moveFrequency,True,True,True,sortMovesByAlphabetical,sortMovesByFrequency))
  2064.                 f.write('\n')
  2065.             f.write('\n')        
  2066.         f.close()  
  2067.  
  2068.     ## Print sets to file
  2069.     for fracThreshold in ignoreSetsFraction:
  2070.         if fracThreshold == 0:
  2071.             fout = foutTemplate + '_' + gen + '_sets' + '.txt'
  2072.         else:
  2073.             fout = foutTemplate + '_' + gen + '_sets' + '_cut_' + str(int(1/fracThreshold)) +'.txt'
  2074.         f = open(fout,'w',encoding='utf-8', errors='ignore')
  2075.         f.write('Built from ' + foutTemplate + '.txt\n')
  2076.         f.write('-'*50 + '\n')
  2077.         f.write('Fraction of sets ignored: ')
  2078.         if fracThreshold == 0:
  2079.             f.write('None')
  2080.         else:
  2081.             f.write("{:.2f}".format(fracThreshold*100) + '% or 1/' + str(int(1/fracThreshold)))
  2082.         f.write('\n')
  2083.         f.write('EV movements ignored: ' + str(EVthreshold) + '\n')  
  2084.         f.write('IV sum deviation from 31 ignored: ')
  2085.         if IVthreshold < 31*6:
  2086.             f.write(str(IVthreshold))
  2087.         else:
  2088.             f.write('All')
  2089.         f.write('\n')
  2090.         f.write('Moveslots combined: ' + str(combineMoves) + '\n')
  2091.         f.write('Move Sort Order: ')
  2092.         if sortMovesByFrequency == 1:
  2093.             f.write('Increasing Frequency')
  2094.         elif sortMovesByFrequency == -1:
  2095.             f.write('Decreasing Frequency')
  2096.         elif sortMovesByAlphabetical == 1:
  2097.             f.write('Increasing Alphabetical')
  2098.         elif sortMovesByAlphabetical == -1:
  2099.             f.write('Decreasing Alphabetical')
  2100.         else:
  2101.             f.write('Retained From Import')
  2102.         f.write('\n')
  2103.         f.write('Show IVs when Hidden Power Type is ambiguous: ')
  2104.         f.write('Yes' if showIVs else 'No')
  2105.         f.write('\n')
  2106.         f.write('Show Shiny: ')
  2107.         f.write('Yes' if showShiny else 'No')
  2108.         f.write('\n')
  2109.         f.write('Show Nicknames: ')
  2110.         f.write('Yes' if showNicknames else 'No')
  2111.         f.write('\n')
  2112.         f.write('-'*50 + '\n')
  2113.         f.write('To read statistics:\n')
  2114.         f.write('Counts | Frequency given the same Pokemon (%)\n\n')
  2115.  
  2116.         for s in setListMoves2Sorted:
  2117.             frac = s['CountMoves']/monFrequency[s['Name']]
  2118.             if frac >= fracThreshold:
  2119.                 f.write(PrintSet(s,moveFrequency,showShiny,showIVs,showNicknames,sortMovesByAlphabetical,sortMovesByFrequency))
  2120.                 if showStatisticsInSets:                
  2121.                     f.write('-'*28 + '\n')
  2122.                     if bool(s['SharedMoves1']) or bool(s['SharedMoves2']):
  2123.                         moveCombinations = list()
  2124.                         if bool(s['SharedMoves2']):
  2125.                             for m2 in s['SharedMoves2']:
  2126.                                 for m1 in s['SharedMoves2'][m2]:
  2127.                                     moveCombinations.append((m1 + ' / ' + m2, s['SharedMoves2'][m2][m1]))
  2128.                         elif bool(s['SharedMoves1']):
  2129.                             for m in s['SharedMoves1']:
  2130.                                 moveCombinations.append((m, s['SharedMoves1'][m]))
  2131.                         moveCombinations.sort(key=lambda x:x[1],reverse=True)
  2132.                         maxMoveCombinationsNameLen = max([len(m[0]) for m in moveCombinations])
  2133.                         maxMoveCombinationsCountLen = max([len(str(m[1])) for m in moveCombinations])
  2134.                         for (m,c) in moveCombinations:
  2135.                             f.write(m + ' '*(maxMoveCombinationsNameLen-len(m)) + ': ')
  2136.                             f.write(' '*(maxMoveCombinationsCountLen-len(str(c))) + str(c) + ' | ' + "{:.1f}".format(c/monFrequency[s['Name']]*100) + '%\n')
  2137.                 f.write('Total: ' + str(s['CountMoves']) + ' | ' + "{:.1f}".format(frac*100) + '%\n')
  2138.                 f.write('\n')
  2139.         f.close()
  2140.    
  2141. ## Print incomplete teams
  2142. f = open(foutTemplate + '_incomplete' + '.txt','w',encoding='utf-8', errors='ignore')
  2143. teamListIncomplete.sort(key=lambda x:x['Line'])
  2144. for n in range(len(teamListIncomplete)):
  2145.     f.write('=== [' + teamListIncomplete[n]['Gen'] + '] ')
  2146.     f.write(teamListIncomplete[n]['Name'])
  2147.     f.write(' ===\n\n')
  2148.     for i in range(teamListIncomplete[n]['Index'][0],teamListIncomplete[n]['Index'][1]):
  2149.         f.write(PrintSet(setListIncomplete[i],moveFrequency,True,True,True,0,0))
  2150.         f.write('\n')
  2151.     f.write('\n')          
  2152. f.close()
  2153.    
  2154. ## Print entire builder to file
  2155. if analyzeTeams and sortBuilder:
  2156.     if sortGenByFrequency != 0:
  2157.         generation.sort(key=lambda x:numTeamsGen[x],reverse=ToBool(sortGenByFrequency))
  2158.     elif sortGenByAlphabetical != 0:
  2159.         generation.sort(reverse=ToBool(sortGenByAlphabetical))
  2160.     fo = open(foutTemplate + '_full_sorted_builder' + '.txt','w',encoding='utf-8', errors='ignore')
  2161.    
  2162.     if includeIncompleteTeams:
  2163.         fi = open(foutTemplate + '_incomplete' + '.txt',encoding='utf-8', errors='ignore')
  2164.         line = fi.readline()
  2165.         while line:
  2166.             fo.write(line)
  2167.             line = fi.readline()
  2168.         fi.close()
  2169.     for gen in generation:
  2170.         fi = open(foutTemplate + '_' + gen + '_sorted_builder' + '.txt', encoding='utf-8', errors='ignore')
  2171.         line = fi.readline()
  2172.         while line:
  2173.             fo.write(line)
  2174.             line = fi.readline()
  2175.         fi.close()
  2176.     fo.close()
  2177.  
  2178. print('Processing complete.')
  2179. print('Find your files in the same directory.')
  2180.  
Add Comment
Please, Sign In to add comment