Exemplo n.º 1
0
def _add_top_down_entry(layout: QVBoxLayout,
                        controller: str,
                        constant: str,
                        label_text: str,
                        label_font,
                        value_font,
                        alignment: Qt.AlignmentFlag = Qt.AlignLeft
                        | Qt.AlignVCenter,
                        force_value=None) -> None:
    hbox = QHBoxLayout()
    hbox.setContentsMargins(0, 0, 0, 0)
    # Create label
    label = QLabel()
    label.setFont(label_font)
    label.setText(label_text)
    label.setAlignment(alignment)
    # Create value
    value_label = QLabel()
    value_label.setFont(value_font)
    value = None
    if controller == 'boxcar' and constant:
        value = get_boxcar_constant(constant)
    elif controller == 'ga' and constant:
        value = get_ga_constant(constant)
    elif force_value:
        value = force_value

    value_label.setText(str(value))

    # Add the labels to the hbox
    hbox.addWidget(label, 1)
    hbox.addWidget(value_label, 1)

    # Finally add hbox to the top-down layout
    layout.addLayout(hbox)
Exemplo n.º 2
0
def _add_row_entry(form: QFormLayout,
                   controller: str,
                   constant: str,
                   label_text: str,
                   label_font,
                   value_font,
                   alignment: Qt.AlignmentFlag = Qt.AlignLeft
                   | Qt.AlignVCenter,
                   force_value=None) -> None:
    # Create label
    label = QLabel()
    label.setFont(label_font)
    label.setText(label_text)
    label.setAlignment(alignment)
    # Create value
    value_label = QLabel()
    value_label.setFont(value_font)
    value = None
    if controller == 'boxcar' and constant:
        value = get_boxcar_constant(constant)
    elif controller == 'ga' and constant:
        value = get_ga_constant(constant)
    elif force_value:
        value = force_value

    value_label.setText(str(value))

    form.addRow(label, value_label)
Exemplo n.º 3
0
 def calculate_fitness(self) -> None:
     """
     Calculate the fitness of an individual at the end of a generation.
     """
     func = get_ga_constant('fitness_function')
     fitness = func(max(self.max_position, 0.0), self.num_wheels,
                    self.chassis_volume, self.wheels_volume, self.frames)
     self._fitness = max(fitness, 0.0001)
Exemplo n.º 4
0
    def _set_first_gen(self) -> None:
        """
        Sets the first generation, i.e. random cars
        """
        # Create the floor if FIRST_GEN, but not if it's in progress
        if self.state == States.FIRST_GEN:
            self.floor = Floor(self.world)

        # We are now in progress of creating the first gen
        self.state = States.FIRST_GEN_IN_PROGRESS

        # Initialize cars randomly
        self.cars = []
        # Determine how many cars to make
        num_to_create = None
        if get_ga_constant(
                'num_parents'
        ) - self._total_individuals_ran >= get_boxcar_constant(
                'run_at_a_time'):
            num_to_create = get_boxcar_constant('run_at_a_time')
        else:
            num_to_create = get_ga_constant(
                'num_parents') - self._total_individuals_ran

        # @NOTE that I create the subset of cars
        for i in range(num_to_create):
            car = create_random_car(self.world, self.floor.winning_tile,
                                    self.floor.lowest_y)
            self.cars.append(car)

        self._next_pop.extend(
            self.cars
        )  # Add the cars to the next_pop which is used by population

        leader = self.find_new_leader()
        self.leader = leader

        # Time to go to new state?
        if self._total_individuals_ran == get_ga_constant('num_parents'):
            self._creating_random_cars = False
            self.state = States.NEXT_GEN
Exemplo n.º 5
0
 def _set_number_of_cars_alive(self) -> None:
     """
     Set the number of cars alive on the screen label
     """
     total_for_gen = get_ga_constant('num_parents')
     if self.current_generation > 0:
         total_for_gen = self._next_gen_size
     num_batches = math.ceil(total_for_gen /
                             get_boxcar_constant('run_at_a_time'))
     text = '{}/{} (batch {}/{})'.format(self.num_cars_alive,
                                         self.batch_size,
                                         self.current_batch, num_batches)
     self.stats_window.current_num_alive.setText(text)
Exemplo n.º 6
0
    def _mutation(self, chromosome: np.ndarray) -> None:
        """
        Randomly decide if we should perform mutation on a gene within the chromosome. This is done in place
        """
        rand_mutation = random.random()
        mutation_bucket = np.digitize(rand_mutation, self._mutation_bins)

        # Gaussian
        if mutation_bucket == 0:
            mutation_rate = get_ga_constant('mutation_rate')
            if get_ga_constant('mutation_rate_type').lower() == 'dynamic':
                mutation_rate = mutation_rate / math.sqrt(
                    self.current_generation + 1)
            gaussian_mutation(chromosome,
                              mutation_rate,
                              scale=get_ga_constant('gaussian_mutation_scale'))

        # Random uniform
        elif mutation_bucket == 1:
            #@TODO: add to this
            pass
        else:
            raise Exception(
                'Unable to determine valid mutation based off probabilities')
Exemplo n.º 7
0
    def _crossover(self, p1_chromosome: np.ndarray,
                   p2_chromosome: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
        """
        Perform crossover between two parent chromosomes and return TWO child chromosomes
        """
        rand_crossover = random.random()
        crossover_bucket = np.digitize(rand_crossover, self._crossover_bins)

        # SBX
        if crossover_bucket == 0:
            c1_chromosome, c2_chromosome = SBX(p1_chromosome, p2_chromosome,
                                               get_ga_constant('SBX_eta'))
        else:
            raise Exception(
                'Unable to determine valid crossover based off probabilities')

        return c1_chromosome, c2_chromosome
Exemplo n.º 8
0
    def _update(self) -> None:
        """
        Called once every 1/FPS to update everything
        """
        for car in self.cars:
            if not car.is_alive:
                continue
            # Did the car die/win?
            if not car.update():
                # Another individual has finished
                self._total_individuals_ran += 1
                # Decrement the number of cars alive
                self.num_cars_alive -= 1
                self._set_number_of_cars_alive()

                # If the car that just died/won was the leader, we need to find a new one
                if car == self.leader:
                    leader = self.find_new_leader()
                    self.leader = leader
                    self.game_window.leader = leader
            else:
                if not self.leader:
                    self.leader = leader
                    self.game_window.leader = leader
                else:
                    car_pos = car.position.x
                    if car_pos > self.leader.position.x:
                        self.leader = car
                        self.game_window.leader = car
        # If the leader is valid, then just pan to the leader
        if not self.manual_control and self.leader:
            self.game_window.pan_camera_to_leader()
        # If there is not a leader then the generation is over OR the next group of N need to run
        if not self.leader:
            # Replay state
            if self.state == States.REPLAY:
                name = 'car_{}.npy'.format(self.current_generation)
                car = load_car(self.world, self.floor.winning_tile,
                               self.floor.lowest_y, np.inf,
                               args.replay_from_folder, name)
                self.cars = [car]
                self.game_window.cars = self.cars
                self.leader = self.find_new_leader()
                self.game_window.leader = self.leader
                self.current_generation += 1
                txt = 'Replay {}/{}'.format(self.current_generation,
                                            self.num_replay_inds)
                self.stats_window.generation.setText(
                    "<font color='red'>Replay</font>")
                self.stats_window.pop_size.setText(
                    "<font color='red'>Replay</font>")
                self.stats_window.current_num_alive.setText(
                    "<font color='red'>" + txt + '</font>')
                return
            # Are we still in the process of just random creation?
            if self.state in (States.FIRST_GEN, States.FIRST_GEN_IN_PROGRESS):
                self._set_first_gen()
                self.game_window.leader = self.leader
                self.game_window.cars = self.cars
                self.num_cars_alive = len(self.cars)
                self.batch_size = self.num_cars_alive
                self.current_batch += 1
                self._set_number_of_cars_alive()
                return
            # Next N individuals need to run
            # We already have a population defined and we need to create N cars to run
            elif self.state == States.NEXT_GEN_CREATE_OFFSPRING:
                num_create = min(
                    self._next_gen_size - self._total_individuals_ran,
                    get_boxcar_constant('run_at_a_time'))

                self.cars = self._create_num_offspring(num_create)
                self.batch_size = len(self.cars)
                self.num_cars_alive = len(self.cars)

                self._next_pop.extend(
                    self.cars)  # These cars will now be part of the next pop
                self.game_window.cars = self.cars
                leader = self.find_new_leader()
                self.leader = leader
                self.game_window.leader = leader
                # should we go to the next state?
                if (self.current_generation == 0 and (self._total_individuals_ran >= get_ga_constant('num_parents'))) or\
                    (self.current_generation > 0 and (self._total_individuals_ran >= self._next_gen_size)):
                    self.state = States.NEXT_GEN
                else:
                    self.current_batch += 1
                    self._set_number_of_cars_alive()
                return
            elif self.state in (States.NEXT_GEN,
                                States.NEXT_GEN_COPY_PARENTS_OVER,
                                States.NEXT_GEN_CREATE_OFFSPRING):
                self.next_generation()
                return
            else:
                raise Exception(
                    'You should not be able to get here, but if you did, awesome! Report this to me if you actually get here.'
                )

        self.world.ClearForces()

        # Update windows
        self.game_window._update()

        # Step
        self.world.Step(1. / FPS, 10, 6)
Exemplo n.º 9
0
    def _create_num_offspring(self, number_of_offspring) -> List[Individual]:
        """
        This is a helper function to decide whether to grab from current pop or create new offspring.

        Creates a number of offspring from the current population. This assumes that the current population are all able to reproduce.
        This is broken up from the main next_generation function so that we can create N individuals at a time if needed without going
        to the next generation. Mainly used if `run_at_a_time` is < the number of individuals that are in the next generation.
        """
        next_pop: List[Individual] = []
        #@TODO: comment this to new state
        # If the selection type is plus, then it means certain individuals survive to the next generation, so we need
        # to grab those first before we create new ones
        # if get_ga_constant('selection_type').lower() == 'plus' and len(self._next_pop) < get_ga_constant('num_parents'):
        if self.state == States.NEXT_GEN_COPY_PARENTS_OVER:
            # Select the subset of the individuals to bring to the next gen
            increment = 0  # How much did the offset increment by
            for idx in range(self._offset_into_population,
                             len(self.population.individuals)):
                # for individual in self.population.individuals[self._offset_into_population: self._offset_into_population + number_of_offspring]:
                individual = self.population.individuals[idx]
                increment += 1  # For offset
                world = self.world
                wheel_radii = individual.wheel_radii
                wheel_densities = individual.wheel_densities
                #wheel_motor_speeds = individual.wheel_motor_speeds
                chassis_vertices = individual.chassis_vertices
                chassis_densities = individual.chassis_densities
                winning_tile = individual.winning_tile
                lowest_y_pos = individual.lowest_y_pos
                lifespan = individual.lifespan

                # If the individual is still alive, they survive
                if lifespan > 0:
                    car = Car(
                        world,
                        wheel_radii,
                        wheel_densities,  # wheel_motor_speeds,       # Wheel
                        chassis_vertices,
                        chassis_densities,  # Chassis
                        winning_tile,
                        lowest_y_pos,
                        lifespan)
                    next_pop.append(car)
                    # Check to see if we've added enough parents. The reason we check here is if you requet 5 parents but
                    # 2/5 are dead, then you need to keep going until you get 3 good ones.
                    if len(next_pop) == number_of_offspring:
                        break
                else:
                    print("Oh dear, you're dead")
            # Increment offset for the next time
            self._offset_into_population += increment
            # If there weren't enough parents that made it to the new generation, we just accept it and move on.
            # Since the lifespan could have reached 0, you are not guaranteed to always have the same number of parents copied over.
            if self._offset_into_population >= len(
                    self.population.individuals):
                self.state = States.NEXT_GEN_CREATE_OFFSPRING
        # Otherwise just perform crossover with the current population and produce num_of_offspring
        # @NOTE: The state, even if we got here through State.NEXT_GEN or State.NEXT_GEN_COPY_PARENTS_OVER is now
        # going to switch to State.NEXT_GEN_CREATE_OFFSPRING based off this else condition. It's not set here, but
        # rather at the end of new_generation
        else:
            # Keep adding children until we reach the size we need
            while len(next_pop) < number_of_offspring:
                # Tournament crossover
                if get_ga_constant(
                        'crossover_selection').lower() == 'tournament':
                    p1, p2 = tournament_selection(
                        self.population, 2, get_ga_constant('tournament_size'))
                # Roulette
                elif get_ga_constant(
                        'crossover_selection').lower() == 'roulette':
                    p1, p2 = roulette_wheel_selection(self.population, 2)
                else:
                    raise Exception(
                        'crossover_selection "{}" is not supported'.format(
                            get_ga_constant('crossover_selection').lower()))

                # Crossover
                c1_chromosome, c2_chromosome = self._crossover(
                    p1.chromosome, p2.chromosome)

                # Mutation
                self._mutation(c1_chromosome)
                self._mutation(c2_chromosome)

                # Don't let the chassis density become <=0. It is bad
                smart_clip(c1_chromosome)
                smart_clip(c2_chromosome)

                # Create children from the new chromosomes
                c1 = Car.create_car_from_chromosome(
                    p1.world, p1.winning_tile, p1.lowest_y_pos,
                    get_ga_constant('lifespan'), c1_chromosome)
                c2 = Car.create_car_from_chromosome(
                    p2.world, p2.winning_tile, p2.lowest_y_pos,
                    get_ga_constant('lifespan'), c2_chromosome)

                # Add children to the next generation
                next_pop.extend([c1, c2])

        # Return the next population that will play. Remember, this can be a subset of the overall population since
        # those parents still exist.
        return next_pop
Exemplo n.º 10
0
    def next_generation(self) -> None:
        if self.state == States.NEXT_GEN:
            self.stats_window.pop_size.setText(str(self._next_gen_size))
            self.current_batch = 0
            # Set next state to copy parents if its plus, otherwise comma is just going to create offspring
            if get_ga_constant('selection_type').lower() == 'plus':
                self.state = States.NEXT_GEN_COPY_PARENTS_OVER
            elif get_ga_constant('selection_type').lower() == 'comma':
                self.state = States.NEXT_GEN_CREATE_OFFSPRING
            else:
                raise Exception('Invalid selection_type: "{}"'.format(
                    get_ga_constant('selection_type')))

            self._offset_into_population = 0
            self._total_individuals_ran = 0  # Reset back to the first individual

            self.population.individuals = self._next_pop
            self._next_pop = []  # Reset the next pop

            # Calculate fit
            for individual in self.population.individuals:
                individual.calculate_fitness()

            # Should we save the pop
            if args.save_pop:
                path = os.path.join(
                    args.save_pop, 'pop_gen{}'.format(self.current_generation))
                if os.path.exists(path):
                    raise Exception(
                        '{} already exists. This would overwrite everything, choose a different folder or delete it and try again'
                        .format(path))
                os.makedirs(path)
                save_population(path, self.population, settings.settings)
            # Save best?
            if args.save_best:
                save_car(args.save_best,
                         'car_{}'.format(self.current_generation),
                         self.population.fittest_individual, settings.settings)

            self._set_previous_gen_avg_fitness()
            self._set_previous_gen_num_winners()
            self._increment_generation()

            # Grab the best individual and compare to best fitness
            best_ind = self.population.fittest_individual
            if best_ind.fitness > self.max_fitness:
                self.max_fitness = best_ind.fitness
                self._set_max_fitness()
                self.gen_without_improvement = 0
            else:
                self.gen_without_improvement += 1
            # Set text for gen improvement
            self.stats_window.gens_without_improvement.setText(
                str(self.gen_without_improvement))

            # Set the population to be just the parents allowed for reproduction. Only really matters if `plus` method is used.
            # If `plus` method is used, there can be more individuals in the next generation, so this limits the number of parents.
            self.population.individuals = elitism_selection(
                self.population, get_ga_constant('num_parents'))

            random.shuffle(self.population.individuals)

            # Parents + offspring selection type ('plus')
            if get_ga_constant('selection_type').lower() == 'plus':
                # Decrement lifespan
                for individual in self.population.individuals:
                    individual.lifespan -= 1

        num_offspring = min(self._next_gen_size - len(self._next_pop),
                            get_boxcar_constant('run_at_a_time'))
        self.cars = self._create_num_offspring(num_offspring)
        # Set number of cars alive
        self.num_cars_alive = len(self.cars)
        self.batch_size = self.num_cars_alive
        self.current_batch += 1
        self._set_number_of_cars_alive()
        self._next_pop.extend(self.cars)  # Add to next_pop
        self.game_window.cars = self.cars
        leader = self.find_new_leader()
        self.leader = leader
        self.game_window.leader = leader
        if get_ga_constant('selection_type').lower() == 'comma':
            self.state = States.NEXT_GEN_CREATE_OFFSPRING
        elif get_ga_constant('selection_type').lower(
        ) == 'plus' and self._offset_into_population >= len(
                self.population.individuals):
            self.state = States.NEXT_GEN_CREATE_OFFSPRING
Exemplo n.º 11
0
    def __init__(self, world, replay=False):
        super().__init__()
        self.world = world
        self.title = 'Genetic Algorithm - Cars'
        self.top = 150
        self.left = 150
        self.width = 1100
        self.height = 700
        self.max_fitness = 0.0
        self.cars = []
        self.population = Population([])
        self.state = States.FIRST_GEN
        self._next_pop = [
        ]  # Used when you are in state 1, i.e. creating new cars from the old population
        self.current_batch = 1
        self.batch_size = get_boxcar_constant('run_at_a_time')
        self.gen_without_improvement = 0
        self.replay = replay

        self.manual_control = False

        self.current_generation = 0
        self.leader = None  # What car is leading
        self.num_cars_alive = get_boxcar_constant('run_at_a_time')
        self.batch_size = self.num_cars_alive
        self._total_individuals_ran = 0
        self._offset_into_population = 0  # Used if we display only a certain number at a
        # Determine whether or not we are in the process of creating random cars.
        # This is used for when we only run so many at a time. For instance if `run_at_a_time` is 20 and
        # `num_parents` is 1500, then we can't just create 1500 cars. Instead we create batches of 20 to
        # run at a time. This flag is for deciding when we are done with that so we can move on to crossover
        # and mutation.
        self._creating_random_cars = True

        # Determine how large the next generation is
        if get_ga_constant('selection_type').lower() == 'plus':
            self._next_gen_size = get_ga_constant(
                'num_parents') + get_ga_constant('num_offspring')
        elif get_ga_constant('selection_type').lower() == 'comma':
            self._next_gen_size = get_ga_constant('num_parents')
        else:
            raise Exception('Selection type "{}" is invalid'.format(
                get_ga_constant('selection_type')))

        if self.replay:
            global args
            self.floor = Floor(self.world)
            self.state = States.REPLAY
            self.num_replay_inds = len([
                x for x in os.listdir(args.replay_from_folder)
                if x.startswith('car_')
            ])
        else:
            self._set_first_gen()
        # self.population = Population(self.cars)
        # For now this is all I'm supporting, may change in the future. There really isn't a reason to use
        # uniform or single point here because all the values have different ranges, and if you clip them, it
        # can make those crossovers useless. Instead just use simulated binary crossover to ensure better crossover.
        self._crossover_bins = np.cumsum([get_ga_constant('probability_SBX')])

        self._mutation_bins = np.cumsum([
            get_ga_constant('probability_gaussian'),
            get_ga_constant('probability_random_uniform')
        ])

        self.init_window()
        self.stats_window.pop_size.setText(str(get_ga_constant('num_parents')))
        self._set_number_of_cars_alive()
        self.game_window.cars = self.cars
        self._timer = QTimer(self)
        self._timer.timeout.connect(self._update)
        self._timer.start(1000 // get_boxcar_constant('fps'))
Exemplo n.º 12
0
    def _add_ga_settings_window(self) -> None:
        self.ga_settings_window = QVBoxLayout()
        # GA settings title
        label_ga_settings = QLabel()
        label_ga_settings.setFont(font_bold)
        label_ga_settings.setText('Genetic Algorithm Settings')
        label_ga_settings.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
        self.ga_settings_window.addWidget(label_ga_settings)

        self._add_ga_entry('selection_type', 'Selection Type:', font_bold,
                           normal_font)
        # Prob mutation
        mutation_prob = '{:.1f}% Gaussian\n{:.1f}% Uniform'.format(
            get_ga_constant('probability_gaussian') * 100.0,
            get_ga_constant('probability_random_uniform') * 100.0)
        self._add_ga_entry(None,
                           'Prob. Mutation:',
                           font_bold,
                           normal_font,
                           force_value=mutation_prob)
        # Prob crossover
        crossover_prob = '{:.1f}% SBX'.format(
            get_ga_constant('probability_SBX') * 100.0)
        self._add_ga_entry(None,
                           'Prob. Crossover:',
                           font_bold,
                           normal_font,
                           force_value=crossover_prob)
        # Crossover selection
        # Tournament crossover selection
        if get_ga_constant('crossover_selection').lower() == 'tournament':
            if not get_ga_constant('tournament_size'):
                raise Exception('You must provide a tournament size if you choose "tournament" for crossover_selection.\n'+\
                    '"tournament_size" can be anywhere from [1, len(population))')
            parent_crossover = 'tournament of {}'.format(
                get_ga_constant('tournament_size'))
        # roulette
        elif get_ga_constant('crossover_selection').lower() == 'roulette':
            parent_crossover = 'roulette'
        else:
            raise Exception(
                'Unable to determine crossover_selection "{}"'.format(
                    get_ga_constant('crossover_selection')))
        self._add_ga_entry(None,
                           'Crossover Selection:',
                           font_bold,
                           normal_font,
                           force_value=parent_crossover)
        # Mutation rate
        mutation_rate = '{:.1f}%'.format(
            get_ga_constant('mutation_rate') * 100)
        # static
        if get_ga_constant('mutation_rate_type').lower() == 'static':
            mutation_rate += ' Static'
        # decaying
        elif get_ga_constant('mutation_rate_type').lower() == 'decaying':
            mutation_rate += ' Decaying'
        else:
            raise Exception(
                'Unable to determine mutation_rate_type "{}"'.format(
                    get_ga_constant('mutation_rate_type')))
        self._add_ga_entry(None,
                           'Mutation Rate:',
                           font_bold,
                           normal_font,
                           force_value=mutation_rate)
        # Lifespan
        lifespan = get_ga_constant('lifespan')
        lifespan = str(int(lifespan)) if lifespan != np.inf else 'infinite'
        self._add_ga_entry(None,
                           'Lifespan:',
                           font_bold,
                           normal_font,
                           force_value=lifespan)

        self.grid.addLayout(self.ga_settings_window, 0, 3)
Exemplo n.º 13
0
    def _init_window(self) -> None:
        ROW = 0
        COL = 0
        stats_vbox = QVBoxLayout()
        stats_vbox.setContentsMargins(0, 0, 0, 0)

        # Create the current generation
        generation_label = QLabel()
        generation_label.setFont(font_bold)
        generation_label.setText('Generation:')
        generation_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.generation = QLabel()
        self.generation.setFont(normal_font)
        self.generation.setText("<font color='red'>" + '1' + '</font>')
        self.generation.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        hbox_generation = QHBoxLayout()
        hbox_generation.setContentsMargins(5, 0, 0, 0)
        # Give equal weight
        hbox_generation.addWidget(generation_label, 1)
        hbox_generation.addWidget(self.generation, 1)
        stats_vbox.addLayout(hbox_generation)

        # Current number alive
        current_num_alive_label = QLabel()
        current_num_alive_label.setFont(font_bold)
        current_num_alive_label.setText('Current Alive:')
        current_num_alive_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.current_num_alive = QLabel()
        self.current_num_alive.setFont(normal_font)
        self.current_num_alive.setText(str(get_ga_constant('num_parents')))
        self.current_num_alive.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        hbox_num_alive = QHBoxLayout()
        hbox_num_alive.setContentsMargins(5, 0, 0, 0)
        # Give equal weight
        hbox_num_alive.addWidget(current_num_alive_label, 1)
        hbox_num_alive.addWidget(self.current_num_alive, 1)
        stats_vbox.addLayout(hbox_num_alive)

        # population size
        pop_size_label = QLabel()
        pop_size_label.setFont(font_bold)
        pop_size_label.setText('Population Size:')
        pop_size_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.pop_size = QLabel()
        self.pop_size.setFont(normal_font)
        self.pop_size.setText(str(get_ga_constant('num_parents')))
        self.pop_size.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        hbox_pop_size = QHBoxLayout()
        hbox_pop_size.setContentsMargins(5, 0, 0, 0)
        # Give equal weight
        hbox_pop_size.addWidget(pop_size_label, 1)
        hbox_pop_size.addWidget(self.pop_size, 1)
        stats_vbox.addLayout(hbox_pop_size)

        # Create best fitness
        best_fitness_label = QLabel()
        best_fitness_label.setFont(font_bold)
        best_fitness_label.setText('Best Fitness Ever:')
        generation_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.best_fitness = QLabel()
        self.best_fitness.setFont(normal_font)
        self.best_fitness.setText('0.0')
        self.best_fitness.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        hbox_fitness = QHBoxLayout()
        hbox_fitness.setContentsMargins(5, 0, 0, 0)
        # Give equal weight
        hbox_fitness.addWidget(best_fitness_label, 1)
        hbox_fitness.addWidget(self.best_fitness, 1)
        stats_vbox.addLayout(hbox_fitness)

        # Average fitness last gen
        average_fitness_label = QLabel()
        average_fitness_label.setFont(font_bold)
        average_fitness_label.setText('Mean Fitness Last Gen:')
        average_fitness_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.average_fitness_last_gen = QLabel()
        self.average_fitness_last_gen.setFont(normal_font)
        self.average_fitness_last_gen.setText('0.0')
        self.average_fitness_last_gen.setAlignment(Qt.AlignLeft
                                                   | Qt.AlignVCenter)
        hbox_avg_fitness = QHBoxLayout()
        hbox_avg_fitness.setContentsMargins(5, 0, 0, 0)
        # Give equal weight
        hbox_avg_fitness.addWidget(average_fitness_label, 1)
        hbox_avg_fitness.addWidget(self.average_fitness_last_gen, 1)
        stats_vbox.addLayout(hbox_avg_fitness)

        # num solved last gen
        num_solved_last_gen_label = QLabel()
        num_solved_last_gen_label.setFont(font_bold)
        num_solved_last_gen_label.setText('# Solved Last Gen:')
        num_solved_last_gen_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        self.num_solved_last_gen = QLabel()
        self.num_solved_last_gen.setFont(normal_font)
        self.num_solved_last_gen.setText('0')
        self.num_solved_last_gen.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)
        hbox_num_solved = QHBoxLayout()
        hbox_num_solved.setContentsMargins(5, 0, 0, 0)
        # Give equal weight
        hbox_num_solved.addWidget(num_solved_last_gen_label, 1)
        hbox_num_solved.addWidget(self.num_solved_last_gen, 1)
        stats_vbox.addLayout(hbox_num_solved)

        # generations without improvement
        gens_without_improvement_label = QLabel()
        gens_without_improvement_label.setFont(font_bold)
        gens_without_improvement_label.setText('Stale Generations:')
        gens_without_improvement_label.setAlignment(Qt.AlignLeft
                                                    | Qt.AlignVCenter)
        self.gens_without_improvement = QLabel()
        self.gens_without_improvement.setFont(normal_font)
        self.gens_without_improvement.setText('0')
        self.gens_without_improvement.setAlignment(Qt.AlignLeft
                                                   | Qt.AlignVCenter)
        hbox_gens_without_improvement = QHBoxLayout()
        hbox_gens_without_improvement.setContentsMargins(5, 0, 0, 0)
        # Give equal weight
        hbox_gens_without_improvement.addWidget(gens_without_improvement_label,
                                                1)
        hbox_gens_without_improvement.addWidget(self.gens_without_improvement,
                                                1)
        stats_vbox.addLayout(hbox_gens_without_improvement)

        self.grid.addLayout(stats_vbox, 0, 0)
        # self.grid.addWidget(QLabel(),0,1)
        # self.grid.setColumnStretch(1,10)
        self._add_ga_settings_window()  # Finally add the GA Settings Window
Exemplo n.º 14
0
def create_random_car(world: b2World, winning_tile: b2Vec2,
                      lowest_y_pos: float):
    """
    Creates a random car based off the values found in settings.py under the settings dictionary
    """
    # Create a number of random wheels.
    # Each wheel will have a random radius and density
    num_wheels = random.randint(get_boxcar_constant('min_num_wheels'),
                                get_boxcar_constant('max_num_wheels') + 1)
    wheel_verts = list(range(num_wheels))  # What vertices should we attach to?
    rand.shuffle(wheel_verts)
    wheel_verts = wheel_verts[:num_wheels]
    wheel_radii = [0.0 for _ in range(8)]
    wheel_densities = [0.0 for _ in range(8)]
    # wheel_motor_speeds = [random.uniform(get_boxcar_constant('min_wheel_motor_speed'), get_boxcar_constant('max_wheel_motor_speed'))
    #                       for _ in range(8)]  # Doesn't matter if this is set. There won't be a wheel if the density OR radius is 0

    # Assign a random radius/density to vertices found in wheel_verts
    for vert_idx in wheel_verts:
        radius = random.uniform(get_boxcar_constant('min_wheel_radius'),
                                get_boxcar_constant('max_wheel_radius'))
        density = random.uniform(get_boxcar_constant('min_wheel_density'),
                                 get_boxcar_constant('max_wheel_density'))

        # Override the intiial 0.0
        wheel_radii[vert_idx] = radius
        wheel_densities[vert_idx] = density

    min_chassis_axis = get_boxcar_constant('min_chassis_axis')
    max_chassis_axis = get_boxcar_constant('max_chassis_axis')

    ####
    # The chassis vertices are on a grid and defined by v0-v7 like so:
    #
    #             v2
    #              |
    #          v3  |  v1
    #     v4 -------------- v0
    #          v5  |  v7
    #              |
    #             v6
    #
    # V0, V2, V4 and V6 are on an axis, while the V1 is defined somewhere between V0 and V2, V3 is defined somewhere between V2 and V4, etc.
    chassis_vertices = []
    chassis_vertices.append(
        b2Vec2(random.uniform(min_chassis_axis, max_chassis_axis), 0))
    chassis_vertices.append(
        b2Vec2(random.uniform(min_chassis_axis, max_chassis_axis),
               random.uniform(min_chassis_axis, max_chassis_axis)))
    chassis_vertices.append(
        b2Vec2(0, random.uniform(min_chassis_axis, max_chassis_axis)))
    chassis_vertices.append(
        b2Vec2(-random.uniform(min_chassis_axis, max_chassis_axis),
               random.uniform(min_chassis_axis, max_chassis_axis)))
    chassis_vertices.append(
        b2Vec2(-random.uniform(min_chassis_axis, max_chassis_axis), 0))
    chassis_vertices.append(
        b2Vec2(-random.uniform(min_chassis_axis, max_chassis_axis),
               -random.uniform(min_chassis_axis, max_chassis_axis)))
    chassis_vertices.append(
        b2Vec2(0, -random.uniform(min_chassis_axis, max_chassis_axis)))
    chassis_vertices.append(
        b2Vec2(random.uniform(min_chassis_axis, max_chassis_axis),
               -random.uniform(min_chassis_axis, max_chassis_axis)))

    # Now t hat we have our chassis vertices, we need to get a random density for them as well
    densities = []
    for i in range(8):
        densities.append(
            random.uniform(get_boxcar_constant('min_chassis_density'),
                           get_boxcar_constant('max_chassis_density')))

    return Car(
        world,
        wheel_radii,
        wheel_densities,  # wheel_motor_speeds,
        chassis_vertices,
        densities,
        winning_tile,
        lowest_y_pos,
        lifespan=get_ga_constant('lifespan'))