diff --git a/main.py b/main.py index f907777..cd0d288 100644 --- a/main.py +++ b/main.py @@ -5,9 +5,11 @@ from src.agents.heuristic import ( utility ) + from src.agents.heuristic_trainer import train from src.agents.geneticAlgAgentJon import GeneticAlgAgentJM + def test(): # algAgent = GeneticAlgAgentJM() # algAgent.number_of_selection(2) @@ -35,6 +37,7 @@ def test(): # print(utility(boards, 0, -1, 0, 0, 0)) # boards.printBoard() +<<<<<<< HEAD # board = Tetris() # manager = TetrisGameManager(board) # agent = create_agent("heuristic") @@ -57,3 +60,11 @@ def test(): #train() >>>>>>> c14418b (feat: :rocket: genetic agent class and it's training algorithm commenced) +======= + board = Tetris() + # manager = TetrisGameManager(board) + # agent = create_agent("heuristic") + agents = train_genetic_algorithm(10) + + # manager.startDemo(agent) +>>>>>>> fa9eeb9 (Co-authored-by: HÃ¥vard Fossdal ) diff --git a/src/agents/geneticAlgAgent.py b/src/agents/geneticAlgAgent.py index 5270b48..39953bf 100644 --- a/src/agents/geneticAlgAgent.py +++ b/src/agents/geneticAlgAgent.py @@ -6,18 +6,20 @@ # TODO Create a fitness function # TODO Create a genetic algorithm based on the import random +import numpy as np +import operator from math import sqrt from src.agents.agent import Agent, play_game from src.game.tetris import Action, Tetris, transition_model, get_all_actions from src.agents.heuristic import utility -class geneticAgent(Agent): +class GeneticAgent(Agent): NUMBER_OF_GAMES = 10 def __init__(self): - self.weight_vector = [random.uniform(-2.00, 2.00) for _ in range(4)] + self.weight_vector = [random.uniform(-2.00, 2.00) for _ in range(5)] def result(self, board: Tetris) -> list[Action]: possible_boards = board.getPossibleBoards() @@ -52,33 +54,74 @@ def _fitness(self, board: Tetris) -> float: return fitness def _normalize_weights(self): - self.weight_vector = sqrt(sum([v**2 for v in self.get_weight_vector()])) + ## TODO: Fix this function + + self.weight_vector = [x/sqrt(sum([i**2 for i in self.weight_vector])) for x in self.weight_vector] + + + def mutate_child(self): + for i in range(5): + if random.random() < 0.05: + self.get_weight_vector()[i] += random.uniform(-0.20, 0.20) - def _crossover(self, parent1: geneticAgent, parent2: geneticAgen) -> geneticAgent: - for i in range(len(parent1)): - self.weight_vector[i] = ((parent1[i]*parent1.get_weight_vector()[i] + parent2[i]*parent2.get_weight_vector()[i])) + def _crossover(self, parent1: tuple[float, 'GeneticAgent'], parent2: + tuple[float, 'GeneticAgent'] ) -> None: + for i in range(len(self.get_weight_vector())): + self.weight_vector[i] = ((parent1[0]*parent1[1].get_weight_vector()[i] + parent2[0]*parent2[1].get_weight_vector()[i])) + + + +def average_weight_values(agents: list[float, GeneticAgent]) -> float: + sum_of_weights = 0 + for agent in agents: + sum_of_weights += sum(agent[1].get_weight_vector())/len(agent[1].get_weight_vector()) + return sum_of_weights/len(agents) + -def train_genetic_algorithm(init_population_size: int) -> list[tuple[float, geneticAgent]]: +def train_genetic_algorithm(init_population_size: int, tol = 1e-6) -> list[tuple[float, GeneticAgent]]: candidates = [] # List of genetic agents on the form (fitness, agent) - - for _ in range(init_population_size): - candidate = geneticAgent() + candidate_fitness = np.array([]) + + print("Starting genetic algorithm") + for i in range(init_population_size): + print("Creating candidate ", i) + candidate = GeneticAgent() board = Tetris() fitness = candidate._fitness(board) candidates.append((fitness, candidate)) # Sort the candidates based on their fitness + print("Initial population done") child_candidates = [] - - while len(child_candidates) < 0.3*init_population_size: - random_indices = select_random_parents(init_population_size) - parent_candidates = [] - for i in random_indices: - parent_candidates.append(candidates[i]) - parent_candidates.sort(reverse=True) - child_candidates.append(make_offspring(board, parent_candidates[0], parent_candidates[1])) + tolerance = average_weight_values(candidates) + iterations = 0 + print("Starting iterations") + while abs(tolerance) > tol: + iterations += 1 + print("Tolerance: ", tolerance) + print("Starting new generation") + while len(child_candidates) < 0.3*init_population_size: + random_indices = select_random_parents(init_population_size) + parent_candidates = [] + for i in random_indices: + parent_candidates.append((candidates[i][0], candidates[i][1])) + parent_candidates = sorted(parent_candidates, key=operator.itemgetter(0), reverse=True) + print(len(parent_candidates)) + child_tuple = make_offspring(board, parent_candidates[0], parent_candidates[1]) + child_candidates.append((child_tuple)) + tolerance = average_weight_values(candidates) + candidates = sorted(candidates, key=operator.itemgetter(0), reverse=True) + candiates = candiates[:init_population_size*0.7+1] + for child in child_candidates: + candidates.append(child) + tolerance -= average_weight_values(candidates) + print("Generation done") + print("-------------------") + print(iterations, " iterations done") + candidates = sorted(candidates, key=operator.itemgetter(0), reverse=True) + print("Best candidates weights: [", candidate[0].get_weight_vector()[0], ", ", candidate[0].get_weight_vector()[1], ", ", candidate[0].get_weight_vector()[2], ", ", candidate[0].get_weight_vector()[3], "]") return candidates @@ -92,7 +135,7 @@ def select_random_parents(init_population_size: int) -> list[int]: list of indices of unique selected agents. """ random_selection = [] - while len(random_selection) < (init_population_size/10): + while len(random_selection) < max(2, init_population_size/10): random_index = random.randint(0, init_population_size - 1) if random_index not in random_selection: random_selection.append(random_index) @@ -100,11 +143,17 @@ def select_random_parents(init_population_size: int) -> list[int]: -def make_offspring(board: Tetris, parent1: tuple[float, geneticAgent], parent2: tuple[float, geneticAgent]) -> tuple[float, geneticAgent]: - child = geneticAgent() +def make_offspring(board: Tetris, parent1: tuple[float, GeneticAgent], parent2: tuple[float, GeneticAgent]) -> tuple[float, GeneticAgent]: + child = GeneticAgent() child.weight_vector = child._crossover(parent1, parent2) + child.mutate_child() + child._normalize_weights() child_fitness = child._fitness(board) return (child_fitness, child) - +# def mutate_child(child: geneticAgent) -> geneticAgent: +# for i in range(len(child.get_weight_vector())): +# if random.random() < 0.05: +# child.get_weight_vector()[i] += random.uniform(-0.20, 0.20) +# return child diff --git a/src/agents/heuristic_agent.py b/src/agents/heuristic_agent.py index 2778f0d..b422c44 100644 --- a/src/agents/heuristic_agent.py +++ b/src/agents/heuristic_agent.py @@ -24,16 +24,5 @@ def result(self, board: Tetris) -> list[Action]: # Find the actions needed to transform the current board to the new board actions = [] -<<<<<<< HEAD actions = transition_model(board, best_board) - return actions - -======= - try: - actions = transition_model(board, best_board) - return actions - except: - return actions - - ->>>>>>> c14418b (feat: :rocket: genetic agent class and it's training algorithm commenced) + return actions \ No newline at end of file