integrated explosion animation, reformatted roulette-GA file

This commit is contained in:
s452645 2021-06-20 21:55:23 +02:00
parent 2944099e93
commit dc539223db
12 changed files with 223 additions and 207 deletions

View File

@ -0,0 +1,175 @@
import numpy as np
import random
import operator
import pandas as pd
from algorithms.learn.genetic_algorithm import helpers
import os
from minefield import Minefield
gl_minefield = None
scores_table = None
class Fitness:
def __init__(self, route):
self.route = route
self.score = 0
self.fitness = 0.0
def route_distance(self):
if self.score == 0:
self.score = helpers.get_score(gl_minefield, self.route, scores_table)
return self.score
def route_fitness(self):
if self.fitness == 0:
self.fitness = 1000 / float(self.route_distance())
return self.fitness
def create_route(mine_list):
route = random.sample(mine_list, len(mine_list))
return route
def initial_population(pop_size, mine_list):
population = []
for i in range(0, pop_size):
population.append(create_route(mine_list))
return population
def rank_routes(population):
fitness_results = {}
for i in range(0, len(population)):
fitness_results[i] = Fitness(population[i]).route_fitness()
return sorted(fitness_results.items(), key=operator.itemgetter(1), reverse=True)
def selection(pop_ranked, elite_size):
selection_results = []
df = pd.DataFrame(np.array(pop_ranked), columns=["Index", "Fitness"])
df['cum_sum'] = df.Fitness.cumsum()
df['cum_perc'] = 100 * df.cum_sum / df.Fitness.sum()
for i in range(0, elite_size):
selection_results.append(pop_ranked[i][0])
for i in range(0, len(pop_ranked) - elite_size):
pick = 100 * random.random()
for j in range(0, len(pop_ranked)):
if pick <= df.iat[j, 3]:
selection_results.append(pop_ranked[j][0])
break
return selection_results
def mating_pool(population, selection_results):
matingpool = []
for i in range(0, len(selection_results)):
index = selection_results[i]
matingpool.append(population[index])
return matingpool
def breed(parent1, parent2):
child_p1 = []
gene_a = int(random.random() * len(parent1))
gene_b = int(random.random() * len(parent1))
start_gene = min(gene_a, gene_b)
end_gene = max(gene_a, gene_b)
for i in range(start_gene, end_gene):
child_p1.append(parent1[i])
child_p2 = [item for item in parent2 if item not in child_p1]
child = child_p1 + child_p2
return child
def breed_population(matingpool, elite_size):
children = []
length = len(matingpool) - elite_size
pool = random.sample(matingpool, len(matingpool))
for i in range(0, elite_size):
children.append(matingpool[i])
for i in range(0, length):
child = breed(pool[i], pool[len(matingpool) - i - 1])
children.append(child)
return children
def mutate(individual, mutation_rate):
for swapped in range(len(individual)):
if random.random() < mutation_rate:
swap_with = int(random.random() * len(individual))
city1 = individual[swapped]
city2 = individual[swap_with]
individual[swapped] = city2
individual[swap_with] = city1
return individual
def mutate_population(population, mutation_rate):
mutated_pop = []
for ind in range(0, len(population)):
mutated_ind = mutate(population[ind], mutation_rate)
mutated_pop.append(mutated_ind)
return mutated_pop
def next_generation(scores, current_gen, elite_size, mutation_rate):
selection_results = selection(scores, elite_size)
matingpool = mating_pool(current_gen, selection_results)
children = breed_population(matingpool, elite_size)
next_gen = mutate_population(children, mutation_rate)
return next_gen
def genetic_algorithm(minefield, population, pop_size, elite_size, mutation_rate, generations):
global gl_minefield, scores_table
gl_minefield = minefield
scores_table = helpers.create_scores_table(gl_minefield)
pop = initial_population(pop_size, population)
scores = rank_routes(pop)
print("Initial score: " + str(1000 / scores[0][1]))
for i in range(0, generations):
pop = next_generation(scores, pop, elite_size, mutation_rate)
scores = rank_routes(pop)
print(f"Generation {i} best score: {str(1000 / scores[0][1])}")
best_route_index = scores[0][0]
best_route = pop[best_route_index]
print(best_route)
print("Final score: " + str(1000 / scores[0][1]))
best_route_index = scores[0][0]
best_route = pop[best_route_index]
return best_route
if __name__ == "__main__":
gl_minefield = Minefield(os.path.join("..", "..", "..", "resources", "minefields", "fourthmap.json"))
genetic_algorithm(minefield=gl_minefield,
population=helpers.get_mines_coords(gl_minefield),
pop_size=100,
elite_size=20,
mutation_rate=0.01,
generations=100)

View File

@ -7,6 +7,7 @@ from numpy.random import rand
from algorithms.learn.genetic_algorithm import helpers from algorithms.learn.genetic_algorithm import helpers
# this is helper function for sum_distance function, it counts the taxi cab distance between 2 vectors [x,y] # this is helper function for sum_distance function, it counts the taxi cab distance between 2 vectors [x,y]
def distance(x, y): def distance(x, y):
temp1 = abs(x[0] - y[0]) temp1 = abs(x[0] - y[0])
@ -14,6 +15,7 @@ def distance(x,y):
vector_distance = temp1 + temp2 vector_distance = temp1 + temp2
return vector_distance return vector_distance
# this is fitting function which tells how well specimen fits the environment # this is fitting function which tells how well specimen fits the environment
# this function counts the sum of distances between vectors for a specimen # this function counts the sum of distances between vectors for a specimen
# this was just for testing, it should be probably changed to A* # this was just for testing, it should be probably changed to A*
@ -76,7 +78,6 @@ def mutation(speciment, r_mut):
speciment[pom] = temp speciment[pom] = temp
# genetic algorithm # genetic algorithm
def genetic_algorithm(minefield, objective, n_iter, n_pop, r_cross, r_mut): def genetic_algorithm(minefield, objective, n_iter, n_pop, r_cross, r_mut):
# this is hardcoded list of coordinates of all mines (for tests only) which represents one speciment in population # this is hardcoded list of coordinates of all mines (for tests only) which represents one speciment in population
@ -116,7 +117,6 @@ def genetic_algorithm(minefield, objective, n_iter, n_pop, r_cross, r_mut):
if __name__ == "__main__": if __name__ == "__main__":
# define the total iterations # define the total iterations
n_iter = 100 n_iter = 100
# bits # bits
@ -131,11 +131,10 @@ if __name__ == "__main__":
# create new minefield instance # create new minefield instance
import os import os
from minefield import Minefield from minefield import Minefield
minefield = Minefield(os.path.join("..", "..", "..", "resources", "minefields", "fourthmap.json")) minefield = Minefield(os.path.join("..", "..", "..", "resources", "minefields", "fourthmap.json"))
# perform the genetic algorithm search # perform the genetic algorithm search
best, score = genetic_algorithm(minefield, helpers.get_score, n_iter, n_pop, r_cross, r_mut) best, score = genetic_algorithm(minefield, helpers.get_score, n_iter, n_pop, r_cross, r_mut)
print('Done!') print('Done!')
print('f(%s) = %f' % (best, score)) print('f(%s) = %f' % (best, score))

View File

@ -87,7 +87,7 @@ def get_score(minefield, speciment, table=None):
mine.active = False mine.active = False
minefield.next_turn(n_turns=cost) minefield.next_turn(n_turns=cost, mode="ga")
score += cost score += cost
score += 200 * minefield.explosions score += 200 * minefield.explosions

View File

@ -1,174 +0,0 @@
import time
import numpy as np, random, operator, pandas as pd
from algorithms.learn.genetic_algorithm import helpers
import os
from minefield import Minefield
gl_minefield = None
scores_table = None
class Fitness:
def __init__(self, route):
self.route = route
self.distance = 0
self.fitness = 0.0
def routeDistance(self):
if self.distance == 0:
self.distance = helpers.get_score(gl_minefield, self.route, scores_table)
return self.distance
def routeFitness(self):
if self.fitness == 0:
self.fitness = 1000 / float(self.routeDistance())
return self.fitness
def createRoute(cityList):
route = random.sample(cityList, len(cityList))
return route
def initialPopulation(popSize, cityList):
population = []
for i in range(0, popSize):
population.append(createRoute(cityList))
return population
def rankRoutes(population):
fitnessResults = {}
for i in range(0, len(population)):
fitnessResults[i] = Fitness(population[i]).routeFitness()
return sorted(fitnessResults.items(), key=operator.itemgetter(1), reverse=True)
def selection(popRanked, eliteSize):
selectionResults = []
df = pd.DataFrame(np.array(popRanked), columns=["Index", "Fitness"])
df['cum_sum'] = df.Fitness.cumsum()
df['cum_perc'] = 100 * df.cum_sum / df.Fitness.sum()
for i in range(0, eliteSize):
selectionResults.append(popRanked[i][0])
for i in range(0, len(popRanked) - eliteSize):
pick = 100 * random.random()
for i in range(0, len(popRanked)):
if pick <= df.iat[i, 3]:
selectionResults.append(popRanked[i][0])
break
return selectionResults
def matingPool(population, selectionResults):
matingpool = []
for i in range(0, len(selectionResults)):
index = selectionResults[i]
matingpool.append(population[index])
return matingpool
def breed(parent1, parent2):
child = []
childP1 = []
childP2 = []
geneA = int(random.random() * len(parent1))
geneB = int(random.random() * len(parent1))
startGene = min(geneA, geneB)
endGene = max(geneA, geneB)
for i in range(startGene, endGene):
childP1.append(parent1[i])
childP2 = [item for item in parent2 if item not in childP1]
child = childP1 + childP2
return child
def breedPopulation(matingpool, eliteSize):
children = []
length = len(matingpool) - eliteSize
pool = random.sample(matingpool, len(matingpool))
for i in range(0, eliteSize):
children.append(matingpool[i])
for i in range(0, length):
child = breed(pool[i], pool[len(matingpool) - i - 1])
children.append(child)
return children
def mutate(individual, mutationRate):
for swapped in range(len(individual)):
if (random.random() < mutationRate):
swapWith = int(random.random() * len(individual))
city1 = individual[swapped]
city2 = individual[swapWith]
individual[swapped] = city2
individual[swapWith] = city1
return individual
def mutatePopulation(population, mutationRate):
mutatedPop = []
for ind in range(0, len(population)):
mutatedInd = mutate(population[ind], mutationRate)
mutatedPop.append(mutatedInd)
return mutatedPop
def nextGeneration(scores, currentGen, eliteSize, mutationRate):
selectionResults = selection(scores, eliteSize)
matingpool = matingPool(currentGen, selectionResults)
children = breedPopulation(matingpool, eliteSize)
nextGeneration = mutatePopulation(children, mutationRate)
return nextGeneration
def genetic_algorithm(minefield, population, popSize, eliteSize, mutationRate, generations):
global gl_minefield, scores_table
gl_minefield = minefield
scores_table = helpers.create_scores_table(gl_minefield)
pop = initialPopulation(popSize, population)
scores = rankRoutes(pop)
print("Initial score: " + str(1000 / scores[0][1]))
for i in range(0, generations):
pop = nextGeneration(scores, pop, eliteSize, mutationRate)
scores = rankRoutes(pop)
print(f"Generation {i} best score: {str(1000 / scores[0][1])}")
bestRouteIndex = scores[0][0]
bestRoute = pop[bestRouteIndex]
print(bestRoute)
print("Final score: " + str(1000 / scores[0][1]))
bestRouteIndex = scores[0][0]
bestRoute = pop[bestRouteIndex]
return bestRoute
if __name__ == "__main__":
gl_minefield = Minefield(os.path.join("..", "..", "..", "resources", "minefields", "fourthmap.json"))
genetic_algorithm(minefield=gl_minefield,
population=helpers.get_mines_coords(gl_minefield),
popSize=100,
eliteSize=20,
mutationRate=0.01,
generations=100)

View File

@ -74,6 +74,9 @@ def blit_graphics(minefield):
seconds = tile.mine.timer % 60 seconds = tile.mine.timer % 60
display_time_mine(tile.position, minutes, "0" + str(seconds)) display_time_mine(tile.position, minutes, "0" + str(seconds))
if tile.mine.blacked:
const.SCREEN.blit(const.MINE_INACTIVE, tile_screen_coords)
# changed sapper's movement from jumping to continuous movement (moved everything to Agent's class) # changed sapper's movement from jumping to continuous movement (moved everything to Agent's class)
# # sapper # # sapper
# display_sapper( # display_sapper(

View File

@ -3,12 +3,15 @@ import pygame
import os import os
from project_constants import V_TILE_SIZE, DIR_ASSETS, SCREEN from project_constants import V_TILE_SIZE, DIR_ASSETS, SCREEN
from objects.mine_models.mine import Mine
import assets.display_assets import assets.display_assets
class Explosion(pygame.sprite.Sprite): class Explosion(pygame.sprite.Sprite):
def __init__(self, coords: (int, int)): def __init__(self, coords: (int, int) = None, mine: Mine = None):
if coords is None:
coords = mine.position
pygame.sprite.Sprite.__init__(self) pygame.sprite.Sprite.__init__(self)
@ -28,6 +31,7 @@ class Explosion(pygame.sprite.Sprite):
assets.display_assets.calculate_screen_position(coords), assets.display_assets.calculate_screen_position(coords),
(V_TILE_SIZE, V_TILE_SIZE) (V_TILE_SIZE, V_TILE_SIZE)
) )
self.mine = mine
def update(self, *args, **kwargs): def update(self, *args, **kwargs):
@ -39,6 +43,10 @@ class Explosion(pygame.sprite.Sprite):
if self.frame == len(self.explosion_animation): if self.frame == len(self.explosion_animation):
self.kill() self.kill()
elif self.frame == len(self.explosion_animation) - 1:
if self.mine is not None:
self.mine.blacked = True
else: else:
self.image = self.explosion_animation[self.frame] self.image = self.explosion_animation[self.frame]

10
game.py
View File

@ -5,7 +5,7 @@ import project_constants as const
from assets.display_assets import blit_graphics from assets.display_assets import blit_graphics
from algorithms.search import a_star from algorithms.search import a_star
from algorithms.learn.genetic_algorithm import new_ga, helpers from algorithms.learn.genetic_algorithm import ga_roulette, helpers
from minefield import Minefield from minefield import Minefield
@ -150,11 +150,11 @@ class Game:
genetics_minefield = Minefield(const.MAP_RANDOM_10x10) genetics_minefield = Minefield(const.MAP_RANDOM_10x10)
sequence = \ sequence = \
new_ga.genetic_algorithm(minefield=genetics_minefield, ga_roulette.genetic_algorithm(minefield=genetics_minefield,
population=helpers.get_mines_coords(self.minefield), population=helpers.get_mines_coords(self.minefield),
popSize=100, pop_size=100,
eliteSize=20, elite_size=20,
mutationRate=0.01, mutation_rate=0.01,
generations=generations) generations=generations)
self.genetic_sequence = sequence self.genetic_sequence = sequence

View File

@ -57,12 +57,6 @@ def main():
# checking if game should stop running # checking if game should stop running
running = not is_quit_button_pressed(events) running = not is_quit_button_pressed(events)
# TODO : added for testing, remove after moving the explosion line
keys = pygame.key.get_pressed()
if keys[pygame.K_SPACE]:
# TODO : move this line to where explosion is called
const.EXPLOSIONS.add(Explosion((2, 3)))
# drawing minefield and agent instances # drawing minefield and agent instances
game.draw_minefield() game.draw_minefield()
@ -187,7 +181,7 @@ def main():
if not game.agent.defuse_a_mine(game.get_mine(game.goal)): if not game.agent.defuse_a_mine(game.get_mine(game.goal)):
print("BOOOOOOM\n\n") print("BOOOOOOM\n\n")
game.explosion(game.get_mine(game.goal)) game.explosion(game.get_mine(game.goal))
# is_game_over = True const.EXPLOSIONS.add(Explosion(mine=game.get_mine(game.goal)))
else: else:
print("guess you will live a little longer...\n\n") print("guess you will live a little longer...\n\n")

View File

@ -1,4 +1,5 @@
import project_constants as const import project_constants as const
from assets.explosion import Explosion
from objects import tile as tl, agent as ag from objects import tile as tl, agent as ag
from objects.mine_models.time_mine import TimeMine from objects.mine_models.time_mine import TimeMine
import json_generator as jg import json_generator as jg
@ -39,7 +40,7 @@ class Minefield:
self.time_mines = self._get_time_mines() self.time_mines = self._get_time_mines()
def next_turn(self, n_turns=1): def next_turn(self, n_turns=1, mode="main"):
self.turn += n_turns self.turn += n_turns
self.points += n_turns self.points += n_turns
@ -47,11 +48,13 @@ class Minefield:
mine.timer = max(0, mine.starting_time - int(self.turn)) mine.timer = max(0, mine.starting_time - int(self.turn))
if mine.timer == 0 and mine.active: if mine.timer == 0 and mine.active:
# TODO: BOOM
self.explosions += 1 self.explosions += 1
self.points += const.EXPLOSION_PENALTY self.points += const.EXPLOSION_PENALTY
mine.active = False mine.active = False
if mode == "main":
const.EXPLOSIONS.add(Explosion(mine=mine))
def get_active_mines(self): def get_active_mines(self):
mines = list() mines = list()

View File

@ -17,11 +17,13 @@ class Mine(ABC):
self.position = position self.position = position
self.wire = None self.wire = None
self.active = active self.active = active
self.blacked = False
@abstractmethod @abstractmethod
def disarm(self, wire): def disarm(self, wire):
if wire == self.wire: if wire == self.wire:
self.active = False self.active = False
self.blacked = True
return True return True
else: else:

View File

@ -161,6 +161,12 @@ HIGHLIGHT = pygame.transform.scale(
) )
HIGHLIGHT.set_alpha(100) HIGHLIGHT.set_alpha(100)
MINE_INACTIVE = pygame.transform.scale(
pygame.image.load(os.path.join(DIR_ASSETS, "old_tiles/tile_black.png")),
(V_TILE_SIZE, V_TILE_SIZE)
)
MINE_INACTIVE.set_alpha(160)
# ============== # # ============== #
# ==== MAPS ==== # # ==== MAPS ==== #

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B