def nextGeneration(self, tetronimoes):
        scores = [(strategy, strategy.score(tetronimoes))
                  for strategy in self.strategies]
        scores.sort(reverse=True, key=lambda x: x[1])

        # keep best 25%
        self.strategies = []
        for i in range(int(self.size / 4)):
            self.strategies.append(scores[i][0])

        # generate 10% new data for the gene pool
        for _ in range(int(self.size / 10)):
            self.strategies.append(
                Strategy.generateRandomStrategy(self.functions))

        # for 10% of the data, randomly select 20% of the data, swap a weight in best 2
        for _ in range(int(self.size / 10)):
            subset = random.sample(scores, int(self.size / 5))
            subset.sort(reverse=True, key=lambda x: x[1])

            index = random.sample(
                [i for i in range(len(subset[0][0].weights))], 2)

            subset[0][0].weights[index[0]], subset[0][0].weights[
                index[1]] = subset[0][0].weights[
                    index[1]], subset[0][0].weights[index[0]]
            self.strategies.append(
                Strategy(self.functions, subset[0][0].weights))

        # fill the rest of the strategies with evolved data
        # randomly select 20% of the data, average the best 2
        while len(self.strategies) < self.size:
            subset = []
            if self.size >= 10:
                subset = random.sample(scores, int(self.size / 5))
            else:
                subset = random.sample(scores, 2)
            subset.sort(reverse=True, key=lambda x: x[1])

            weights = [
                subset[0][0].weights[f] + subset[1][0].weights[f]
                for f in range(len(self.functions))
            ]
            weightnorm = pow(sum(w**2 for w in weights), 0.5)
            weights = [w / weightnorm for w in weights]
            self.strategies.append(Strategy(self.functions, weights))

        #replace repeating stratagies with random weights
        self.strategies.sort(reverse=True, key=lambda x: x.weights[0])
        for i in range(self.size):
            if i != 0 and self.strategies[i - 1].weights == self.strategies[i]:
                self.strategies[i - 1] = generateRandomStrategy(self.functions)

        return scores, sum(score for (_, score) in scores) / len(scores)
    def __init__(self, functions, input, size=100):
        assert (size >= 2)
        self.functions = functions
        self.size = size
        self.strategies = []

        if input:
            with open(input, 'r') as inputWeights:
                reader = csv.reader(inputWeights)
                for row in reader:
                    weights = list(map(float, row))

                    if len(functions) != len(weights):
                        sys.exit(
                            "input file weight list is not the same length as the functions list used"
                        )

                    self.strategies.append(Strategy(functions, weights))
            self.size = len(self.strategies)
        else:
            for _ in range(size):
                self.strategies.append(
                    Strategy.generateRandomStrategy(functions))