def backTester(logicCode, greaterLessCode, stat1Code, stat2Code, stat3Code):
	logicString = '{0:03b}'.format(logicCode)
	greaterLessString = '{0:03b}'.format(greaterLessCode)
	stat1String = '{0:08b}'.format(stat1Code)
	stat2String = '{0:08b}'.format(stat2Code)
	stat3String = '{0:08b}'.format(stat3Code)
	bitList = list(stat1String + stat2String + stat3String + logicString + greaterLessString)
	weeks = ["2014-2.txt", "2014-3.txt", "2014-4.txt", "2014-5.txt", "2014-6.txt", "2014-7.txt", "2014-8.txt", "2014-9.txt",\
			"2014-10.txt", "2014-11.txt", "2014-12.txt", "2014-13.txt", "2014-14.txt", "2014-15.txt", "2014-16.txt", "2014-17.txt"]
	columns = range(177, 193)
	statIDs = ['Opponent_Extra_Points_Made_per_Game', 'Third_Down_Conversions_per_Game', 'Opponent_Special_Teams_Points_per_Game_(Estimated)', 'Opponent_Completions_per_Game', 'Opponent_Takeaway_Fumble_Recovery_Percentage', 'Red_Zone_Scores_per_Game_(TDs_only)', 'Average_Scoring_Margin', 'Punt_Blocked_Percentage', '4th_Quarter_Time_of_Possession_Share_%', 'Opponent_Interceptions_Thrown_Percentage', 'Yards_per_Play', 'Points_per_Play_Margin', '2nd_Half_Time_of_Possession_Share_%', 'Rushing_Attempts_per_Game', 'Opponent_2nd_Half_Points_Game', 'Giveaways_per_Game', 'Rushing_Play_Percentage', 'Field_Goal_Got_Blocked_Percentage', 'Fumbles_Lost_per_Game', 'Opponent_Two_Point_Conversion_Attempts_per_Game', 'Average_Time_of_Possession_(Excluding_OT)', 'Yards_per_Pass_Attempt', 'Red_Zone_Scoring_Percentage_(TD_only)', 'Special_Teams_Touchdowns_per_Game', 'QB_Sacked_Percentage', 'Opponent_Penalties_per_Play', 'Opponent_Field_Goal_Attempts_per_Game', 'Opponent_Rushing_First_Downs_per_Game', 'Opponent_Punts_per_Offensive_Score', '3rd_Quarter_Time_of_Possession_Share_%', 'Opponent_Two_Point_Conversion_Percentage', 'Red_Zone_Scoring_Attempts_per_Game', 'Punts_per_Play', 'Opponent_Punts_per_Play', 'Passing_Touchdown_Percentage', 'Offensive_Point_Share_Percentage_(Estimated)', 'Rushing_Yards_per_Game', 'Opponent_Passing_Play_Percentage', 'Opponent_Third_Down_Conversion_Percentage', 'Fourth_Down_Conversions_per_Game', 'Opponent_Third_Down_Conversions_per_Game', 'Passing_Yards_Percentage', 'Opponent_Field_Goal_Conversion_Percentage_(Net_of_Blocks)', 'Sacks_per_Game', 'Fumbles_per_Game', 'Opponent_Pass_Attempts_per_Game', 'Defensive_Touchdowns_per_Game', 'Interceptions_Thrown_Percentage', 'Passing_Play_Percentage', 'Third_Down_Conversion_Percentage', 'Pass_Attempts_per_Game', 'Opponent_Red_Zone_Scores_per_Game_(TDs_only)', 'Yards_per_Point_Margin', 'Points_per_Play', '1st_Half_Time_of_Possession_Share_%', 'Passing_First_Down_Percentage', 'Opponent_Yards_per_Pass_Attempt', 'Opponent_Rushing_Yards_Percentage', 'Interceptions_Thrown_per_Game', 'Opponent_Net_Yards_per_Punt_Attempt', 'Opponent_Rushing_First_Down_Percentage', 'Opponent_Extra_Point_Conversion_Percentage', 'Field_Goal_Attempts_per_Game', 'Defensive_Points_per_Game_(Estimated)', 'Fumble_Recovery_Percentage', 'Offensive_Points_per_Game_(Estimated)', 'Opponent_Completion_Percentage', 'Overtime_Points_Game', 'Turnover_Margin_per_Game', '1st_Quarter_Time_of_Possession_Share_%', 'Yards_per_Game', 'Fumbles_Not_Lost_per_Game', 'Field_Goal_Conversion_Percentage', 'Opponent_Fourth_Down_Conversions_per_Game', '2nd_Quarter_Points_Game', 'Yards_per_Point', 'Offensive_Touchdowns_per_Game', 'Penalties_per_Play', '1st_Quarter_Points_Game', 'Fourth_Downs_per_Game', 'Opponent_Gross_Punt_Yards_per_Game', 'Opponent_Giveaway_Fumble_Recovery_Percentage', 'Opponent_Fumbles_Lost_per_Game', 'Field_Goals_Made_per_Game', 'Average_Team_Passer_Rating', 'Opponent_Passing_First_Down_Percentage', 'Opponent_Defensive_Points_per_Game_(Estimated)', 'Rushing_Touchdown_Percentage', 'Takeaway_Fumble_Recovery_Percentage', '2nd_Quarter_Time_of_Possession_Share_%', 'Opponent_Net_Punt_Yards_per_Game', 'Rushing_First_Down_Percentage', 'Opp_2nd_Quarter_Points_Game', 'Opponent_Rushing_Attempts_per_Game', 'Opponent_Yards_per_Play', 'QB_Sacked_per_Game', 'Passing_Touchdowns_per_Game', 'Opp_4th_Quarter_Points_Game', 'Opponent_Points_per_Field_Goal_Attempt', 'Opponent_Fumble_Recovery_Percentage', 'Opponent_Rushing_Touchdowns_per_Game', 'Net_Yards_per_Successful_Punt', 'Kickoff_Touchback_Percentage', 'Opponent_Touchbacks_per_Game', 'Field_Goals_Got_Blocked_per_Game', 'Opponent_Two_Point_Conversions_per_Game', 'Opponent_Yards_per_Completion', 'First_Downs_per_Play', 'Opponent_Passing_Yards_Percentage', 'Penalty_First_Downs_per_Game', 'Takeaways_per_Game', 'Opponent_Plays_per_Game', 'Block_Field_Goal_Percentage', 'Rushing_Touchdowns_per_Game', 'Safeties_per_Game', 'Rushing_Yards_Percentage', 'Incompletions_per_Game', 'Opponent_Red_Zone_Scoring_Percentage_(TD_only)', 'Penalty_Yards_per_Game', 'Opponent_Passing_Yards_per_Game', 'Extra_Point_Conversion_Percentage', 'Opponent_Gross_Yards_per_Successful_Punt', 'Yards_per_Completion', 'Opponent_Yards_per_Rush_Attempt', 'Block_Punt_Percentage', 'Passing_First_Downs_per_Game', 'Opponent_Rushing_Yards_per_Game', 'Touchbacks_per_Game', 'Opponent_Fumbles_Not_Lost_per_Game', 'Opponent_Passing_Touchdown_Percentage', 'Opponent_Points_per_Game', 'Opponent_Passing_First_Downs_per_Game', 'Gross_Punt_Yards_per_Game', 'Two_Point_Conversion_Attempts_per_Game', 'Opponent_Average_Team_Passer_Rating', 'Opponent_Field_Goal_Conversion_Percentage', 'Opponent_First_Downs_per_Game', 'Extra_Point_Attempts_per_Game', 'Punt_Attempts_per_Game', 'Opponent_Kickoff_Touchback_Percentage', 'Punts_per_Offensive_Score', 'Third_Downs_per_Game', 'Sack_Percentage', 'Opponent_Interceptions_Thrown_per_Game', 'Opponent_Penalty_First_Downs_per_Game', 'Opponent_Yards_per_Game', 'Opponent_Incompletions_per_Game', 'Giveaway_Fumble_Recovery_Percentage', 'Opponent_Field_Goals_Made_per_Game', 'Opponent_Net_Yards_per_Successful_Punt', 'Opponent_Extra_Point_Attempts_per_Game', 'Opponent_Points_per_Play', 'Gross_Yards_per_Successful_Punt', 'Fourth_Down_Conversion_Percentage', 'Opponent_Rushing_Play_Percentage', 'Opponent_Fourth_Downs_per_Game', 'Opponent_Offensive_Touchdowns_per_Game', 'Opponent_Non-Offensive_Touchdowns_per_Game', 'Opponent_Time_of_Possession_Percentage_(Net_of_OT)', 'Opp_3rd_Quarter_Points_Game', 'Opponent_Red_Zone_Scoring_Attempts_per_Game', 'Non-Offensive_Touchdowns_per_Game', 'Yards_per_Rush_Attempt', 'Completions_per_Game', 'Net_Yards_per_Punt_Attempt', 'Time_of_Possession_Percentage_(Net_of_OT)', 'Opponent_Fourth_Down_Conversion_Percentage', 'Penalty_Yards_per_Penalty', 'Opponent_Passing_Touchdowns_per_Game', 'Opponent_Punt_Attempts_per_Game', 'Points_per_Field_Goal_Attempt', 'Opponent_Safeties_per_Game', 'Special_Teams_Points_per_Game_(Estimated)', '1st_Half_Points_Game', 'Rushing_First_Downs_per_Game', 'Two_Point_Conversion_Percentage', 'Opponent_Kickofs_per_Game', 'Opponent_Defensive_Touchdowns_per_Game', 'Opponent_First_Downs_per_Play', 'Opp_1st_Quarter_Points_Game', 'Plays_per_Game', 'Opponent_Offensive_Point_Share_Percentage_(Estimated)', 'Opponent_Third_Downs_per_Game', 'Punts_Blocked_per_Game', 'First_Downs_per_Game', 'Opp_Yards_per_Point', 'Opp_Overtime_Points_Game', 'Field_Goals_Blocked_per_Game', 'Completion_Percentage', '4th_Quarter_Points_Game', 'Opponent_Penalty_Yards_per_Game', 'Passing_Yards_per_Game', '3rd_Quarter_Points_Game', 'Opponent_1st_Half_Points_Game', 'Two_Point_Conversions_per_Game', '2nd_Half_Points_Game', 'Points_per_Game', 'Touchdowns_per_Game', 'Opponent_Penalty_Yards_per_Penalty', 'Opponent_Penalties_per_Game', 'Extra_Points_Made_per_Game', 'Opponent_Average_Time_of_Possession_(Net_of_OT)', 'Opponent_Offensive_Points_per_Game_(Estimated)', 'Opponent_Rushing_Touchdown_Percentage', 'Net_Punt_Yards_per_Game', 'Opponent_Touchdowns_per_Game', 'Opponent_Special_Teams_Touchdowns_per_Game', 'Kickoffs_per_Game', 'Opponent_Fumbles_per_Game', 'Penalties_per_Game', 'Field_Goal_Conversion_Percentage_(Excluding_Blocks)']

	suspect = individual(bitList)
	fitness = suspect.getFitness(weeks, columns)
	print "Prediction rate:", float(fitness) / 240
	print "Using stats:", statIDs[stat1Code], " -- ", statIDs[stat2Code], " -- ", statIDs[stat3Code]
def geneticAlgorithm(popSize, stringLen, crossOverRate, mutationRate, generations):
	# 211 stats
	gen1 = []
	# weeks = ["2003-17.txt", "2004-17.txt", "2005-17.txt", "2006-17.txt", "2007-17.txt", "2008-17.txt", "2009-17.txt", "2010-17.txt", "2011-17.txt", "2012-17.txt", "2013-17.txt"]
	weeks = ["2010-2.txt", "2010-3.txt", "2010-4.txt", "2010-5.txt", "2010-6.txt", "2010-7.txt", "2010-8.txt", "2010-9.txt",\
			"2010-10.txt", "2010-11.txt", "2010-12.txt", "2010-13.txt", "2010-14.txt", "2010-15.txt", "2010-16.txt", "2010-17.txt",\
			 "2011-2.txt", "2011-3.txt", "2011-4.txt", "2011-5.txt", "2011-6.txt", "2011-7.txt", "2011-8.txt", "2011-9.txt",\
			"2011-10.txt", "2011-11.txt", "2011-12.txt", "2011-13.txt", "2011-14.txt", "2011-15.txt", "2011-16.txt", "2011-17.txt",\
			 "2012-2.txt", "2012-3.txt", "2012-4.txt", "2012-5.txt", "2012-6.txt", "2012-7.txt", "2012-8.txt", "2012-9.txt",\
			"2012-10.txt", "2012-11.txt", "2012-12.txt", "2012-13.txt", "2012-14.txt", "2012-15.txt", "2012-16.txt", "2012-17.txt",\
			 "2013-2.txt", "2013-3.txt", "2013-4.txt", "2013-5.txt", "2013-6.txt", "2013-7.txt", "2013-8.txt", "2013-9.txt",\
			"2013-10.txt", "2013-11.txt", "2013-12.txt", "2013-13.txt", "2013-14.txt", "2013-15.txt", "2013-16.txt", "2013-17.txt"]
	# columns = [16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 272, 288, 304, 320]
	columns = range(1, 65) 
	for i in range(popSize): #loop over all individualas
		indvBitList = []
		print "SETTING UP BITLISTS", " -- ", i
		for j in range(stringLen): # set up their bit string
			if(random.uniform(0,1) < .5):
				indvBitList.append("0")
			else:
				indvBitList.append("1")
		while(int("".join(indvBitList[0:8]), 2) > 210 or (int("".join(indvBitList[8:16]), 2) > 210) or (int("".join(indvBitList[16:24]), 2) > 210)):
			indvBitList = []
			print "SETTING UP BITLISTS", " -- ", i
			for k in range(stringLen): # set up their bit string
				if(random.uniform(0,1) < .5):
					indvBitList.append("0")
				else:
					indvBitList.append("1")

		x = individual(indvBitList)
		gen1.append((x, x.getFitness(weeks, columns), x.getLogicalGroupingString(), x.getGreaterLessString(), x.getStat1(), x.getStat2(), x.getStat3()))

 
	gen1 = sorted(gen1, reverse=True, key=getKey) # sort by fitness values

	 
	previousGen = gen1
	numGenerations = 0
	for i in range(generations):
		previousGen[popSize-1] = previousGen[0] #delete the worst and replace it with the best
		# print "\n\n"
		# print previousGen
		print "WORKING ON GENERATION:", i
		nextGen = []
		for j in range(popSize):
			parent1 = previousGen[random.randint(0, popSize-1)][0] # 0 <= x <= popSize-1
			parent2 = previousGen[random.randint(0, popSize-1)][0]
			while(parent2 == parent1):
				parent2 = previousGen[random.randint(0, popSize-1)][0]

			if(random.uniform(0,1) < crossOverRate): # perform crossover 60 percent of the time
				randomCutoffPoint = random.randint(0, stringLen-2) # stringLen-2 or stringLen-1
				childBitList = parent1.getBitList()[0:randomCutoffPoint] + parent2.getBitList()[randomCutoffPoint:]

				while(int("".join(childBitList[0:8]), 2) > 210 or (int("".join(childBitList[8:16]), 2) > 210) or (int("".join(childBitList[16:24]), 2) > 210)):
					randomCutoffPoint = random.randint(0, stringLen-2) # stringLen-2 or stringLen-1
					childBitList = parent1.getBitList()[0:randomCutoffPoint] + parent2.getBitList()[randomCutoffPoint:]

				mutate(childBitList, mutationRate)
				newChild = individual(childBitList)
				nextGen.append((newChild, newChild.getFitness(weeks, columns), newChild.getLogicalGroupingString(), newChild.getGreaterLessString(), newChild.getStat1(), newChild.getStat2(), newChild.getStat3()))

			else:
				if(parent1.getFitness(weeks, columns) > parent2.getFitness(weeks, columns)):
					mutate(parent1.getBitList(), mutationRate)
					nextGen.append((parent1, parent1.getFitness(weeks, columns), parent1.getLogicalGroupingString(), parent1.getGreaterLessString(), parent1.getStat1(), parent1.getStat2(), parent1.getStat3()))

				else:
					mutate(parent2.getBitList(), mutationRate)
					nextGen.append((parent2, parent2.getFitness(weeks, columns), parent2.getLogicalGroupingString(), parent2.getGreaterLessString(), parent2.getStat1(), parent2.getStat2(), parent2.getStat3()))
		nextGen = sorted(nextGen, reverse=True, key=getKey) # sort by fitness values
		for indiv in nextGen:
			print "Fitness:", indiv[1], "  ||  Logical Code: ", indiv[2], "  ||  Greater / Less Code:", indiv[3], "  ||  Stat 1", indiv[4], "  ||  Stat 2", indiv[5], "  ||  Stat 3", indiv[6]
		previousGen = nextGen

		

	
	print "--------------------------------------------"
	print "Number of Generations:", generations