forked from Royz2123/Clash-of-Clans-AI
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/master'
- Loading branch information
Showing
10 changed files
with
297 additions
and
54 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# -*- coding: utf-8 -*- | ||
"""pyeasyga | ||
A simple and easy-to-use genetic algorithm implementation library in Python. | ||
For a bit array solution representation, simply instantiate the | ||
GeneticAlgorithm class with input data, define and supply a fitness function, | ||
run the Genetic Algorithm, and retrieve the solution! | ||
Other solution representations will require setting some more attributes. | ||
""" | ||
|
||
__author__ = 'Ayodeji Remi-Omosowon' | ||
__email__ = '[email protected]' | ||
__version__ = '0.3.1' |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
# -*- coding: utf-8 -*- | ||
""" | ||
pyeasyga module | ||
""" | ||
|
||
import random | ||
import copy | ||
from operator import attrgetter | ||
|
||
from six.moves import range | ||
|
||
|
||
class GeneticAlgorithm(object): | ||
"""Genetic Algorithm class. | ||
This is the main class that controls the functionality of the Genetic | ||
Algorithm. | ||
A simple example of usage: | ||
# Select only two items from the list and maximise profit | ||
from pyeasyga.pyeasyga import GeneticAlgorithm | ||
input_data = [('pear', 50), ('apple', 35), ('banana', 40)] | ||
easyga = GeneticAlgorithm(input_data) | ||
def fitness (member, data): | ||
return sum([profit for (selected, (fruit, profit)) in | ||
zip(member, data) if selected and | ||
member.count(1) == 2]) | ||
easyga.fitness_function = fitness | ||
easyga.run() | ||
print easyga.best_individual() | ||
""" | ||
|
||
def __init__(self, | ||
seed_data, | ||
population_size=50, | ||
generations=100, | ||
crossover_probability=0.8, | ||
mutation_probability=0.2, | ||
elitism=True, | ||
maximise_fitness=True): | ||
"""Instantiate the Genetic Algorithm. | ||
:param seed_data: input data to the Genetic Algorithm | ||
:type seed_data: list of objects | ||
:param int population_size: size of population | ||
:param int generations: number of generations to evolve | ||
:param float crossover_probability: probability of crossover operation | ||
:param float mutation_probability: probability of mutation operation | ||
""" | ||
|
||
self.seed_data = seed_data | ||
self.population_size = population_size | ||
self.generations = generations | ||
self.crossover_probability = crossover_probability | ||
self.mutation_probability = mutation_probability | ||
self.elitism = elitism | ||
self.maximise_fitness = maximise_fitness | ||
|
||
self.current_generation = [] | ||
|
||
def create_individual(seed_data): | ||
"""Create a candidate solution representation. | ||
e.g. for a bit array representation: | ||
>>> return [random.randint(0, 1) for _ in range(len(data))] | ||
:param seed_data: input data to the Genetic Algorithm | ||
:type seed_data: list of objects | ||
:returns: candidate solution representation as a list | ||
""" | ||
return [random.randint(0, 1) for _ in range(len(seed_data))] | ||
|
||
def crossover(parent_1, parent_2): | ||
"""Crossover (mate) two parents to produce two children. | ||
:param parent_1: candidate solution representation (list) | ||
:param parent_2: candidate solution representation (list) | ||
:returns: tuple containing two children | ||
""" | ||
index = random.randrange(1, len(parent_1)) | ||
child_1 = parent_1[:index] + parent_2[index:] | ||
child_2 = parent_2[:index] + parent_1[index:] | ||
return child_1, child_2 | ||
|
||
def mutate(individual): | ||
"""Reverse the bit of a random index in an individual.""" | ||
mutate_index = random.randrange(len(individual)) | ||
individual[mutate_index] = (0, 1)[individual[mutate_index] == 0] | ||
|
||
def random_selection(population): | ||
"""Select and return a random member of the population.""" | ||
return random.choice(population) | ||
|
||
def tournament_selection(population): | ||
"""Select a random number of individuals from the population and | ||
return the fittest member of them all. | ||
""" | ||
if self.tournament_size == 0: | ||
self.tournament_size = 2 | ||
members = random.sample(population, self.tournament_size) | ||
members.sort( | ||
key=attrgetter('fitness'), reverse=self.maximise_fitness) | ||
return members[0] | ||
|
||
self.fitness_function = None | ||
self.tournament_selection = tournament_selection | ||
self.tournament_size = self.population_size // 10 | ||
self.random_selection = random_selection | ||
self.create_individual = create_individual | ||
self.crossover_function = crossover | ||
self.mutate_function = mutate | ||
self.selection_function = self.tournament_selection | ||
|
||
def create_initial_population(self): | ||
"""Create members of the first population randomly. | ||
""" | ||
initial_population = [] | ||
for _ in range(self.population_size): | ||
print("Created "+str(_+1)+" individuals!") | ||
genes = self.create_individual(self.seed_data) | ||
individual = Chromosome(genes) | ||
initial_population.append(individual) | ||
self.current_generation = initial_population | ||
|
||
def calculate_population_fitness(self): | ||
"""Calculate the fitness of every member of the given population using | ||
the supplied fitness_function. | ||
""" | ||
for individual in self.current_generation: | ||
individual.fitness = self.fitness_function( | ||
individual.genes, self.seed_data) | ||
|
||
def rank_population(self): | ||
"""Sort the population by fitness according to the order defined by | ||
maximise_fitness. | ||
""" | ||
self.current_generation.sort( | ||
key=attrgetter('fitness'), reverse=self.maximise_fitness) | ||
|
||
def create_new_population(self,gen): | ||
"""Create a new population using the genetic operators (selection, | ||
crossover, and mutation) supplied. | ||
""" | ||
new_population = [] | ||
elite = copy.deepcopy(self.current_generation[0]) | ||
selection = self.selection_function | ||
if self.elitism: | ||
new_population.append (elite) | ||
count=0 | ||
while len(new_population) < self.population_size: | ||
parent_1 = copy.deepcopy(selection(self.current_generation)) | ||
parent_2 = copy.deepcopy(selection(self.current_generation)) | ||
|
||
child_1, child_2 = parent_1, parent_2 | ||
child_1.fitness, child_2.fitness = 0, 0 | ||
|
||
can_crossover = random.random() < self.crossover_probability | ||
can_mutate = random.random() < self.mutation_probability | ||
|
||
if can_crossover: | ||
child_1.genes, child_2.genes = self.crossover_function( | ||
parent_1.genes, parent_2.genes) | ||
|
||
if can_mutate: | ||
self.mutate_function(child_1.genes) | ||
self.mutate_function(child_2.genes) | ||
count+=2 | ||
print("Created "+str(count)+" individuals"+" generation: "+str(gen)) | ||
new_population.append(child_1) | ||
if len(new_population) < self.population_size: | ||
new_population.append(child_2) | ||
if len(new_population) < self.population_size: | ||
new_population.append(child_1) | ||
|
||
|
||
|
||
self.current_generation = new_population | ||
|
||
def create_first_generation(self): | ||
"""Create the first population, calculate the population's fitness and | ||
rank the population by fitness according to the order specified. | ||
""" | ||
self.create_initial_population() | ||
self.calculate_population_fitness() | ||
self.rank_population() | ||
|
||
def create_next_generation(self,gen): | ||
"""Create subsequent populations, calculate the population fitness and | ||
rank the population by fitness in the order specified. | ||
""" | ||
self.create_new_population(gen) | ||
self.calculate_population_fitness() | ||
self.rank_population() | ||
|
||
def run(self): | ||
"""Run (solve) the Genetic Algorithm.""" | ||
self.create_first_generation() | ||
print("Population created!") | ||
for _ in range(1, self.generations): | ||
self.best_individual()[1].game_board.update_viz() | ||
print("best fitness: "+str(self.best_individual()[0])) | ||
print("Generation "+str(_)+" completed!") | ||
self.create_next_generation(_) | ||
print("All done!") | ||
|
||
def best_individual(self): | ||
"""Return the individual with the best fitness in the current | ||
generation. | ||
""" | ||
best = self.current_generation[0] | ||
return (best.fitness, best.genes) | ||
|
||
def last_generation(self): | ||
"""Return members of the last generation as a generator function.""" | ||
return ((member.fitness, member.genes) for member | ||
in self.current_generation) | ||
|
||
|
||
class Chromosome(object): | ||
""" Chromosome class that encapsulates an individual's fitness and solution | ||
representation. | ||
""" | ||
def __init__(self, genes): | ||
"""Initialise the Chromosome.""" | ||
self.genes = genes | ||
self.fitness = 0 | ||
|
||
def __repr__(self): | ||
"""Return initialised Chromosome representation in human readable form. | ||
""" | ||
return repr((self.fitness, self.genes)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,65 @@ | ||
from pyeasyga import pyeasyga | ||
from GameGenetics import * | ||
|
||
|
||
seed_data = GameGenetics() | ||
print("seed") | ||
# initialise the GA | ||
ga = pyeasyga.GeneticAlgorithm(seed_data, | ||
population_size=300, | ||
generations=200, | ||
mutation_probability=MUTATION_RATE, | ||
elitism=True, | ||
maximise_fitness=True) | ||
if __name__ == "__main__": | ||
import pyeasyga.pyeasyga as ps | ||
from GameGenetics import * | ||
seed_data = GameGenetics() | ||
# initialise the GA | ||
ga = ps.GeneticAlgorithm(seed_data, | ||
population_size=50, | ||
generations=200, | ||
mutation_probability=MUTATION_RATE, | ||
elitism=True, | ||
maximise_fitness=False) | ||
|
||
|
||
# define and set function to create a candidate solution representation | ||
def create_individual(data): | ||
return data.create_individual() | ||
# define and set function to create a candidate solution representation | ||
def create_individual(data): | ||
return data.create_individual() | ||
|
||
|
||
ga.create_individual = create_individual | ||
ga.create_individual = create_individual | ||
|
||
|
||
# define and set the GA's crossover operation | ||
def crossover(parent_1, parent_2): | ||
return parent_1,parent_2 | ||
# define and set the GA's crossover operation | ||
def crossover(parent_1, parent_2): | ||
return parent_1,parent_2 | ||
|
||
ga.crossover_function = crossover | ||
ga.crossover_function = crossover | ||
|
||
# define and set the GA's mutation operation | ||
def mutate(individual): | ||
individual.mutation(MUTATION_CHANGE) | ||
return individual | ||
# define and set the GA's mutation operation | ||
def mutate(individual): | ||
individual.mutation(MUTATION_CHANGE) | ||
return individual | ||
|
||
ga.mutate_function = mutate | ||
ga.mutate_function = mutate | ||
|
||
|
||
# define and set the GA's selection operation | ||
def selection(population): | ||
r = random.random() | ||
fitness_list = [element.genes.get_fitness() for element in population] | ||
sum_of_fitness = sum(fitness_list) | ||
probs = [fit / sum_of_fitness for fit in fitness_list] | ||
i = 0 | ||
while r > 0: | ||
r -= probs[i] | ||
return population[i] | ||
# define and set the GA's selection operation | ||
def selection(population): | ||
r = random.random() | ||
fitness_list = [element.genes.get_fitness() for element in population] | ||
sum_of_fitness = sum(fitness_list) | ||
probs = [fit / sum_of_fitness for fit in fitness_list] | ||
i = 0 | ||
while r > 0: | ||
r -= probs[i] | ||
return population[i] | ||
|
||
|
||
ga.selection_function = selection | ||
ga.selection_function = selection | ||
|
||
|
||
# define a fitness function | ||
def fitness(individual, data): | ||
return individual.get_fitness() | ||
# define a fitness function | ||
def fitness(individual, data): | ||
return individual.get_fitness() | ||
|
||
|
||
ga.fitness_function = fitness # set the GA's fitness function | ||
ga.fitness_function = fitness # set the GA's fitness function | ||
|
||
|
||
ga.run() # run the GA | ||
ga.run() # run the GA | ||
|
||
|
||
ga.best_individual()[1].game_board.update_viz() | ||
ga.best_individual()[1].game_board.update_viz() | ||
|