diff --git a/__pycache__/gen_algo.cpython-38.pyc b/__pycache__/gen_algo.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..405cda6c515797b0d78076361d961454e2fab437
Binary files /dev/null and b/__pycache__/gen_algo.cpython-38.pyc differ
diff --git a/__pycache__/graph.cpython-38.pyc b/__pycache__/graph.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2a852878ebe6729f05b9e42ed9f5e00e27a15367
Binary files /dev/null and b/__pycache__/graph.cpython-38.pyc differ
diff --git a/gen_algo.py b/gen_algo.py
new file mode 100644
index 0000000000000000000000000000000000000000..5aba5f06ad5ca2fc675cec194302d9bb0d9a09ef
--- /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 0000000000000000000000000000000000000000..fecfbc35cd3a98c8a5b2e5f4d46f242035108631
--- /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 0000000000000000000000000000000000000000..5aef399df4166997ec6b3c719b5665f1c1a1f280
--- /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 0000000000000000000000000000000000000000..ad07dc4468a81d64c7f2bb84e7eaab3b63081fc2
--- /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()
+