From 5e10b4c4617069c8e68f6f58f1caec9a55c669b7 Mon Sep 17 00:00:00 2001 From: ClemW <clement.wang@student-cs.fr> Date: Mon, 7 Jun 2021 00:53:30 +0200 Subject: [PATCH] push MVP --- __pycache__/gen_algo.cpython-38.pyc | Bin 0 -> 9332 bytes __pycache__/graph.cpython-38.pyc | Bin 0 -> 1490 bytes gen_algo.py | 325 ++++++++++++++++++++++++++++ graph.py | 59 +++++ main.py | 80 +++++++ main_seq.py | 106 +++++++++ 6 files changed, 570 insertions(+) create mode 100644 __pycache__/gen_algo.cpython-38.pyc create mode 100644 __pycache__/graph.cpython-38.pyc create mode 100644 gen_algo.py create mode 100644 graph.py create mode 100644 main.py create mode 100644 main_seq.py diff --git a/__pycache__/gen_algo.cpython-38.pyc b/__pycache__/gen_algo.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..405cda6c515797b0d78076361d961454e2fab437 GIT binary patch literal 9332 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 Ra4>Q(bFc{1Gw=z70|42_*&YA@ literal 0 HcmV?d00001 diff --git a/__pycache__/graph.cpython-38.pyc b/__pycache__/graph.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a852878ebe6729f05b9e42ed9f5e00e27a15367 GIT binary patch literal 1490 zcmWIL<>g{vU|=W++?$Za&cN^(#6iZ)3=9ko3=9m#W(*7rDGVu$ISf%Cnkk1dgdv4F zg{6fdiYbLPg{_4liaCWng`<TbiY0|Rg)@b#g)xdXg)x{xljkMKL_bZ&TZ~%CEFd-% zvoSC*fUI{0S&+uSz)-?a!<faG!YIkm%;>@p>ru-B6=8yknAEb?FxIftu%<CdGNdqN zGZiV6Fl8|>V5woKVXk4VVG?1eVF8m&HB4zN3z-=i3Ka_ZN|>@(7qBg4U}Q*P2xeHx z>{rCdz`*blM66`L#afh@m!5iywKyZOAoUhoL1lVkZt5+Tl%mA)TP#JX$tAbgQ!<MS zauO?Vv6U92B$lLVvfW}U&&*57FTcf-n^={4i@Uh6G_fc(zBscg^%fUMC_XtqC%@<x zW9BW!td$H!!VC-yzk-sjVnT~ki;82Cb5e6tV_Z^;vrF;|VhkN)5=%?+a}!Gv^Gae0 zit@8kONwK15|b*6GV@9@Qe)Cn^HNJPlXc-jG3iB#1sQq;mA5!S&Ws1yC;$pYAyCjV z2{8&W3NW%UGBGkS3jO6^EfQs5V4!l)Gm{haMWFOl#LB?HpvhLm%fP@;#0Mhy85kI% z1d|ezv*Xi?@=NnlAfZ<z2$BSO2^@Y9LWF^V;TBhNPHJKi*xRyjZ!=<eTMXoFkZqt) z0bzCq1_n+B1_p473u0gZr8kBerWD2$re3BLW=V!x)*4n9hFG0iwi3oHrUlG3p!CF+ z!Xn8~!z{v3!<NDdWii(<L(`X731b$^0#;b+(q!`s(PX^ER+O5YUzGBa3FHM(q6a5A zO}1MspoCP!0TKqeqzIG&inu{s9#F8bgM;N33pfq36({Bv<fImX!six$aYlZ5d_iJK z1|)QDvE-)a=HFs2$S;P33@D8ifrEz=u2d5gI-r!nz$nDX0ZkH8f7w`z<UwHrau_IF zKp4aZg%vn#V!*jEg|V5Dk)ebkg{g+2nTe61hB<{Ho3luzgdv5wh8ZMM!jQrO&a<qN z3|UN25w;R$xEM<fQx<D8Oa^QoC=Y6~`&H@ak^rj2lT!1NGjbD)vK8{v6hJwmD6u3n zKd*|bBr`WvL8Dkx!K#WUDYdvnp*T6eC{@7<9O6|XzKMAmi6te8c?v0+#U+V($*Brf znrydN3qY9*6nq7@Siw4OaporGWyFJ2-Qr2jONlSYFU|y6af>CjBC|x3<rZ^#YThlD z#GLf}A}LTZWC2-?9{3#Kz)w%jGXaG?D19?9axij%u@IvaV-YA$G#ULg*}$pb7Dsw& wUOd=LP%bS3rCD&~fYl)xj)-0ko80`A(wtN~Pytp9N<bWp9DE$S96TJ{01gmMRsaA1 literal 0 HcmV?d00001 diff --git a/gen_algo.py b/gen_algo.py new file mode 100644 index 0000000..5aba5f0 --- /dev/null +++ b/gen_algo.py @@ -0,0 +1,325 @@ +import numpy as np +import random + + +def manh_dist(pos1, pos2): + return abs(pos1[0] - pos2[0]) + abs(pos1[1] - pos2[1]) + + +class Game(object): + # nothing : 0 + # wall : 1 + # entrance : 2 + # exit : 3 + + def __init__(self, shape=(3, 7)): + + self.dist_factor = 5 + self.exploration_factor = 6 + + self.wall_penality = 2 + self.entrance_penality = 50 + self.exit_reward = 100 + + self.entrance = (1 + 2 * random.randint(0, shape[0] - 1), 0) + self.exit = (1 + 2 * random.randint(0, shape[0] - 1), shape[1] * 2) + + self.maze = np.zeros((shape[0] * 2 + 1, shape[1] * 2 + 1)) + + # recursive generation with queue + + # delimitations : + + self.maze[0] = 1 + self.maze[-1] = 1 + self.maze[:, 0] = 1 + self.maze[:, -1] = 1 + + # generation : + + def recursive_maze(maze): + if maze.shape[0] <= 1 and maze.shape[1] <= 1: + return [] + + if maze.shape[0] < maze.shape[1]: + verti = ( + 2 * random.randint(0, maze.shape[1] // 2 - 1) + 1 + ) # odd between 0 and shape[1]-1 + hori = 2 * random.randint(0, maze.shape[0] // 2) + + maze[:, verti] = 1 + maze[hori, verti] = 0 + + return [maze[:, :verti], maze[:, verti + 1 :]] + else: + hori = 2 * random.randint(0, maze.shape[0] // 2 - 1) + 1 + verti = 2 * random.randint(0, maze.shape[1] // 2) + maze[hori] = 1 + maze[hori, verti] = 0 + return [maze[:hori, :], maze[hori + 1 :, :]] + + queue = [self.maze[1:-1, 1:-1]] + while len(queue) != 0: + sub = queue.pop(0) + temp = recursive_maze(sub) + for i in temp: + queue.append(i) + + self.maze[self.entrance] = 2 + self.maze[self.exit] = 3 + + def play(self, player, record=False): + """ + argument : + - player, a Sample class object + return : + - list with score and end position + """ + position = self.entrance + score = 0 + + memo = [position] + for i in player.genome: + + if i == 0: + temp = (position[0], position[1] + 1) + if i == 1: + temp = (position[0] + 1, position[1]) + if i == 2: + temp = (position[0], position[1] - 1) + if i == 3: + temp = (position[0] - 1, position[1]) + if i == 4: + temp = (position[0], position[1]) + + if temp[1] >= 0 and temp[1] < self.maze.shape[1]: + # this can occur at the entrance and at the exit + if self.maze[temp] == 1: + score -= self.wall_penality + if self.maze[temp] == 0: + position = temp + if self.maze[temp] == 2: + position = temp + score -= self.entrance_penality + if self.maze[temp] == 3: + # this makes it go for exit faster and staying there + score += self.exit_reward + position = temp + + memo.append(position) + else: + score -= self.wall_penality + + ### push the exploration : + score += self.exploration_factor * len(set(memo)) + + ### push the way toward the exit : + score -= self.dist_factor * manh_dist(position, self.exit) + if record: + return score, position, memo + return score, position + + @property + def entrance(self): + return self._entrance + + @entrance.setter + def entrance(self, new): + self._entrance = new + + @property + def exit(self): + return self._exit + + @exit.setter + def exit(self, new): + self._exit = new + + @property + def maze(self): + return self._maze + + @maze.setter + def maze(self, new): + self._maze = new + + def print_maze(self): + print(self.maze) + + +class Sample(object): + """ + genome : list of integers between 0 and 4 + 0 : up + 1 : right + 2 : down + 3 : left + 4 : stay still -> this is usefull to lessen the number + of moves without changing the length of the list + + Some tweaks were done to adapt to the particular genome + """ + + def __init__( + self, creation="random", max_length=30, genome=[], parent1=None, parent2=None, + ): + """ + creation = "random" : random, be carefull to set the length + "cross over" : do a cross over between 2 samples + be carefull to set parent1 and parent2 + """ + self.score = None + self.end_position = None + if creation == "random": + self.genome = [random.randint(0, 4) for l in range(max_length)] + elif creation == "genome": + self.genome = genome + elif creation == "cross over": + if parent1 is None or parent2 is None: + raise NameError("Parents are not defined") + # we want to keep the beginning of the best parent + if parent1.score > parent2.score: + begin = parent1 + end = parent2 + else: + begin = parent2 + end = parent1 + cross_point = random.randint(0, len(parent1.genome)) + self.genome = begin.genome[:cross_point] + end.genome[cross_point:] + + else: + raise NameError("Mode of creation not defined") + + def mutate(self): + """ + That mutation tries to favour straight lines to get out of local minimum + """ + x1 = random.randint(0, len(self.genome)) + x2 = random.randint(0, (len(self.genome) - x1)) + + for k in range(x1, x1 + x2): + temp = random.randint(0, 6) + if temp < 5: + self.genome[k] = temp + else: + if k > 0: + self.genome[k] = self.genome[k - 1] + + @property + def genome(self): + return self._genome + + @genome.setter + def genome(self, new): + self._genome = new + + @property + def score(self): + if self._score is None: + raise NameError("Score is None") + return self._score + + @score.setter + def score(self, new): + self._score = new + + @property + def end_position(self): + if self._end_position is None: + raise NameError("End position is None") + return self._end_position + + @end_position.setter + def end_position(self, new): + self._end_position = new + + +class GA(object): + """ + Attributes : + pop_card : cardinal of population + pop : list of people which compose the population + elite_card : cardinal of the elite + the elite corresponds to pop[:elite_card] + death_card : cardinal of the deaths in each generation + they correspond to pop[mortality_card:] + mutation_rate + max_length + + This class tries to be as general as it can to solve a game + The only thing you have to change in it is the maxlength parameter + it is only used during the initialisation + + """ + + def __init__( + self, + game, + genome_length=None, + pop_card=5000, + elite=0.01, + mortality=0.4, + mutation_rate=0.2, + ): + self.game = game + self.mutation_rate = mutation_rate + self.pop_card = pop_card + self.elite_card = int(elite * pop_card) + self.death_card = int(mortality * pop_card) + self.pop = [ + Sample(creation="random", max_length=genome_length) for _ in range(pop_card) + ] + for sample in self.pop: + temp = game.play(sample) + sample.score = temp[0] + sample.end_position = temp[1] + self.pop.sort(key=lambda sample: sample.score, reverse=True) + + def cross_over_step(self, number): + """ + self.pop[number] is replaced by a new sample obtained by a cross over + between 2 random samples + """ + parent1 = random.randint(0, self.pop_card - 1) + + parent2 = random.randint(0, self.pop_card - 1) + self.pop[number] = Sample( + creation="cross over", parent1=self.pop[parent1], parent2=self.pop[parent2], + ) + temp = self.game.play(self.pop[number]) + self.pop[number].score = temp[0] + self.pop[number].end_position = temp[1] + + def cross_over(self): + """ + the elite won't be changed at all + the worst pop will get replaced by offspring + we allow new offspring to reproduce at the moment they are created + in order not to complexify too much the implementation + to do this, we have to calculate the score at the same time because + it is useful to do the cross over + + """ + for k in range(self.pop_card - self.death_card, self.pop_card): + self.cross_over_step(k) + + def mutation_step(self, number): + """ + mutation of self.pop[number] + """ + if random.random() < self.mutation_rate: + self.pop[number].mutate() + temp = self.game.play(self.pop[number]) + self.pop[number].score = temp[0] + self.pop[number].end_position = temp[1] + + def mutation(self): + """we will mutate all the population that is not the elite""" + for k in range(self.elite_card, self.pop_card): + self.mutation_step(k) + + def do_gen(self): + self.cross_over() + self.mutation() + self.pop.sort(key=lambda sample: sample.score, reverse=True) + diff --git a/graph.py b/graph.py new file mode 100644 index 0000000..fecfbc3 --- /dev/null +++ b/graph.py @@ -0,0 +1,59 @@ +import pygame +from gen_algo import * + + +########### What to show each generation + +### four ways to show : +# 1) only the final position of all the elite +# 2) the path taken by the best one with arrows to make it clear +# 3) animation of the path taken by the best one +# 4) leaderboard with 10 elites on another window + +# I implemented #2 + + +def draw_maze(window, maze, square_size, maze_color): + + for i in range(maze.shape[0]): + for j in range(maze.shape[1]): + if maze[i, j] == 1: + pygame.draw.rect( + window, + maze_color, + (j * square_size, i * square_size, square_size, square_size), + ) + pygame.display.update() + + +def clear_maze(window, maze, square_size, back_ground_color): + for i in range(maze.shape[0]): + for j in range(maze.shape[1]): + if maze[i, j] != 1: + pygame.draw.rect( + window, + back_ground_color, + (j * square_size, i * square_size, square_size, square_size), + ) + pygame.display.update() + + +def show_path(window, game, sample, square_size, show_path_color): + memo = game.play(sample, record=True)[2] + for pos in memo: + pygame.draw.rect( + window, + show_path_color, + (pos[1] * square_size, pos[0] * square_size, square_size, square_size), + ) + pygame.display.update() + + +def show_gen(gen, algo, game, time=None): + print("--------------------------------------------") + print("benchmark of generation", gen) + if time is not None: + print("time (s) :", time) + print("best score :", algo.pop[0].score) + print("Manhattan distance :", manh_dist(algo.pop[0].end_position, game.exit)) + print("--------------------------------------------") diff --git a/main.py b/main.py new file mode 100644 index 0000000..5aef399 --- /dev/null +++ b/main.py @@ -0,0 +1,80 @@ +import pygame +from pygame.locals import * +from gen_algo import * +from graph import * +import time + +###### Parameters + +#### Graphism +window_length = 640 +window_width = 480 +maze_color = (255, 0, 0) +backgroud_color = (255, 255, 255) +entrance_color = (0, 255, 0) +exit_color = (0, 0, 255) + +show_path_color = (160, 225, 55) + +#### Back +shape = (10, 10) +number_of_generations = 1000 +pop_card = 3000 +elite = 0.01 +mortality = 0.4 +mutation_rate = 0.4 +max_moves = 100 + + +################### Inititialisation of pygame +pygame.init() + + +# Init game +t0 = time.time() + +game = Game(shape) +algo = GA( + game, + genome_length=max_moves, + pop_card=pop_card, + elite=elite, + mortality=mortality, + mutation_rate=mutation_rate, +) +square_size = min( + window_length // game.maze.shape[1], window_width // game.maze.shape[0] +) + +window = pygame.display.set_mode( + (square_size * game.maze.shape[1], square_size * game.maze.shape[0]) +) + +window.fill(backgroud_color) + + +draw_maze(window, game.maze, square_size, maze_color) +game.print_maze() + + +print("Initialisation took ", time.time() - t0) +t0 = time.time() + + +show_gen(0, algo, game) +show_path(window, game, algo.pop[0], square_size, show_path_color) + +for gen in range(1, number_of_generations): + print("starting generation", gen) + algo.do_gen() + clear_maze(window, game.maze, square_size, backgroud_color) + show_path(window, game, algo.pop[0], square_size, show_path_color) + show_gen(gen, algo, game, time=time.time() - t0) + t0 = time.time() + + +flag = 1 +while flag: + for event in pygame.event.pump(): + if event.type == QUIT: + flag = 0 diff --git a/main_seq.py b/main_seq.py new file mode 100644 index 0000000..ad07dc4 --- /dev/null +++ b/main_seq.py @@ -0,0 +1,106 @@ +import pygame +from pygame.locals import * +from gen_algo import * +from graph import * +import time + +###### Parameters + +#### Graphism +window_length = 640 +window_width = 480 +maze_color = (255, 0, 0) +backgroud_color = (255, 255, 255) +entrance_color = (0, 255, 0) +exit_color = (0, 0, 255) + +show_path_color = (160, 225, 55) + +#### Back +shape = (10, 20) +number_of_generations = 1000 +pop_card = 3000 +elite = 0.01 +mortality = 0.4 +mutation_rate = 0.4 +max_moves = 300 + + +################### Inititialisation of pygame +pygame.init() + + +# Init game +t0 = time.time() + +game = Game(shape) +algo = GA( + game, + genome_length=max_moves, + pop_card=pop_card, + elite=elite, + mortality=mortality, + mutation_rate=mutation_rate, +) +square_size = min( + window_length // game.maze.shape[1], window_width // game.maze.shape[0] +) + +window = pygame.display.set_mode( + (square_size * game.maze.shape[1], square_size * game.maze.shape[0]) +) + +window.fill(backgroud_color) + + +draw_maze(window, game.maze, square_size, maze_color) +game.print_maze() + +print("Initialisation took ", time.time() - t0) +t0 = time.time() +show_gen(0, algo, game) +show_path(window, game, algo.pop[0], square_size, show_path_color) + +flag = 1 +state = ( + "cross_over" # this variable is either equal to "cross_over", "mutation" or "sort" +) +count_co = algo.pop_card - algo.death_card +count_mutate = algo.elite_card +gen = 1 + + +while flag: + for event in pygame.event.get(): + if event.type == QUIT: + flag = 0 + + if state == "cross_over": + if count_co == 0: + print("starting generation", gen) + if count_co == algo.pop_card: + print("Cross over done") + count_co = algo.pop_card - algo.death_card + state = "mutation" + else: + algo.cross_over_step(count_co) + count_co += 1 + + elif state == "mutation": + if count_mutate == algo.pop_card: + print("Mutation done") + count_mutate = algo.elite_card + state = "sort" + else: + algo.mutation_step(count_mutate) + count_mutate += 1 + + elif state == "sort": + state = "cross_over" + algo.pop.sort(key=lambda sample: sample.score, reverse=True) + gen += 1 + clear_maze(window, game.maze, square_size, backgroud_color) + show_path(window, game, algo.pop[0], square_size, show_path_color) + show_gen(gen, algo, game, time=time.time() - t0) + t0 = time.time() + -- GitLab