added 'score' textbox and 'GA iterations' input
This commit is contained in:
parent
dfac987643
commit
fcbd10684d
@ -72,25 +72,12 @@ def get_score(minefield, speciment, table=None):
|
|||||||
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) - 1):
|
||||||
|
|
||||||
if table[(initial_state.row, initial_state.column)] \
|
|
||||||
[(speciment[el_index][0], speciment[el_index][1])] \
|
|
||||||
[initial_state.direction]:
|
|
||||||
|
|
||||||
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])] \
|
||||||
[initial_state.direction]
|
[initial_state.direction]
|
||||||
|
|
||||||
initial_state = State(speciment[el_index][0], speciment[el_index][1], end_state.direction)
|
initial_state = State(speciment[el_index][0], speciment[el_index][1], end_state.direction)
|
||||||
|
|
||||||
else:
|
|
||||||
action_sequence, _, cost = \
|
|
||||||
graphsearch(initial_state,
|
|
||||||
minefield,
|
|
||||||
target_type="mine",
|
|
||||||
tox=speciment[el_index][0],
|
|
||||||
toy=speciment[el_index][1],
|
|
||||||
with_data=True)
|
|
||||||
|
|
||||||
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:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import time
|
||||||
import numpy as np, random, operator, pandas as pd
|
import numpy as np, random, operator, pandas as pd
|
||||||
from algorithms.learn.genetic_algorithm import helpers
|
from algorithms.learn.genetic_algorithm import helpers
|
||||||
|
|
||||||
@ -40,8 +41,12 @@ 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)
|
||||||
|
|
||||||
|
|
||||||
@ -137,16 +142,24 @@ 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]
|
||||||
bestRoute = pop[bestRouteIndex]
|
bestRoute = pop[bestRouteIndex]
|
||||||
@ -158,6 +171,15 @@ 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", "fifthmap.json"))
|
||||||
|
|
||||||
|
73
game.py
73
game.py
@ -38,6 +38,7 @@ class Game:
|
|||||||
self.action_delta_time = 0
|
self.action_delta_time = 0
|
||||||
|
|
||||||
self.genetic_sequence = None
|
self.genetic_sequence = None
|
||||||
|
self.genetics_done = False
|
||||||
|
|
||||||
# declaring and initializing gui components
|
# declaring and initializing gui components
|
||||||
# ui_component managers
|
# ui_component managers
|
||||||
@ -45,12 +46,15 @@ class Game:
|
|||||||
self.game_over_gui_components_manager = UiComponentsManager()
|
self.game_over_gui_components_manager = UiComponentsManager()
|
||||||
|
|
||||||
# in game gui
|
# in game gui
|
||||||
|
self.textbox_points = TextBox((0, 0), (0, 0))
|
||||||
|
|
||||||
self.input_box_row = InputBox((0, 0), (0, 0))
|
self.input_box_row = InputBox((0, 0), (0, 0))
|
||||||
self.input_box_column = InputBox((0, 0), (0, 0))
|
self.input_box_column = InputBox((0, 0), (0, 0))
|
||||||
self.button_auto = Button((0, 0), (0, 0))
|
self.button_auto = Button((0, 0), (0, 0))
|
||||||
self.button_genetic_algorithm = Button((0, 0), (0, 0))
|
|
||||||
self.button_random = Button((0, 0), (0, 0))
|
self.button_random = Button((0, 0), (0, 0))
|
||||||
self.button_ok = Button((0, 0), (0, 0))
|
self.button_ok = Button((0, 0), (0, 0))
|
||||||
|
self.button_genetic_algorithm = Button((0, 0), (0, 0))
|
||||||
|
self.input_box_generations = InputBox((0, 0), (0, 0))
|
||||||
|
|
||||||
# game over screen
|
# game over screen
|
||||||
self.text_box_game_over = TextBox((0, 0), (0, 0))
|
self.text_box_game_over = TextBox((0, 0), (0, 0))
|
||||||
@ -109,6 +113,7 @@ class Game:
|
|||||||
if self.millisecond_timer >= const.TURN_INTERVAL:
|
if self.millisecond_timer >= const.TURN_INTERVAL:
|
||||||
self.turn += 1
|
self.turn += 1
|
||||||
self.minefield.next_turn()
|
self.minefield.next_turn()
|
||||||
|
self.textbox_points.text = str(self.minefield.points)
|
||||||
|
|
||||||
# resetting timer
|
# resetting timer
|
||||||
self.millisecond_timer %= const.TURN_INTERVAL
|
self.millisecond_timer %= const.TURN_INTERVAL
|
||||||
@ -138,7 +143,9 @@ class Game:
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def run_genetics(self):
|
def run_genetics(self, generations=10):
|
||||||
|
print("Starting genetics algorithm...")
|
||||||
|
|
||||||
genetics_minefield = Minefield(const.MAP_RANDOM_10x10)
|
genetics_minefield = Minefield(const.MAP_RANDOM_10x10)
|
||||||
|
|
||||||
sequence = \
|
sequence = \
|
||||||
@ -147,9 +154,10 @@ class Game:
|
|||||||
popSize=100,
|
popSize=100,
|
||||||
eliteSize=20,
|
eliteSize=20,
|
||||||
mutationRate=0.01,
|
mutationRate=0.01,
|
||||||
generations=15)
|
generations=generations)
|
||||||
|
|
||||||
self.genetic_sequence = sequence
|
self.genetic_sequence = sequence
|
||||||
|
self.genetics_done = True
|
||||||
|
|
||||||
def set_next_genetic_target(self):
|
def set_next_genetic_target(self):
|
||||||
if any(self.genetic_sequence):
|
if any(self.genetic_sequence):
|
||||||
@ -187,6 +195,10 @@ class Game:
|
|||||||
row, column = position
|
row, column = position
|
||||||
return self.minefield.matrix[row][column].mine
|
return self.minefield.matrix[row][column].mine
|
||||||
|
|
||||||
|
def explosion(self, position):
|
||||||
|
# show explosion on position
|
||||||
|
self.minefield.points += const.EXPLOSION_PENALTY
|
||||||
|
|
||||||
# initializes attributes before game loop begins
|
# initializes attributes before game loop begins
|
||||||
def initialize_before_game_loop(self):
|
def initialize_before_game_loop(self):
|
||||||
self.agent_action = None
|
self.agent_action = None
|
||||||
@ -258,8 +270,15 @@ class Game:
|
|||||||
gui_y = const.SCREEN.get_height() / 2 - (2 * ib_height + 3 * bt_height + 50) / 2
|
gui_y = const.SCREEN.get_height() / 2 - (2 * ib_height + 3 * bt_height + 50) / 2
|
||||||
|
|
||||||
# creating in game gui components
|
# creating in game gui components
|
||||||
|
self.textbox_points = TextBox(
|
||||||
|
position=(gui_x, gui_y - 134),
|
||||||
|
dimensions=(gui_width, ib_height - 10),
|
||||||
|
text="0",
|
||||||
|
box_color=(172, 220, 172)
|
||||||
|
)
|
||||||
|
|
||||||
self.input_box_row = InputBox(
|
self.input_box_row = InputBox(
|
||||||
position=(gui_x, gui_y),
|
position=(gui_x, gui_y - 50),
|
||||||
dimensions=(gui_width, ib_height),
|
dimensions=(gui_width, ib_height),
|
||||||
text="row",
|
text="row",
|
||||||
box_color=(100, 200, 100),
|
box_color=(100, 200, 100),
|
||||||
@ -273,7 +292,7 @@ class Game:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.input_box_column = InputBox(
|
self.input_box_column = InputBox(
|
||||||
position=(gui_x, gui_y + ib_height + 10),
|
position=(gui_x, gui_y + ib_height - 40),
|
||||||
dimensions=(gui_width, ib_height),
|
dimensions=(gui_width, ib_height),
|
||||||
text="column",
|
text="column",
|
||||||
box_color=(100, 200, 100),
|
box_color=(100, 200, 100),
|
||||||
@ -287,7 +306,7 @@ class Game:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.button_auto = Button(
|
self.button_auto = Button(
|
||||||
position=(gui_x, gui_y + 2 * ib_height + 20),
|
position=(gui_x, gui_y + 2 * ib_height - 30),
|
||||||
dimensions=(gui_width, bt_height),
|
dimensions=(gui_width, bt_height),
|
||||||
text="auto",
|
text="auto",
|
||||||
box_color=(100, 200, 100),
|
box_color=(100, 200, 100),
|
||||||
@ -295,17 +314,8 @@ class Game:
|
|||||||
outline_additional_pixel=True
|
outline_additional_pixel=True
|
||||||
)
|
)
|
||||||
|
|
||||||
self.button_genetic_algorithm = Button(
|
|
||||||
position=(gui_x, gui_y + 2 * ib_height + bt_height + 30),
|
|
||||||
dimensions=(gui_width, bt_height),
|
|
||||||
text="genetic",
|
|
||||||
box_color=(100, 200, 100),
|
|
||||||
outline_color=(80, 180, 80),
|
|
||||||
outline_additional_pixel=True
|
|
||||||
)
|
|
||||||
|
|
||||||
self.button_random = Button(
|
self.button_random = Button(
|
||||||
position=(gui_x, gui_y + 2 * ib_height + 2 * bt_height + 40),
|
position=(gui_x, gui_y + 2 * ib_height + 1 * bt_height - 20),
|
||||||
dimensions=(gui_width, bt_height),
|
dimensions=(gui_width, bt_height),
|
||||||
text="random",
|
text="random",
|
||||||
box_color=(100, 200, 100),
|
box_color=(100, 200, 100),
|
||||||
@ -314,7 +324,7 @@ class Game:
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.button_ok = Button(
|
self.button_ok = Button(
|
||||||
position=(gui_x, gui_y + 2 * ib_height + 3 * bt_height + 50),
|
position=(gui_x, gui_y + 2 * ib_height + 2 * bt_height - 10),
|
||||||
dimensions=(gui_width, bt_height),
|
dimensions=(gui_width, bt_height),
|
||||||
text="ok",
|
text="ok",
|
||||||
box_color=(100, 200, 100),
|
box_color=(100, 200, 100),
|
||||||
@ -322,11 +332,37 @@ class Game:
|
|||||||
outline_additional_pixel=True
|
outline_additional_pixel=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.button_genetic_algorithm = Button(
|
||||||
|
position=(gui_x, gui_y + 2 * ib_height + 4 * bt_height + 10),
|
||||||
|
dimensions=(gui_width, bt_height),
|
||||||
|
text="genetic",
|
||||||
|
box_color=(100, 200, 100),
|
||||||
|
outline_color=(80, 180, 80),
|
||||||
|
outline_additional_pixel=True
|
||||||
|
)
|
||||||
|
|
||||||
|
self.input_box_generations = InputBox(
|
||||||
|
position=(gui_x, gui_y + 2 * ib_height + 5 * bt_height + 20),
|
||||||
|
dimensions=(gui_width, ib_height),
|
||||||
|
text="iters",
|
||||||
|
user_input="10",
|
||||||
|
box_color=(100, 200, 100),
|
||||||
|
bottom_strip_color=(120, 220, 120),
|
||||||
|
inner_box_color=(120, 220, 120),
|
||||||
|
outline_color=(80, 180, 80),
|
||||||
|
outline_additional_pixel=True,
|
||||||
|
valid_input_characters="1234567890",
|
||||||
|
input_centered=True,
|
||||||
|
clear_input_on_click=True
|
||||||
|
)
|
||||||
|
|
||||||
gui_list = [
|
gui_list = [
|
||||||
|
self.textbox_points,
|
||||||
self.input_box_row,
|
self.input_box_row,
|
||||||
self.input_box_column,
|
self.input_box_column,
|
||||||
self.button_auto,
|
self.button_auto,
|
||||||
self.button_genetic_algorithm,
|
self.button_genetic_algorithm,
|
||||||
|
self.input_box_generations,
|
||||||
self.button_random,
|
self.button_random,
|
||||||
self.button_ok
|
self.button_ok
|
||||||
]
|
]
|
||||||
@ -372,3 +408,6 @@ class Game:
|
|||||||
# heading either left or right
|
# heading either left or right
|
||||||
else:
|
else:
|
||||||
return self.agent.row, max(0, min(9, self.agent.column - self.agent.direction.value + 2))
|
return self.agent.row, max(0, min(9, self.agent.column - self.agent.direction.value + 2))
|
||||||
|
|
||||||
|
def get_input_generations(self):
|
||||||
|
return self.input_box_generations.get_input()
|
||||||
|
22
main.py
22
main.py
@ -1,5 +1,7 @@
|
|||||||
# libraries
|
# libraries
|
||||||
|
import threading
|
||||||
import pygame
|
import pygame
|
||||||
|
|
||||||
from pyglet.gl import * # for blocky textures
|
from pyglet.gl import * # for blocky textures
|
||||||
# other files of this project
|
# other files of this project
|
||||||
from game import Game
|
from game import Game
|
||||||
@ -27,6 +29,7 @@ def main():
|
|||||||
auto = False
|
auto = False
|
||||||
genetics = False
|
genetics = False
|
||||||
genetics_ready = False
|
genetics_ready = False
|
||||||
|
genetics_in_progress = False
|
||||||
is_game_over = False
|
is_game_over = False
|
||||||
|
|
||||||
# create and initialize_gui_components game instance
|
# create and initialize_gui_components game instance
|
||||||
@ -66,7 +69,19 @@ def main():
|
|||||||
# if genetics button is clocked then start genetic algorithm
|
# if genetics button is clocked then start genetic algorithm
|
||||||
genetics = game.button_genetic_algorithm.is_clicked(pygame.mouse.get_pos(), events)
|
genetics = game.button_genetic_algorithm.is_clicked(pygame.mouse.get_pos(), events)
|
||||||
|
|
||||||
if genetics:
|
if genetics and not (genetics_in_progress or genetics_ready):
|
||||||
|
generations = game.get_input_generations()
|
||||||
|
|
||||||
|
genetics_in_progress = True
|
||||||
|
game.genetics_done = False
|
||||||
|
threading.Thread(target=game.run_genetics, args=(int(generations), )).start()
|
||||||
|
|
||||||
|
if genetics_in_progress:
|
||||||
|
genetics = True
|
||||||
|
genetics_ready = game.genetics_done
|
||||||
|
|
||||||
|
if genetics and genetics_ready:
|
||||||
|
genetics_in_progress = False
|
||||||
auto = True
|
auto = True
|
||||||
|
|
||||||
# ========================== #
|
# ========================== #
|
||||||
@ -76,10 +91,6 @@ def main():
|
|||||||
# initializing action_sequence variable
|
# initializing action_sequence variable
|
||||||
action_sequence = None
|
action_sequence = None
|
||||||
|
|
||||||
if genetics and not genetics_ready:
|
|
||||||
game.run_genetics()
|
|
||||||
genetics_ready = True
|
|
||||||
|
|
||||||
# getting action sequence for agent
|
# getting action sequence for agent
|
||||||
if auto and running:
|
if auto and running:
|
||||||
in_menu = False
|
in_menu = False
|
||||||
@ -157,6 +168,7 @@ def main():
|
|||||||
if auto:
|
if auto:
|
||||||
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))
|
||||||
# is_game_over = True
|
# is_game_over = True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -8,6 +8,7 @@ class Minefield:
|
|||||||
def __init__(self, json_path):
|
def __init__(self, json_path):
|
||||||
self.turn = 0
|
self.turn = 0
|
||||||
self.explosions = 0
|
self.explosions = 0
|
||||||
|
self.points = 0
|
||||||
|
|
||||||
self.agent = ag.Agent(json_path)
|
self.agent = ag.Agent(json_path)
|
||||||
self.json_path = json_path
|
self.json_path = json_path
|
||||||
@ -38,6 +39,7 @@ class Minefield:
|
|||||||
|
|
||||||
def next_turn(self):
|
def next_turn(self):
|
||||||
self.turn += 1
|
self.turn += 1
|
||||||
|
self.points += 1
|
||||||
|
|
||||||
for row in range(const.V_GRID_VER_TILES):
|
for row in range(const.V_GRID_VER_TILES):
|
||||||
for column in range(const.V_GRID_VER_TILES):
|
for column in range(const.V_GRID_VER_TILES):
|
||||||
@ -49,6 +51,7 @@ class Minefield:
|
|||||||
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
|
||||||
mine.active = False
|
mine.active = False
|
||||||
|
|
||||||
def get_active_mines(self):
|
def get_active_mines(self):
|
||||||
|
@ -21,6 +21,8 @@ from ui.input_box import InputBox
|
|||||||
V_NAME_OF_WINDOW = "MineFusion TM"
|
V_NAME_OF_WINDOW = "MineFusion TM"
|
||||||
V_FPS = 60
|
V_FPS = 60
|
||||||
|
|
||||||
|
EXPLOSION_PENALTY = 200
|
||||||
|
|
||||||
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
DIR_ASSETS = os.path.join(ROOT_DIR, "resources", "assets")
|
DIR_ASSETS = os.path.join(ROOT_DIR, "resources", "assets")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user