faster GA

This commit is contained in:
s452645 2021-06-20 11:42:43 +02:00
parent fcbd10684d
commit d3ae11fbb1
8 changed files with 46 additions and 51 deletions

View File

@ -131,7 +131,7 @@ 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", "fifthmap.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)

View File

@ -1,3 +1,5 @@
import time
from minefield import Minefield from minefield import Minefield
import project_constants as const import project_constants as const
@ -14,7 +16,7 @@ def possible_states(row, column):
possible_states.append(State(row + 1, column, Direction.UP)) possible_states.append(State(row + 1, column, Direction.UP))
if Minefield.is_valid_move(row - 1, column): if Minefield.is_valid_move(row - 1, column):
possible_states.append(State(row + 1, column, Direction.DOWN)) possible_states.append(State(row - 1, column, Direction.DOWN))
if Minefield.is_valid_move(row, column + 1): if Minefield.is_valid_move(row, column + 1):
possible_states.append(State(row, column + 1, Direction.LEFT)) possible_states.append(State(row, column + 1, Direction.LEFT))
@ -70,7 +72,7 @@ def get_score(minefield, speciment, table=None):
initial_state = State(0, 0, Direction.RIGHT) initial_state = State(0, 0, Direction.RIGHT)
if table is not None: if table is not None:
for el_index in range(len(speciment) - 1): for el_index in range(len(speciment)):
end_state, cost = table[(initial_state.row, initial_state.column)] \ end_state, cost = table[(initial_state.row, initial_state.column)] \
[(speciment[el_index][0], speciment[el_index][1])] \ [(speciment[el_index][0], speciment[el_index][1])] \
@ -81,13 +83,11 @@ def get_score(minefield, speciment, table=None):
mine = minefield.matrix[speciment[el_index][0]][speciment[el_index][1]].mine mine = minefield.matrix[speciment[el_index][0]][speciment[el_index][1]].mine
if isinstance(mine, ChainedMine) and mine.predecessor is not None and mine.predecessor.active: if isinstance(mine, ChainedMine) and mine.predecessor is not None and mine.predecessor.active:
cost += 200 score += 200
mine.active = False mine.active = False
for _ in range(cost): minefield.next_turn(n_turns=cost)
minefield.next_turn()
score += cost score += cost
score += 200 * minefield.explosions score += 200 * minefield.explosions

View File

@ -23,6 +23,7 @@ class Fitness:
def routeFitness(self): def routeFitness(self):
if self.fitness == 0: if self.fitness == 0:
self.fitness = 1000 / float(self.routeDistance()) self.fitness = 1000 / float(self.routeDistance())
return self.fitness return self.fitness
@ -42,10 +43,8 @@ def initialPopulation(popSize, cityList):
def rankRoutes(population): def rankRoutes(population):
fitnessResults = {} fitnessResults = {}
timer_collect_results = Timer("Collect fitness results")
for i in range(0, len(population)): for i in range(0, len(population)):
fitnessResults[i] = Fitness(population[i]).routeFitness() fitnessResults[i] = Fitness(population[i]).routeFitness()
timer_collect_results.stop()
return sorted(fitnessResults.items(), key=operator.itemgetter(1), reverse=True) return sorted(fitnessResults.items(), key=operator.itemgetter(1), reverse=True)
@ -142,23 +141,16 @@ def genetic_algorithm(minefield, population, popSize, eliteSize, mutationRate, g
global gl_minefield, scores_table global gl_minefield, scores_table
gl_minefield = minefield gl_minefield = minefield
timer_scores_table = Timer("Create scores table")
scores_table = helpers.create_scores_table(gl_minefield) scores_table = helpers.create_scores_table(gl_minefield)
timer_scores_table.stop()
timer_initial_population = Timer("Init and rank population")
pop = initialPopulation(popSize, population) pop = initialPopulation(popSize, population)
scores = rankRoutes(pop) scores = rankRoutes(pop)
timer_initial_population.stop()
print("Initial score: " + str(1000 / scores[0][1])) print("Initial score: " + str(1000 / scores[0][1]))
for i in range(0, generations): for i in range(0, generations):
pop = nextGeneration(scores, pop, eliteSize, mutationRate) pop = nextGeneration(scores, pop, eliteSize, mutationRate)
timer_rank_generation = Timer("Rank generation")
scores = rankRoutes(pop) scores = rankRoutes(pop)
timer_rank_generation.stop()
print(f"Generation {i} best score: {str(1000 / scores[0][1])}") print(f"Generation {i} best score: {str(1000 / scores[0][1])}")
bestRouteIndex = scores[0][0] bestRouteIndex = scores[0][0]
@ -171,17 +163,8 @@ def genetic_algorithm(minefield, population, popSize, eliteSize, mutationRate, g
return bestRoute return bestRoute
class Timer:
def __init__(self, name):
self.name = name
self.start_time = time.time()
def stop(self):
print(f"{self.name} took {time.time() - self.start_time} seconds.")
if __name__ == "__main__": if __name__ == "__main__":
gl_minefield = Minefield(os.path.join("..", "..", "..", "resources", "minefields", "fifthmap.json")) gl_minefield = Minefield(os.path.join("..", "..", "..", "resources", "minefields", "fourthmap.json"))
genetic_algorithm(minefield=gl_minefield, genetic_algorithm(minefield=gl_minefield,
population=helpers.get_mines_coords(gl_minefield), population=helpers.get_mines_coords(gl_minefield),

View File

@ -120,6 +120,7 @@ class Game:
def revert_minefield_turn(self): def revert_minefield_turn(self):
self.minefield.turn -= 1 self.minefield.turn -= 1
self.minefield.points -= 1
# returns turn number # returns turn number
def get_turn_number(self): def get_turn_number(self):

View File

@ -158,6 +158,9 @@ def main():
# reset values after the game loop # reset values after the game loop
if game.agent_made_all_actions(action_sequence): if game.agent_made_all_actions(action_sequence):
if genetics:
game.minefield.points -= 1
# clean up after game loop # clean up after game loop
game.agent_take_last_action() game.agent_take_last_action()
game.cleanup_after_game_loop() game.cleanup_after_game_loop()

View File

@ -37,22 +37,20 @@ class Minefield:
predecessor = self.matrix[predecessor_row][predecessor_column].mine predecessor = self.matrix[predecessor_row][predecessor_column].mine
self.matrix[successor_row][successor_column].mine.predecessor = predecessor self.matrix[successor_row][successor_column].mine.predecessor = predecessor
def next_turn(self): self.time_mines = self._get_time_mines()
self.turn += 1
self.points += 1
for row in range(const.V_GRID_VER_TILES): def next_turn(self, n_turns=1):
for column in range(const.V_GRID_VER_TILES): self.turn += n_turns
mine = self.matrix[row][column].mine self.points += n_turns
if mine is not None and isinstance(mine, TimeMine): for mine in self.time_mines:
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 # TODO: BOOM
self.explosions += 1 self.explosions += 1
self.points += const.EXPLOSION_PENALTY self.points += const.EXPLOSION_PENALTY
mine.active = False mine.active = False
def get_active_mines(self): def get_active_mines(self):
mines = list() mines = list()
@ -96,3 +94,15 @@ class Minefield:
def __copy__(self): def __copy__(self):
copy = Minefield(self.json_path) copy = Minefield(self.json_path)
return copy return copy
def _get_time_mines(self):
time_mines = list()
for row in range(const.V_GRID_VER_TILES):
for column in range(const.V_GRID_VER_TILES):
mine = self.matrix[row][column].mine
if mine is not None and isinstance(mine, TimeMine):
time_mines.append(mine)
return time_mines

View File

@ -164,6 +164,6 @@ HIGHLIGHT.set_alpha(100)
# ==== MAPS ==== # # ==== MAPS ==== #
# ============== # # ============== #
MAP_RANDOM_10x10 = os.path.join("resources", "minefields", "fifthmap.json") MAP_RANDOM_10x10 = os.path.join("resources", "minefields", "fourthmap.json")

View File

@ -14,8 +14,7 @@
"0,3": { "0,3": {
"terrain": "GRASS", "terrain": "GRASS",
"mine": { "mine": {
"mine_type": "chained", "mine_type": "standard"
"predecessor": null
} }
}, },
"0,4": { "0,4": {
@ -41,7 +40,7 @@
"terrain": "MUD", "terrain": "MUD",
"mine": { "mine": {
"mine_type": "chained", "mine_type": "chained",
"predecessor": "0,3" "predecessor": "0,4"
} }
}, },
"0,9": { "0,9": {
@ -69,7 +68,7 @@
"terrain": "GRASS", "terrain": "GRASS",
"mine": { "mine": {
"mine_type": "chained", "mine_type": "chained",
"predecessor": null "predecessor": "3,3"
} }
}, },
"1,4": { "1,4": {
@ -165,7 +164,7 @@
"terrain": "MUD", "terrain": "MUD",
"mine": { "mine": {
"mine_type": "chained", "mine_type": "chained",
"predecessor": "1,3" "predecessor": null
} }
}, },
"3,4": { "3,4": {
@ -229,7 +228,7 @@
"terrain": "GRASS", "terrain": "GRASS",
"mine": { "mine": {
"mine_type": "chained", "mine_type": "chained",
"predecessor": null "predecessor": "7,0"
} }
}, },
"4,8": { "4,8": {
@ -247,8 +246,8 @@
"5,1": { "5,1": {
"terrain": "GRASS", "terrain": "GRASS",
"mine": { "mine": {
"mine_type": "chained", "mine_type": "time",
"predecessor": "2,3" "timer": 50
} }
}, },
"5,2": { "5,2": {
@ -262,8 +261,7 @@
"5,4": { "5,4": {
"terrain": "GRASS", "terrain": "GRASS",
"mine": { "mine": {
"mine_type": "chained", "mine_type": "standard"
"predecessor": "2,8"
} }
}, },
"5,5": { "5,5": {
@ -334,7 +332,7 @@
"terrain": "GRASS", "terrain": "GRASS",
"mine": { "mine": {
"mine_type": "chained", "mine_type": "chained",
"predecessor": "4,7" "predecessor": null
} }
}, },
"7,1": { "7,1": {