Commit 4cc4c0cd7bb46d813e100d71d3d6e1f2538019f6

Authored by Olivier
1 parent 4adfe24734
Exists in master

logFileReader for the second round of experiments (5-12), with more info in the logs.

Showing 1 changed file with 1078 additions and 0 deletions Side-by-side Diff

scripts/logFileReader_v2.py View file @ 4cc4c0c
Diff suppressed. Click to show
  1 +#!/usr/bin/python
  2 +
  3 +import re
  4 +import sys
  5 +import os
  6 +import random
  7 +from functools import reduce #All of
  8 +from operator import mul #this is for
  9 +from fractions import Fraction #n choose k
  10 +from collections import defaultdict
  11 +
  12 +
  13 +#
  14 +#Methods
  15 +#
  16 +
  17 +#n choose k
  18 +def nChooseK(n,k):
  19 + return int( reduce(mul, (Fraction(n-i, i+1) for i in range(k)), 1) )
  20 +
  21 +#Counts the number of SNPs corresponding to the given ID by checking the superchagedMapping dict
  22 +#id is a string
  23 +def numberSNPsInside(id):
  24 +
  25 + if not superchargedMapping[id]: #not in the mapping
  26 + return 1
  27 +
  28 + else:
  29 + sum = 0
  30 +
  31 + for otherId in superchargedMapping[id]:
  32 + sum += numberSNPsInside(otherId)
  33 +
  34 + return sum
  35 +
  36 +#Recursive method that returns a list of strings, representing the ids of SNPs inside a supercharged SNP
  37 +def getListOfSNPsInsideSupercharged(snpId):
  38 +
  39 + if not superchargedMapping[snpId]:
  40 + #print("This SNP: " + snpId + " is not supercharged, returning it.")
  41 + return [getOriginalIdOfSNP(snpId)]
  42 +
  43 + else:
  44 + listOfSNPs = []
  45 + for otherId in superchargedMapping[snpId]:
  46 + #print("otherId = " + otherId + ", calling the recursive method on it.")
  47 + listOfSNPs.extend(getListOfSNPsInsideSupercharged(otherId)) #could be a list, this is why I use concat instead of append
  48 +
  49 + return listOfSNPs
  50 +
  51 +#Returns the Id of the original SNP corresponding to snpId; nbOfOriginalSNPs is a global variable representing the total number of original SNPs (comes from the graph file)
  52 +#snpId is a string, returns a string
  53 +def getOriginalIdOfSNP(snpId):
  54 +
  55 + snpId = int(snpId)
  56 + #print(str(snpId) + " = ") #test
  57 + if snpId > 0 and snpId <= nbOfOriginalSNPs:
  58 + return str(snpId)
  59 +
  60 + elif snpId < 0:
  61 + snpId *= -1 #change it to a positive value
  62 +
  63 + while snpId > nbOfOriginalSNPs:
  64 + snpId -= nbOfOriginalSNPs
  65 +
  66 + #print(str(snpId) + "\n")
  67 + return str(snpId)
  68 +
  69 + elif snpId == 0:
  70 + print("snpId == 0 --> Big problem!\nScript was stopped. Fix the bug :P") #should never get here
  71 + sys.exit(0)
  72 +
  73 + else: ###We have a supercharged SNP
  74 + return str(snpId)
  75 +
  76 +#Takes a list of SNP ids and returns a string representing the colors in common.
  77 +def findColorsInCommon(snpIdList):
  78 +
  79 + intersectionOfColors = [True] * totalNbOfColors
  80 +
  81 + for snpId in snpIdList:
  82 +
  83 + if int(snpId) > nbOfOriginalSNPs: #supercharged SNP
  84 + colorsOfSNP = findColorsInCommon(superchargedMapping[snpId])
  85 +
  86 + else:
  87 + colorsOfSNP = SNPtoColorsDict[getOriginalIdOfSNP(snpId)]
  88 +
  89 + for x in range(totalNbOfColors):
  90 + if str(x+1) not in colorsOfSNP:
  91 + intersectionOfColors[x] = False
  92 +
  93 + stringColCommon = ""
  94 + for x in range(totalNbOfColors):
  95 + if intersectionOfColors[x]:
  96 + if stringColCommon == "":
  97 + stringColCommon += str(x+1)
  98 + else:
  99 + stringColCommon += "," + str(x+1)
  100 +
  101 + return stringColCommon
  102 +
  103 +#Takes a snpId from a solution and the colorsInCommon of the solution, and puts it in all the indirect solutions possible (see declaration of allSolutions_indirect)
  104 +def lookForIndirectSolutions(snpId, colorsInCommon):
  105 +
  106 + for colors in allSolutions_indirect: #colors is the key
  107 + if checkIfColsAreIncluded(colors, colorsInCommon):
  108 + allSolutions_indirect[colors].add(snpId)
  109 +
  110 +#To check if a combination of colors is found inside the colors of a SNP. colCombination and SNPcols are strings.
  111 +#Returns True or False
  112 +def checkIfColsAreIncluded(colCombination, SNPcols):
  113 +
  114 + commaSplit = colCombination.split(",")
  115 + for color in commaSplit:
  116 + if color not in SNPcols:
  117 + return False
  118 +
  119 + return True
  120 +
  121 +
  122 +################################################################
  123 +################################################################
  124 +################################################################
  125 +
  126 +
  127 +if len(sys.argv) < 6:
  128 + print ("USAGE: python logFileReader.py superchargedMappingFile logFile graphFile.dimacs exactSolutionsFile inputLogFile [inter-experiments_statsFile]")
  129 + sys.exit()
  130 +
  131 +if len(sys.argv) == 7: #we have a inter-experiments_statsFile
  132 + interExpStatsFile = open(sys.argv[6], 'a') #in this file, we will gather information from the different experiments; will be used mostly to study perks
  133 + #First thing we want to do is print the current working directory, which should be a good descriptor of the experiment
  134 + interExpStatsFile.write(os.getcwd() + "\n")
  135 +
  136 +#
  137 +# Opening and reading the mapping file
  138 +#
  139 +
  140 +mappingFile = open(sys.argv[1], 'r')
  141 +superchargedMapping = defaultdict(list)
  142 +
  143 +for line in mappingFile:
  144 +
  145 + if line[0] == '/':
  146 + graphPath = line
  147 +
  148 + else:
  149 + colonSplit = line.split(": ")
  150 + superID = colonSplit[0]
  151 +
  152 + #checking if the superID is already in the mappingFile dict (should not)
  153 + if superchargedMapping[superID]: #evaluated as true if the list is not empty
  154 + print("Something is wrong: " + superID + " is already in the dict of supercharged SNPs.")
  155 +
  156 + else: #Not there yet, as expected
  157 + commaSplit = colonSplit[1].split(", ")
  158 +
  159 + for id in commaSplit:
  160 + superchargedMapping[superID].append(id.strip())
  161 +
  162 + if len(superchargedMapping[superID]) != 10:
  163 + print("Big problem: supercharged SNP " + superID + " does not contain 10 merged SNPs")
  164 +
  165 +mappingFile.close()
  166 +####TEST
  167 +#print(str(numberSNPsInside(2500)))
  168 +#print(str(numberSNPsInside(2501)))
  169 +#print(str(numberSNPsInside(2585)))
  170 +#sys.exit()
  171 +
  172 +
  173 +#
  174 +# Opening and reading the graph file
  175 +#
  176 +
  177 +graphFile = open(sys.argv[3], 'r')
  178 +
  179 +global nbOfOriginalSNPs, totalNbOfColors
  180 +nbOfOriginalSNPs = 0 #not counting the copies
  181 +SNPtoColorsDict = {} #a dict mapping the SNP id (string) to the list of colors (string, comma separated)
  182 +
  183 +for line in graphFile:
  184 +
  185 + spaceSplit = line.split(" ")
  186 +
  187 + if nbOfOriginalSNPs == 0: #we are reading the first line
  188 + nbOfOriginalSNPs = int(spaceSplit[0])
  189 + totalNbOfColors = int(spaceSplit[1].strip())
  190 +
  191 + else:
  192 + SNPtoColorsDict[spaceSplit[1]] = spaceSplit[2].strip()
  193 +
  194 +graphFile.close()
  195 +
  196 +#
  197 +# Opening and reading the exact solutions file
  198 +#
  199 +
  200 +exactSolFile = open(sys.argv[4], 'r')
  201 +
  202 +exactSolDict = defaultdict(set) ### We will put the exact solutions in a dict, where the key is the color (string) and the value is a set of all the SNP ids for that color (string too)
  203 +
  204 +allSolutions_indirect = {} ###NEW: for this dict, we will initialize a new set for each possible subset of colors (given in the exact solutions file), and we
  205 + ### will put in this dict the indirect solutions (e.g. a RED-GREEN-BLUE solution contains solutions for RED, GREEN, BLUE, RED-GREEN, RED-BLUE and GREEN-BLUE)
  206 +
  207 +for line in exactSolFile:
  208 +
  209 + colString = line.split(" ")[0]
  210 +
  211 + allSolutions_indirect[colString] = set() #initializing
  212 +
  213 + if colString == "\n":
  214 + break
  215 + SNPidIter = re.finditer(r'\'(\d+)\'', line)
  216 +
  217 + for id in SNPidIter:
  218 + exactSolDict[colString].add(id.group(1))
  219 +
  220 +
  221 +#
  222 +# Opening and reading the input log file
  223 +#
  224 +
  225 +inputFile = open(sys.argv[5], 'r')
  226 +
  227 +playerIdToUsernameDict = {} # a dict, mapping id to username
  228 +nbInputsDict = {} # a dict, mapping a player ID to its total number of inputs
  229 +perkLevelsDict = defaultdict(list) # a defaultdict of lists, will map a player ID to a list of 4 ints, representing the levels of the 4 skills in the same order as in the game
  230 + # which means: Buyout King, Color Expert, Sequence Collector and Master Trader
  231 +playerIdToXP = {} # a dict mapping a player ID to the player's total experience points
  232 +leaderboardSection = False #to indicate if we are at the end of the file, reading the info on the leaderboard
  233 +for line in inputFile:
  234 +
  235 + if line.strip() == "leaderboard":
  236 + leaderboardSection = True
  237 + continue
  238 +
  239 + if not leaderboardSection and line != "\n":
  240 + if line[0:4] != "Perk": #should be the line with username, ID and number of inputs
  241 + tabSplit = line.split("\t")
  242 + playerIdToUsernameDict[tabSplit[1]] = tabSplit[0]
  243 + nbInputsDict[tabSplit[1]] = int(tabSplit[2].strip())
  244 + #print("hola, tabsplit[0] = " + tabSplit[0] + " and tabSplit[1] = " + tabSplit[1])
  245 + currentPlayerId = tabSplit[1]
  246 +
  247 + else: #perks
  248 + spaceSplit = line.split(" ")
  249 + perkLevelsDict[currentPlayerId].append(int(spaceSplit[4].strip()))
  250 + #print("Current player id = " + currentPlayerId + " and perk level = " + spaceSplit[4].strip())
  251 +
  252 + if leaderboardSection:
  253 + colonSplit = line.split(" : ")
  254 + playerIdToXP[colonSplit[0]] = int(colonSplit[2].strip())
  255 +
  256 +#
  257 +# Opening and reading the log file
  258 +#
  259 +
  260 +logFile = open(sys.argv[2], 'r')
  261 +
  262 +#Sequences sold to the bank
  263 +seqSoldToBank = [] #saving all the solution for sequences sold to the bank
  264 +averageSizeSeqToBank = 0
  265 +sizesSeqToBankDict = defaultdict(int) #stores for all the possible sizes the frequencies
  266 +averageSizeSeqToBankConsideringSupercharge = 0
  267 +averageColorsSeqToBank = 0
  268 +nbColorsSeqToBankDict = defaultdict(int) #stores for all the possible numbers of colors the frequencies
  269 +averagePriceSeqToBank = 0
  270 +
  271 +playerID_sizes_seqSoldToBank = defaultdict(list) #dict that maps a player ID to the list of sizes of all the sequences he sold to the bank
  272 +playerID_sumOfSizes_seqSoldToBankConSuper = defaultdict(int) #dict that maps a player ID to an int representing the sum of seq lengths considering supercharge sold to the bank (for computing average)
  273 +playerID_nbColors_seqSoldToBank = defaultdict(list) #dict that maps a player ID to the list of nb of colors of all the sequences he sold to the bank
  274 +
  275 +forAdvancedStats_listSeqLengthsSoldToBank = [] #saves the seq length of every sequence sold to the bank
  276 +forAdvancedStats_listSeqLengthsSoldToBankConSuper = [] #saves the seq length considering super SNPs of every sequence sold to the bank
  277 +forAdvancedStats_listNbColsInCommonSoldToBank = [] #saves the nb of cols in common of every sequence sold to the bank
  278 +
  279 +#Sequences bought by other players
  280 +seqSoldToPlayer = []
  281 +averageSizeSeqToPlayer = 0
  282 +averageColorsSeqToPlayer = 0
  283 +averagePriceSeqToPlayer = 0
  284 +
  285 +playerID_sizes_seqToPlayer = defaultdict(list) #dict that maps a player ID to the list of sizes of all the sequences he bought
  286 +playerID_nbColors_seqToPlayer = defaultdict(list) #dict that maps a player ID to the list of nb of colors of all the sequences he bought
  287 +
  288 +#Individual SNPs sold to other players
  289 +snpSoldToPlayer = 0 #just saving the number of SNPs that were sold
  290 +colorsToPriceListDict = defaultdict(list) #This will record the list of the history of prices for each subset of colors
  291 +
  292 +playerID_nbColors_snpsBought = defaultdict(list) #dict that maps a player ID to the list of nb of colors of SNPs he bought (by bidding)
  293 +playerID_nbColors_snpsSold = defaultdict(list) #dict that maps a player ID to the list of nb of colors of SNPs he sold to other players
  294 +
  295 +####Challenges
  296 +challengeTypeCompletedDict = defaultdict(int) #counts the number of times each type of challenge has been completed
  297 +
  298 +playerID_challengesCompletedDict = defaultdict(lambda : defaultdict(int)) #dict that maps a player ID to a dict of challenge names mapping to the number of times it was completed
  299 +
  300 +currentChal = "" #the current challenge
  301 +
  302 +challengeTypeCounterDict = defaultdict(int) #To count the number of time each challenge appeared (not that the first and last challenge will probably have to be removed by hand)
  303 +
  304 +#Sell/buy challenge
  305 +nbSNPsSold_duringChal = 0
  306 +nbSNPsSold_notChal = 0
  307 +
  308 +#Min seq length challenge
  309 +averageSeqLength_duringChal = 0 #we are going to make a sum and divide it by the nb of sequences after
  310 +nbSeqsSold_duringChalSeqLen = 0
  311 +averageSeqLength_notChal = 0 #we are going to make a sum and divide it by the nb of sequences after
  312 +nbSeqsSold_notChalSeqLen = 0
  313 +
  314 +#Min nb colors in common
  315 +averageColCommon_duringChal = 0 #we are going to make a sum and divide it by the nb of sequences after
  316 +nbSeqsSold_duringChalColCom = 0
  317 +averageColCommon_notChal = 0 #we are going to make a sum and divide it by the nb of sequences after
  318 +nbSeqsSold_notChalColCom = 0
  319 +
  320 +#Gambles
  321 +totalNbGambles = 0
  322 +nbSuccessfulGambles = 0
  323 +averageChance = 0
  324 +
  325 +#Solutions, organized by colors in common (key)
  326 +allSolutions = defaultdict(set)
  327 +
  328 +playerID_allSolutions = defaultdict(lambda : defaultdict(set)) #a dict mapping each player to his allSolutions defaultdict(set)
  329 +
  330 +#FLAGS
  331 +skipLine = False #a flag to indicate if we should skip the line
  332 +insideChal = False #a flag to indicate that we are still parsing the challenge
  333 +
  334 +for line in logFile:
  335 +
  336 + if line[0] == '/':
  337 + skipLine = True #we will skip the next line which represents the time when the server was started
  338 + if graphPath != line:
  339 + print("Big problem: graph file in mapping file does not match graph file in log file.")
  340 + sys.exit()
  341 +
  342 + elif skipLine:
  343 + skipLine = False
  344 + continue;
  345 +
  346 + elif insideChal:
  347 + #print("skipping line describing challenge: " + line)
  348 + if "]]" in line:
  349 + insideChal = False #we finished parsing the challenge info
  350 +
  351 + else:
  352 + spaceSplit = line.split("\t")[1].split(" ") #getting rid of the first character and tab
  353 +
  354 + transactionList = [] # a list that will contain the information for the transaction
  355 +
  356 + for number in spaceSplit: #number can also be the id of the seller (and buyer)
  357 +
  358 + if number[len(number)-1] == ';': #removing the semicolon
  359 + number = number[:-1]
  360 +
  361 + transactionList.append(number.strip()) #warning: in the case of a V line (challenge), number is not necessarily a number
  362 +
  363 + if line[0] == 'B':
  364 + seqSoldToBank.append(transactionList)
  365 + size = int(transactionList[1])
  366 + averageSizeSeqToBank += size
  367 + sizesSeqToBankDict[size] += 1
  368 + forAdvancedStats_listSeqLengthsSoldToBank.append(size)
  369 + nbColors = int(transactionList[2])
  370 + averageColorsSeqToBank += nbColors
  371 + nbColorsSeqToBankDict[nbColors] += 1
  372 + forAdvancedStats_listNbColsInCommonSoldToBank.append(nbColors)
  373 + averagePriceSeqToBank += int(transactionList[3])
  374 +
  375 + #player reports
  376 + playerId = transactionList[0]
  377 + playerID_sizes_seqSoldToBank[playerId].append(size)
  378 + playerID_nbColors_seqSoldToBank[playerId].append(nbColors)
  379 + #
  380 +
  381 + sizeConSuper = 0 #this variable will contain the size of the sequence considering super SNPs
  382 + for x in range(4, len(transactionList)):
  383 + sizeConSuper += numberSNPsInside(transactionList[x])
  384 +
  385 + averageSizeSeqToBankConsideringSupercharge += sizeConSuper
  386 + forAdvancedStats_listSeqLengthsSoldToBankConSuper.append(sizeConSuper)
  387 + playerID_sumOfSizes_seqSoldToBankConSuper[playerId] += sizeConSuper
  388 +
  389 + #solutions
  390 + snpIdList = transactionList[4:]
  391 + colorsInCommonString = findColorsInCommon(snpIdList)
  392 + for snpId in snpIdList:
  393 + if superchargedMapping[snpId]:
  394 + for otherSNP in getListOfSNPsInsideSupercharged(snpId):
  395 + allSolutions[colorsInCommonString].add(getOriginalIdOfSNP(otherSNP))
  396 + #player reports
  397 + playerID_allSolutions[playerId][colorsInCommonString].add(getOriginalIdOfSNP(otherSNP))
  398 + #indirect solutions
  399 + lookForIndirectSolutions(getOriginalIdOfSNP(otherSNP), colorsInCommonString)
  400 + else:
  401 + allSolutions[colorsInCommonString].add(getOriginalIdOfSNP(snpId))
  402 + #player reports
  403 + playerID_allSolutions[playerId][colorsInCommonString].add(getOriginalIdOfSNP(snpId))
  404 + #indirect solutions
  405 + lookForIndirectSolutions(getOriginalIdOfSNP(snpId), colorsInCommonString)
  406 +
  407 + elif line[0] == 'F': #We use the first sell line ('F') for the challenge analysis
  408 +
  409 + size = int(transactionList[1])
  410 + nbColors = int(transactionList[2])
  411 +
  412 + if currentChal == "MIN_SEQ_LENGTH":
  413 + averageSeqLength_duringChal += size
  414 + nbSeqsSold_duringChalSeqLen += 1
  415 + else:
  416 + averageSeqLength_notChal += size
  417 + nbSeqsSold_notChalSeqLen += 1
  418 +
  419 + if currentChal == "MIN_NB_COLS_IN_COMMON":
  420 + averageColCommon_duringChal += nbColors
  421 + nbSeqsSold_duringChalColCom += 1
  422 + else:
  423 + averageColCommon_notChal += nbColors
  424 + nbSeqsSold_notChalColCom += 1
  425 +
  426 +
  427 +
  428 + elif line[0] == 'A':
  429 + seqSoldToPlayer.append(transactionList)
  430 + averageSizeSeqToPlayer += int(transactionList[1])
  431 + averageColorsSeqToPlayer += int(transactionList[2])
  432 + averagePriceSeqToPlayer += int(transactionList[3])
  433 +
  434 + #player reports
  435 + buyerId = transactionList[0].split(">")[1]
  436 + playerID_sizes_seqToPlayer[buyerId].append(int(transactionList[1]))
  437 + playerID_nbColors_seqToPlayer[buyerId].append(int(transactionList[2]))
  438 +
  439 + elif line[0] == 'S':
  440 + snpSoldToPlayer += 1
  441 + colors = findColorsInCommon([getOriginalIdOfSNP(transactionList[3])]) #sending a list to the method
  442 + colorsToPriceListDict[colors].append(int(transactionList[2]))
  443 +
  444 + #player reports
  445 + sellerId = transactionList[0].split(">")[0]
  446 + buyerId = transactionList[0].split(">")[1]
  447 + playerID_nbColors_snpsSold[sellerId].append(int(transactionList[1]))
  448 + playerID_nbColors_snpsBought[buyerId].append(int(transactionList[1]))
  449 +
  450 + #Challenge analysis
  451 + if currentChal == "SELL_BUY_SNPS":
  452 + nbSNPsSold_duringChal += 1
  453 + else:
  454 + nbSNPsSold_notChal += 1
  455 +
  456 + elif line[0] == 'V':
  457 + challengeTypeCompletedDict[transactionList[len(transactionList)-2]] += 1 #New: now the next to last index of transactionList corresponds to the challenge type
  458 +
  459 + #player reports
  460 + playerId = transactionList[0]
  461 + playerID_challengesCompletedDict[playerId][transactionList[len(transactionList)-1]] += 1
  462 +
  463 + elif line[0] == 'G':
  464 + totalNbGambles += 1
  465 + averageChance += float(transactionList[len(transactionList)-2]) #we are going to make the division at the end to get the average
  466 + if transactionList[len(transactionList)-1] == "SUCCESS":
  467 + nbSuccessfulGambles += 1
  468 +
  469 + elif line[0] == 'N': #for now we don't collect any information on the challenge
  470 + insideChal = True
  471 + #print("This should be a challenge: " + transactionList[0])
  472 + currentChal = transactionList[0]
  473 + challengeTypeCounterDict[currentChal] += 1
  474 +
  475 + elif line[0] == 'U': #for now we don't collect any information on the users logging in or logging out
  476 + continue
  477 +
  478 + else:
  479 + print("WARNING: unexpected line in log file")
  480 +
  481 +logFile.close()
  482 +if totalNbGambles != 0:
  483 + averageChance /= totalNbGambles
  484 +
  485 +## Preparing the outputs
  486 +
  487 +nbSeqsSoldToBank = len(seqSoldToBank)
  488 +averageSizeSeqToBank /= nbSeqsSoldToBank
  489 +averageSizeSeqToBankConsideringSupercharge /= nbSeqsSoldToBank
  490 +averageColorsSeqToBank /= nbSeqsSoldToBank
  491 +averagePriceSeqToBank /= nbSeqsSoldToBank
  492 +
  493 +nbSeqsSoldToPlayer = len(seqSoldToPlayer)
  494 +if nbSeqsSoldToPlayer != 0:
  495 + averageSizeSeqToPlayer /= nbSeqsSoldToPlayer
  496 + averageColorsSeqToPlayer /= nbSeqsSoldToPlayer
  497 + averagePriceSeqToPlayer /= nbSeqsSoldToPlayer
  498 +
  499 +print(str(snpSoldToPlayer) + " individual SNPs were sold to another player.\n")
  500 +print("Colors and price histories:")
  501 +for colors, priceList in colorsToPriceListDict.items():
  502 + print("- " + colors + ": " + str(priceList))
  503 +
  504 +print("\n" + str(nbSeqsSoldToBank) + " sequences were sold to the bank:")
  505 +print("- the average size of those sequence was: " + str(averageSizeSeqToBank))
  506 +print("- if we consider the contents of supercharged SNPs, the average size of the solutions found by the players was: " + str(averageSizeSeqToBankConsideringSupercharge))
  507 +print("- the average number of colors of those sequences was: " + str(averageColorsSeqToBank))
  508 +print("- the average price of those sequences was: " + str(averagePriceSeqToBank) + "\n")
  509 +
  510 +print(str(nbSeqsSoldToPlayer) + " sequences were sold to another player:")
  511 +print("- the average size of those sequence was: " + str(averageSizeSeqToPlayer))
  512 +print("- the average number of colors of those sequences was: " + str(averageColorsSeqToPlayer))
  513 +print("- the average price of those sequences was: " + str(averagePriceSeqToPlayer) + "\n")
  514 +
  515 +print("Challenges:")
  516 +for challenge, nb in challengeTypeCounterDict.items():
  517 + print("- " + challenge + " appeared " + str(nb) + " times")
  518 +
  519 +print()
  520 +
  521 +for challenge, nb in challengeTypeCompletedDict.items():
  522 + print("- " + challenge + " was completed " + str(nb) + " times")
  523 +
  524 +print("\n- Sell/Buy challenge:")
  525 +print("During: " + str(nbSNPsSold_duringChal) + " SNPs were sold")
  526 +print("Rest of the time: " + str(nbSNPsSold_notChal) + " SNPs were sold")
  527 +
  528 +print("\n- Min nb col in common challenge:")
  529 +averageColCommon_duringChal /= nbSeqsSold_duringChalColCom
  530 +print("During: " + str(averageColCommon_duringChal) + " was the average nb of colors in common (" + str(nbSeqsSold_duringChalColCom) + " seqs)")
  531 +averageColCommon_notChal /= nbSeqsSold_notChalColCom
  532 +print("Rest of the time: " + str(averageColCommon_notChal) + " was the average nb of colors in common (" + str(nbSeqsSold_notChalColCom) + " seqs)")
  533 +
  534 +print("\n- Min seq length challenge:")
  535 +averageSeqLength_duringChal /= nbSeqsSold_duringChalSeqLen
  536 +print("During: " + str(averageSeqLength_duringChal) + " was the average seq length (" + str(nbSeqsSold_duringChalSeqLen) + " seqs)")
  537 +averageSeqLength_notChal /= nbSeqsSold_notChalSeqLen
  538 +print("Rest of the time: " + str(averageSeqLength_notChal) + " was the average seq length (" + str(nbSeqsSold_notChalSeqLen) + " seqs)")
  539 +
  540 +
  541 +print("\nGambles:")
  542 +print("- Rate of successful gambles: " + str(nbSuccessfulGambles) + "/" + str(totalNbGambles))
  543 +print("- Average chance (or percentage of the search queue) used by the players: " + "{0:.2f}".format(averageChance*100) + "%")
  544 +
  545 +
  546 +print("\nAll Solutions")
  547 +totalNumberOfGameSolutions = 0
  548 +totalNumberOfExactSolutions = 0
  549 +totalNumberOfIndirectGameSolutions = 0 #new
  550 +numberOfGameSolutionsPerNbColor = [0] * (totalNbOfColors+1) #Cell 0 will be empty
  551 +numberOfExactSolutionsPerNbColor = [0] * (totalNbOfColors+1) #Cell 0 will be empty
  552 +for key in exactSolDict:
  553 + #Couting the number of SNP ids for this color
  554 + exactNb = len(exactSolDict[key])
  555 + gameNb = len(allSolutions[key])
  556 + indirectGameNb = len(allSolutions_indirect[key])
  557 +
  558 + #Counting the number of SNP ids for this number of colors
  559 + numberOfCols = len(key.split(","))
  560 + numberOfExactSolutionsPerNbColor[numberOfCols] += exactNb
  561 + numberOfGameSolutionsPerNbColor[numberOfCols] += gameNb
  562 +
  563 + allSolutionsString = " "
  564 + #allSolutionsIndirectString = str(allSolutions_indirect[key]) #for testing purposes only
  565 + if gameNb > 0:
  566 + allSolutionsString = str(allSolutions[key])
  567 + #Making sure that there is no mistake in the solutions of the game
  568 + for gameId in allSolutions[key]:
  569 + if gameId not in exactSolDict[key]:
  570 + print("BIG PROBLEM! GAME SOLUTION NOT CONTAINED IN EXACT SOLUTION")
  571 + sys.exit()
  572 + #Making sure that there are no mistakes in the indirect solutions of the game
  573 + for indirectGameId in allSolutions_indirect[key]:
  574 + if indirectGameId not in exactSolDict[key]:
  575 + print("BIG PROBLEM! **INDIRECT** GAME SOLUTION NOT CONTAINED IN EXACT SOLUTION")
  576 + sys.exit()
  577 +
  578 + print(key + ": " + allSolutionsString + " --> " + str(gameNb) + "/" + str(exactNb) + " (" + str(indirectGameNb) + "/" + str(exactNb) + " indirect)")
  579 + #print("indirect sols = " + allSolutionsIndirectString) #for testing purposes only
  580 + totalNumberOfExactSolutions += exactNb
  581 + totalNumberOfGameSolutions += gameNb
  582 + totalNumberOfIndirectGameSolutions += indirectGameNb
  583 +
  584 +solutionPercent = "{0:.2f}".format(float(totalNumberOfGameSolutions)/float(totalNumberOfExactSolutions) * 100) #format returns a string
  585 +print("\nIn total, the players have found " + solutionPercent + "% (" + str(totalNumberOfGameSolutions) + "/" + str(totalNumberOfExactSolutions) + ") of the solutions.\n")
  586 +#indirect:
  587 +solutionPercentInd = "{0:.2f}".format(float(totalNumberOfIndirectGameSolutions)/float(totalNumberOfExactSolutions) * 100) #format returns a string
  588 +print("\nConsidering indirect solutions, in total, the players have found " + solutionPercentInd + "% (" +
  589 + str(totalNumberOfIndirectGameSolutions) + "/" + str(totalNumberOfExactSolutions) + ") of the solutions.\n")
  590 +
  591 +#To help calculate the scoring function, based on the exact solutions
  592 +for x in range(1, totalNbOfColors+1):
  593 + print("For " + str(x) + " colors, there are on average " + "{0:.2f}".format(numberOfExactSolutionsPerNbColor[x] / float(nChooseK(totalNbOfColors, x))) +
  594 + " SNPs in the exact solutions.")
  595 +
  596 +################
  597 +#### Graphs ####
  598 +################
  599 +
  600 +#Size of sequences sold to the bank
  601 +file = open("sizesSeqsSoldToBank.dat", 'w')
  602 +for size in sorted(sizesSeqToBankDict):
  603 + file.write(str(size) + "\t" + str(sizesSeqToBankDict[size]) + "\n")
  604 +file.close()
  605 +
  606 +file = open("sizesSeqsSoldToBank.plot", "w")
  607 +file.write("unset key\n")
  608 +file.write("set boxwidth 0.75\n")
  609 +file.write("set style fill solid\n")
  610 +file.write("set title\"Size of sequences sold to the bank\"\n")
  611 +file.write("plot \"sizesSeqsSoldToBank.dat\" using 2:xtic(1) with boxes linecolor rgb \"steelblue\"\n")
  612 +file.write("set term pdfcairo\n")
  613 +file.write("set output \"sizesSeqsSoldToBank.pdf\"\n")
  614 +file.write("replot\n")
  615 +file.close()
  616 +
  617 +os.system("gnuplot sizesSeqsSoldToBank.plot")
  618 +
  619 +#Nb of colors of sequences sold to the bank
  620 +file = open("nbColorsSeqsSoldToBank.dat", 'w')
  621 +for nbColors in sorted(nbColorsSeqToBankDict):
  622 + file.write(str(nbColors) + "\t" + str(nbColorsSeqToBankDict[nbColors]) + "\n")
  623 +file.close()
  624 +
  625 +file = open("nbColorsSeqsSoldToBank.plot", 'w')
  626 +file.write("unset key\n")
  627 +file.write("set boxwidth 0.75\n")
  628 +file.write("set style fill solid\n")
  629 +file.write("set title\"Number of colors of sequences sold to the bank\"\n")
  630 +file.write("plot \"nbColorsSeqsSoldToBank.dat\" using 2:xtic(1) with boxes linecolor rgb \"steelblue\"\n")
  631 +file.write("set term pdfcairo\n")
  632 +file.write("set output \"nbColorsSeqsSoldToBank.pdf\"\n")
  633 +file.write("replot\n")
  634 +file.close()
  635 +
  636 +os.system("gnuplot nbColorsSeqsSoldToBank.plot")
  637 +
  638 +#Percentages of solutions found for each number of colors
  639 +file = open("solutionsPercentages.dat", 'w')
  640 +file2 = open("solutionsPerColor.dat", 'w')
  641 +file2.write("NbCols\tExact\tGame\n")
  642 +for x in range(1, totalNbOfColors+1):
  643 + percentage = "{0:.2f}".format(numberOfGameSolutionsPerNbColor[x] / numberOfExactSolutionsPerNbColor[x] * 100)
  644 + file.write(str(x) + "\t" + percentage + "\n")
  645 + file2.write(str(x) + "\t" + str(numberOfExactSolutionsPerNbColor[x]) + "\t" + str(numberOfGameSolutionsPerNbColor[x]) + "\n")
  646 +file.close()
  647 +file2.close()
  648 +
  649 +file = open("solutionsPercentages.plot", 'w')
  650 +file.write("unset key\n")
  651 +file.write("set boxwidth 0.75\n")
  652 +file.write("set yrange [0:100]\n")
  653 +file.write("set ytics 10\n")
  654 +file.write("set style fill solid\n")
  655 +file.write("set title\"Percentage of solutions found for each nb of colors\"\n")
  656 +file.write("plot \"solutionsPercentages.dat\" using 2:xtic(1) with boxes linecolor rgb \"steelblue\"\n")
  657 +file.write("set term pdfcairo\n")
  658 +file.write("set output \"solutionsPercentages.pdf\"\n")
  659 +file.write("replot\n")
  660 +file.close()
  661 +
  662 +os.system("gnuplot solutionsPercentages.plot")
  663 +
  664 +file2 = open("solutionsPerColor.plot", 'w')
  665 +file2.write("set auto x\n")
  666 +file2.write("set boxwidth 1.0\n")
  667 +file2.write("set style data histogram\n")
  668 +file2.write("set style histogram cluster gap 1\n")
  669 +file2.write("set style fill solid border -1\n")
  670 +file2.write("set title\"Number of solutions found for each nb of colors\"\n")
  671 +file2.write("plot \"solutionsPerColor.dat\" using 2:xtic(1) ti col fc rgb \"steelblue\", '' u 3 ti col fc rgb \"blue\"\n")
  672 +file2.write("set term pdfcairo\n")
  673 +file2.write("set output \"solutionsPerColor.pdf\"\n")
  674 +file2.write("replot\n")
  675 +file2.close()
  676 +
  677 +os.system("gnuplot solutionsPerColor.plot")
  678 +
  679 +
  680 +#Leaderboard and nb of inputs bar plots
  681 +nbOfPlayerIds = len(playerIdToXP)
  682 +fileXp = open("leaderboard.dat", 'w')
  683 +fileInputs = open("inputs.dat", 'w')
  684 +fileRatio = open("inputRatio.dat", 'w')
  685 +playerCounter = 1
  686 +averageRatio_all = 0
  687 +averageRatio_top5 = 0
  688 +averageRatio_others = 0
  689 +for playerId in sorted(playerIdToXP, key=playerIdToXP.get, reverse=True): #this should order the dict values in decreasing order
  690 + playerName_noSpaces = playerIdToUsernameDict[playerId].replace(" ", "_")
  691 + if(playerCounter <= 5): #First 5 players have a different color, that is written in the 3rd column of the data file
  692 + colorString = "\t1\n"
  693 + averageRatio_top5 += playerIdToXP[playerId] / nbInputsDict[playerId]
  694 + else:
  695 + colorString = "\t2\n"
  696 + averageRatio_others += playerIdToXP[playerId] / nbInputsDict[playerId]
  697 + averageRatio_all += playerIdToXP[playerId] / nbInputsDict[playerId]
  698 + fileXp.write(playerName_noSpaces + "\t" + str(playerIdToXP[playerId]) + colorString)
  699 + fileInputs.write(playerName_noSpaces + "\t" + str(nbInputsDict[playerId]) + colorString)
  700 + fileRatio.write(playerName_noSpaces + "\t" + str(playerIdToXP[playerId] / nbInputsDict[playerId]) + colorString)
  701 + playerCounter += 1
  702 +
  703 +#Adding average ratios for the best players, the others and all of them together
  704 +averageRatio_top5 /= 5
  705 +averageRatio_others /= (nbOfPlayerIds - 5)
  706 +averageRatio_all /= nbOfPlayerIds
  707 +fileRatio.write("Top5\t" + str(averageRatio_top5) + "\t1\n")
  708 +fileRatio.write("Others\t" + str(averageRatio_others) + "\t2\n")
  709 +fileRatio.write("All\t" + str(averageRatio_all) + "\t3\n")
  710 +
  711 +fileXp.close()
  712 +fileInputs.close()
  713 +fileRatio.close()
  714 +
  715 +file = open("leaderboardAndInputs.plot", 'w')
  716 +file.write("set term pdfcairo\n")
  717 +file.write("set output \"leaderboardAndInputs.pdf\"\n")
  718 +file.write("unset key\n")
  719 +file.write("unset colorbox\n")
  720 +file.write("set xtics rotate by -25 offset -1,0\n")
  721 +file.write("set palette defined (1 \"gold\", 2 \"steelblue\")\n")
  722 +file.write("set boxwidth 0.75\n")
  723 +file.write("set style fill solid\n")
  724 +file.write("set title \"Leaderboard\"\n")
  725 +file.write("plot \"leaderboard.dat\" using 0:2:3:xticlabel(1) with boxes lc palette\n")
  726 +
  727 +file.write("set title \"Inputs\"\n")
  728 +file.write("plot \"inputs.dat\" using 0:2:3:xticlabel(1) with boxes lc palette\n")
  729 +
  730 +file.write("set palette defined (1 \"gold\", 2 \"steelblue\", 3 \"#556B2F\")\n")
  731 +file.write("set title \"Xp / inputs ratio\"\n")
  732 +file.write("plot \"inputRatio.dat\" using 0:2:3:xticlabel(1) with boxes lc palette\n")
  733 +file.close()
  734 +
  735 +os.system("gnuplot leaderboardAndInputs.plot")
  736 +
  737 +##########################
  738 +# Creating a styles file #
  739 +##########################
  740 +
  741 +styles = open("styles.gp", 'w')
  742 +#Setting line styles for the linespoints graphs
  743 +styles.write("set style line 1 pt 1 ps 1.0 lw 3 lc rgb '#FF0000'\n")
  744 +styles.write("set style line 2 pt 2 ps 1.0 lw 3 lc rgb '#00FF00'\n")
  745 +styles.write("set style line 3 pt 3 ps 1.0 lw 3 lc rgb '#0000FF'\n")
  746 +styles.write("set style line 4 pt 4 ps 1.0 lw 3 lc rgb '#FFFF00'\n")
  747 +styles.write("set style line 5 pt 5 ps 1.0 lw 3 lc rgb '#FF00FF'\n")
  748 +styles.write("set style line 6 pt 6 ps 1.0 lw 3 lc rgb '#00FFFF'\n")
  749 +styles.write("set style line 7 pt 7 ps 1.0 lw 3 lc rgb '#000000'\n")
  750 +styles.write("set style line 8 pt 8 ps 1.0 lw 3 lc rgb '#800000'\n")
  751 +styles.write("set style line 9 pt 9 ps 1.0 lw 3 lc rgb '#008000'\n")
  752 +styles.write("set style line 10 pt 10 ps 1.0 lw 3 lc rgb '#000080'\n")
  753 +styles.write("set style line 11 pt 11 ps 1.0 lw 3 lc rgb '#808000'\n")
  754 +styles.write("set style line 12 pt 12 ps 1.0 lw 3 lc rgb '#800080'\n")
  755 +styles.write("set style line 13 pt 13 ps 1.0 lw 3 lc rgb '#008080'\n")
  756 +styles.write("set style line 14 pt 14 ps 1.0 lw 3 lc rgb '#808080'\n")
  757 +styles.write("set style line 15 pt 15 ps 1.0 lw 3 lc rgb '#C00000'\n")
  758 +styles.write("set style line 16 pt 16 ps 1.0 lw 3 lc rgb '#00C000'\n")
  759 +styles.write("set style line 17 pt 17 ps 1.0 lw 3 lc rgb '#0000C0'\n")
  760 +styles.write("set style line 18 pt 18 ps 1.0 lw 3 lc rgb '#C0C000'\n")
  761 +styles.write("set style line 19 pt 19 ps 1.0 lw 3 lc rgb '#C000C0'\n")
  762 +styles.write("set style line 20 pt 20 ps 1.0 lw 3 lc rgb '#00C0C0'\n")
  763 +styles.close()
  764 +
  765 +
  766 +##################
  767 +# Player reports #
  768 +##################
  769 +
  770 +playerCounter = 1
  771 +
  772 +#opening two files to show all the players in a graph for the sizes, and one for the nb of colors
  773 +allPlayersSizeFile = open("allPlayersSize_time.plot", 'w')
  774 +allPlayersSizeFile.write("set style data linespoints\n")
  775 +allPlayersSizeFile.write("set title\"Seq to bank - size time series\"\n")
  776 +allPlayersSizeFile.write("set yrange [1:10]\n")
  777 +allPlayersSizeFile.write("load \"styles.gp\"\n")
  778 +allPlayersSizeFile.write("plot -1 with line notitle\n") #should not appear, this is just to have a plot, so we can use replot later
  779 +
  780 +allPlayersNbColsFile = open("allPlayersNbCols_time.plot", 'w')
  781 +allPlayersNbColsFile.write("set style data linespoints\n")
  782 +allPlayersNbColsFile.write("set title\"Seq to bank - nb colors time series\"\n")
  783 +allPlayersNbColsFile.write("set yrange [1:" + str(totalNbOfColors) + "]\n")
  784 +allPlayersNbColsFile.write("load \"styles.gp\"\n")
  785 +allPlayersNbColsFile.write("plot -1 with line notitle\n") #should not appear, this is just to have a plot, so we can use replot later
  786 +
  787 +for playerId in playerIdToUsernameDict:
  788 +
  789 + if playerId not in playerIdToXP or playerIdToXP[playerId] == 0: #skipping the 'Test' player
  790 + continue
  791 +
  792 + if not os.path.exists("PlayerReports"):
  793 + os.makedirs("PlayerReports")
  794 + prefix = "PlayerReports/"
  795 + playerName = playerIdToUsernameDict[playerId]
  796 + playerReportFile = open(prefix + playerName + ".plot", 'w')
  797 +
  798 + #interExpStats file - player name
  799 + if 'interExpStatsFile' in locals() or 'interExpStatsFile' in globals():
  800 + interExpStatsFile.write("--- Player: " + playerName + "\n")
  801 +
  802 + #########
  803 + # Perks #
  804 + #########
  805 + #data file
  806 + perkDat = open(prefix + playerName + ".perks", 'w')
  807 + perkDat.write("BuyoutKing\t" + str(perkLevelsDict[playerId][0]) + "\n")
  808 + perkDat.write("ColorExpert\t" + str(perkLevelsDict[playerId][1]) + "\n")
  809 + perkDat.write("SequenceCollector\t" + str(perkLevelsDict[playerId][2]) + "\n")
  810 + perkDat.write("MasterTrader\t" + str(perkLevelsDict[playerId][3]) + "\n")
  811 + perkDat.close()
  812 +
  813 + #interExpStats file
  814 + if 'interExpStatsFile' in locals() or 'interExpStatsFile' in globals():
  815 + interExpStatsFile.write("Perks: " + str(perkLevelsDict[playerId]) + "\n")
  816 +
  817 + #gnuplot file
  818 + playerReportFile.write("set term pdfcairo\n")
  819 + playerReportFile.write("set output \"" + prefix + playerName + ".pdf\"\n")
  820 + playerReportFile.write("unset key\n")
  821 + playerReportFile.write("set boxwidth 0.75\n")
  822 + playerReportFile.write("set yrange [0:6]\n")
  823 + playerReportFile.write("set style fill solid\n")
  824 + playerReportFile.write("set title\"Skills chosen\"\n")
  825 + playerReportFile.write("plot \"" + prefix + playerName + ".perks\" using 2:xtic(1) with boxes linecolor rgb \"steelblue\"\n")
  826 +
  827 + ################
  828 + # Size to bank #
  829 + ################
  830 + averageSeqLength = 0 #for interExpStats
  831 + playerSizesSeqToBankList = [0] * (10)
  832 + for size in playerID_sizes_seqSoldToBank[playerId]:
  833 + playerSizesSeqToBankList[size-1] += 1 #we start to count from 0 in the list
  834 + averageSeqLength += size
  835 +
  836 + #interExpStats
  837 + if 'interExpStatsFile' in locals() or 'interExpStatsFile' in globals():
  838 + if len(playerID_sizes_seqSoldToBank[playerId]) > 0:
  839 + averageSeqLength /= len(playerID_sizes_seqSoldToBank[playerId])
  840 + averageSeqLengthConSuper = playerID_sumOfSizes_seqSoldToBankConSuper[playerId] / len(playerID_sizes_seqSoldToBank[playerId])
  841 + interExpStatsFile.write("NbSeqsSoldToBank: " + str(len(playerID_sizes_seqSoldToBank[playerId])) + "\n")
  842 + interExpStatsFile.write("AverageSeqLength: " + str(averageSeqLength) + "\n")
  843 + interExpStatsFile.write("AverageSeqLengthConSuper: " + str(averageSeqLengthConSuper) + "\n")
  844 +
  845 + sizeToBankDat = open(prefix + playerName + ".sizeToBank", 'w')
  846 + for x in range(10):
  847 + sizeToBankDat.write(str(x+1) + "\t" + str(playerSizesSeqToBankList[x]) + "\n")
  848 + sizeToBankDat.close()
  849 +
  850 + playerReportFile.write("set title\"Sequences sold to bank - sizes\"\n")
  851 + playerReportFile.write("set yrange [*:*]\n")
  852 + playerReportFile.write("plot \"" + prefix + playerName + ".sizeToBank\" using 2:xtic(1) with boxes linecolor rgb \"steelblue\"\n")
  853 +
  854 + #####################
  855 + # Nb colors to bank #
  856 + #####################
  857 + proportionOfSeqsWithMoreThanOneColor = 0 #for interExpStats
  858 + averageNbOfColors = 0 #for interExpStats
  859 + playerNbColorsToBankList = [0] * totalNbOfColors
  860 + for nbCols in playerID_nbColors_seqSoldToBank[playerId]:
  861 + playerNbColorsToBankList[nbCols-1] += 1 #we start to count from 0 in the list
  862 + averageNbOfColors += nbCols
  863 + if nbCols > 1:
  864 + proportionOfSeqsWithMoreThanOneColor += 1
  865 +
  866 + #interExpStats
  867 + averageNbOfColors /= len(playerID_nbColors_seqSoldToBank[playerId])
  868 + proportionOfSeqsWithMoreThanOneColor /= len(playerID_nbColors_seqSoldToBank[playerId])
  869 + if 'interExpStatsFile' in locals() or 'interExpStatsFile' in globals():
  870 + interExpStatsFile.write("AverageNbColors: " + str(averageNbOfColors) + "\n")
  871 + interExpStatsFile.write("MoreThan1Col: " + str(proportionOfSeqsWithMoreThanOneColor) + "\n")
  872 +
  873 + nbColsToBank = open(prefix + playerName + ".nbColsToBank", 'w')
  874 + for x in range(totalNbOfColors):
  875 + nbColsToBank.write(str(x+1) + "\t" + str(playerNbColorsToBankList[x]) + "\n")
  876 + nbColsToBank.close()
  877 +
  878 + playerReportFile.write("set title\"Sequences sold to bank - Nb colors\"\n")
  879 + playerReportFile.write("plot \"" + prefix + playerName + ".nbColsToBank\" using 2:xtic(1) with boxes linecolor rgb \"steelblue\"\n")
  880 +
  881 +
  882 + ###############
  883 + # Size buyout #
  884 + ###############
  885 + playerSizesSeqToPlayerList = [0] * 9 #should not be greater than 9
  886 + for size in playerID_sizes_seqToPlayer[playerId]:
  887 + playerSizesSeqToPlayerList[size-1] += 1 #we start to count from 0 in the list
  888 +
  889 + #interExpStats
  890 + if 'interExpStatsFile' in locals() or 'interExpStatsFile' in globals():
  891 + interExpStatsFile.write("NbBuyouts: " + str(len(playerID_sizes_seqToPlayer[playerId])) + "\n")
  892 +
  893 + sizeToPlayerDat = open(prefix + playerName + ".sizeToPlayer", 'w')
  894 + for x in range(9):
  895 + sizeToPlayerDat.write(str(x+1) + "\t" + str(playerSizesSeqToPlayerList[x]) + "\n")
  896 + sizeToPlayerDat.close()
  897 +
  898 + playerReportFile.write("set title\"Sequences bought - sizes\"\n")
  899 + playerReportFile.write("plot \"" + prefix + playerName + ".sizeToPlayer\" using 2:xtic(1) with boxes linecolor rgb \"steelblue\"\n")
  900 +
  901 +
  902 + ####################
  903 + # Nb colors buyout #
  904 + ####################
  905 + playerNbColorsToPlayerList = [0] * totalNbOfColors
  906 + for nbCols in playerID_nbColors_seqToPlayer[playerId]:
  907 + playerNbColorsToPlayerList[nbCols-1] += 1 #we start to count from 0 in the list
  908 +
  909 + nbColsToPlayer = open(prefix + playerName + ".nbColsToPlayer", 'w')
  910 + for x in range(totalNbOfColors):
  911 + nbColsToPlayer.write(str(x+1) + "\t" + str(playerNbColorsToPlayerList[x]) + "\n")
  912 + nbColsToPlayer.close()
  913 +
  914 + playerReportFile.write("set title\"Sequences bought - Nb colors\"\n")
  915 + playerReportFile.write("plot \"" + prefix + playerName + ".nbColsToPlayer\" using 2:xtic(1) with boxes linecolor rgb \"steelblue\"\n")
  916 +
  917 +
  918 + #######################
  919 + # Nb colors SNPs sold #
  920 + #######################
  921 + playerNbColorsSNPsSoldList = [0] * totalNbOfColors
  922 + for nbCols in playerID_nbColors_snpsSold[playerId]:
  923 + playerNbColorsSNPsSoldList[nbCols-1] += 1 #we start to count from 0 in the list
  924 +
  925 + #interExpStats
  926 + if 'interExpStatsFile' in locals() or 'interExpStatsFile' in globals():
  927 + interExpStatsFile.write("NbSNPsSold: " + str(len(playerID_nbColors_snpsSold[playerId])) + "\n")
  928 +
  929 + nbColsSNPsSold = open(prefix + playerName + ".nbColsSNPsSold", 'w')
  930 + for x in range(totalNbOfColors):
  931 + nbColsSNPsSold.write(str(x+1) + "\t" + str(playerNbColorsSNPsSoldList[x]) + "\n")
  932 + nbColsSNPsSold.close()
  933 +
  934 + playerReportFile.write("set title\"SNPs sold - Nb colors\"\n")
  935 + playerReportFile.write("plot \"" + prefix + playerName + ".nbColsSNPsSold\" using 2:xtic(1) with boxes linecolor rgb \"steelblue\"\n")
  936 +
  937 +
  938 + #########################
  939 + # Nb colors SNPs bought #
  940 + #########################
  941 + playerNbColorsSNPsBoughtList = [0] * totalNbOfColors
  942 + for nbCols in playerID_nbColors_snpsBought[playerId]:
  943 + playerNbColorsSNPsBoughtList[nbCols-1] += 1 #we start to count from 0 in the list
  944 +
  945 + nbColsSNPsBought = open(prefix + playerName + ".nbColsSNPsBought", 'w')
  946 + for x in range(totalNbOfColors):
  947 + nbColsSNPsBought.write(str(x+1) + "\t" + str(playerNbColorsSNPsBoughtList[x]) + "\n")
  948 + nbColsSNPsBought.close()
  949 +
  950 + playerReportFile.write("set title\"SNPs bought - Nb colors\"\n")
  951 + playerReportFile.write("plot \"" + prefix + playerName + ".nbColsSNPsBought\" using 2:xtic(1) with boxes linecolor rgb \"steelblue\"\n")
  952 +
  953 +
  954 + ##################
  955 + # Challenges won #
  956 + ##################
  957 + atLeastOne = False
  958 + chalCompleted = open(prefix + playerName + ".chalCompleted", 'w')
  959 + for challengeType, nb in sorted(playerID_challengesCompletedDict[playerId].items()):
  960 + atLeastOne = True
  961 + chalCompleted.write(challengeType + "\t" + str(nb) + "\n")
  962 + chalCompleted.close()
  963 +
  964 + if(atLeastOne):
  965 + playerReportFile.write("set title\"Challenges completed\"\n")
  966 + playerReportFile.write("set ytics 1\n")
  967 + playerReportFile.write("plot \"" + prefix + playerName + ".chalCompleted\" using 2:xticlabel(1) with boxes linecolor rgb \"steelblue\"\n")
  968 +
  969 +
  970 + ####################################
  971 + # Size to bank and nbColors / time #
  972 + ####################################
  973 + dataFileSize = open(prefix + playerName + ".sizeToBankTime", 'w')
  974 + dataFileNbCols = open(prefix + playerName + ".nbColsToBankTime", 'w')
  975 + atLeastOne = False
  976 + counter = 0;
  977 + averageSize = 0;
  978 + averageNbCols = 0;
  979 + timeSlice = 5; #specifies the length of the time slice (every 5 observations for example corresponds to a dot in the graph)
  980 + for x in range(len(playerID_sizes_seqSoldToBank[playerId])): #the 2 lists (size and nbCols) should be the same length
  981 + counter += 1
  982 + averageSize += playerID_sizes_seqSoldToBank[playerId][x]
  983 + averageNbCols += playerID_nbColors_seqSoldToBank[playerId][x]
  984 + if counter % timeSlice == 0:
  985 + atLeastOne = True
  986 + averageSize /= timeSlice
  987 + averageNbCols /= timeSlice
  988 + dataFileSize.write(str(counter) + "\t" + str(averageSize) + "\n")
  989 + dataFileNbCols.write(str(counter) + "\t" + str(averageNbCols) + "\n")
  990 + averageSize = 0 #reset the averages
  991 + averageNbCols = 0
  992 + dataFileSize.close()
  993 + dataFileNbCols.close()
  994 +
  995 + if atLeastOne:
  996 + playerReportFile.write("set style data linespoints\n")
  997 + playerReportFile.write("set title\"Seq to bank - size time series\"\n")
  998 + playerReportFile.write("set xtics auto\n")
  999 + playerReportFile.write("set yrange [1:10]\n")
  1000 + playerReportFile.write("plot \"" + prefix + playerName + ".sizeToBankTime\" using 1:2 with linespoints pt 13 ps 1.5 lw 3 linecolor rgb \"steelblue\"\n")
  1001 + #Plotting also in the file with all the other players
  1002 + allPlayersSizeFile.write("replot \"" + prefix + playerName + ".sizeToBankTime\" using 1:2 with linespoints ls " + str(playerCounter) + "title '" + playerName + "'\n") #using one of the line styles above
  1003 +
  1004 + playerReportFile.write("set title\"Seq to bank - nb colors time series\"\n")
  1005 + playerReportFile.write("set yrange [1:" + str(totalNbOfColors) + "]\n")
  1006 + playerReportFile.write("plot \"" + prefix + playerName + ".nbColsToBankTime\" using 1:2 with linespoints pt 13 ps 1.5 lw 3 linecolor rgb \"steelblue\"\n")
  1007 + #Plotting also in the file with all the other players
  1008 + allPlayersNbColsFile.write("replot \"" + prefix + playerName + ".nbColsToBankTime\" using 1:2 with linespoints ls " + str(playerCounter) + "title '" + playerName + "'\n") #using one of the line styles above
  1009 +
  1010 + ############################
  1011 + # #
  1012 + ############################
  1013 +
  1014 +
  1015 + #Calling gnuplot
  1016 + playerReportFile.close()
  1017 + os.system("gnuplot \"" + prefix + playerName + ".plot\"")
  1018 +
  1019 + #incrementing the player counter
  1020 + playerCounter += 1
  1021 +
  1022 +#printing the 2 time series with all the players
  1023 +allPlayersSizeFile.write("set term pdfcairo\n")
  1024 +allPlayersSizeFile.write("set output \"allPlayersSize_time.pdf\"\n")
  1025 +allPlayersSizeFile.write("replot")
  1026 +allPlayersSizeFile.close()
  1027 +
  1028 +allPlayersNbColsFile.write("set term pdfcairo\n")
  1029 +allPlayersNbColsFile.write("set output \"allPlayersNbColors_time.pdf\"\n")
  1030 +allPlayersNbColsFile.write("replot")
  1031 +allPlayersNbColsFile.close()
  1032 +
  1033 +os.system("gnuplot " + "allPlayersSize_time.plot")
  1034 +os.system("gnuplot " + "allPlayersNbCols_time.plot")
  1035 +
  1036 +
  1037 +###############################
  1038 +# Advanced stats output files #
  1039 +###############################
  1040 +
  1041 +if not os.path.exists("AdvancedStatsFiles"):
  1042 + os.makedirs("AdvancedStatsFiles")
  1043 +
  1044 +prefix = "AdvancedStatsFiles/"
  1045 +
  1046 +#List of seq sizes sold to the bank
  1047 +theFile = open(prefix + "listSeqSizesToBank.txt", 'w')
  1048 +