docs: def compete(self, chromosomes):
          """
          Simulate competition/survival of the fittest.
          
          The fitness of each chromosome is used to calculate a survival probability
          based on how it compares to the overall fitness range of the run and the
          fitness range of the current generation. The ``abs_fit_weight`` and 
          ``rel_fit_weight`` attributes determine the degree to which overall and
          current fitness ranges affect the final survival probability.
          
          return:  list of surviving chromosomes
          """
          # update overall fitness for this run
          self.sort(chromosomes)
          min_fit = self.get_fitness(chromosomes[0])
          max_fit = self.get_fitness(chromosomes[-1])
          
          if min_fit < self.min_fit_ever:
              self.min_fit_ever = min_fit
          if max_fit > self.max_fit_ever:
              self.max_fit_ever = max_fit
          
          overall_fit_range = self.max_fit_ever - self.min_fit_ever  # "absolute" fitness range
          current_fit_range = max_fit - min_fit                      # "relative" fitness range
          
          # choose survivors based on relative fitness within overall fitness range
          survivors = []
          for chromosome in chromosomes:
              fit = self.get_fitness(chromosome)
              p_survival_absolute = (fit - self.min_fit_ever) / overall_fit_range if overall_fit_range != 0 else 1
              p_survival_relative = (fit - min_fit) / current_fit_range if current_fit_range != 0 else 1
              
              # compute weighted average survival probability
              # a portion accounts for absolute overall fitness for all chromosomes ever encountered (environment-driven)
              # the other portion accounts for relative fitness within the current population (competition-driven)
              p_survival = p_survival_absolute * self.abs_fit_weight + p_survival_relative * self.rel_fit_weight
  
              if random.random() < p_survival:
                  survivors.append(chromosome)
  
          if not survivors:
              # rarely, nothing survives -- allow everyone to live
              return chromosomes
                  
          return survivors