faster GA
This commit is contained in:
parent
fcbd10684d
commit
d3ae11fbb1
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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),
|
||||||
|
1
game.py
1
game.py
@ -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):
|
||||||
|
3
main.py
3
main.py
@ -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()
|
||||||
|
24
minefield.py
24
minefield.py
@ -37,15 +37,13 @@ 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:
|
||||||
@ -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
|
||||||
|
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
@ -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": {
|
||||||
|
Loading…
Reference in New Issue
Block a user