diff --git a/Tp5/minesweeper.zip b/Tp5/minesweeper.zip
new file mode 100644
index 0000000000000000000000000000000000000000..687e5f0b46b39c599e0898f6bbf214e5d9e55113
Binary files /dev/null and b/Tp5/minesweeper.zip differ
diff --git a/Tp5/minesweeper/apl1test.py b/Tp5/minesweeper/apl1test.py
new file mode 100644
index 0000000000000000000000000000000000000000..8533ccaca5d99e7cfb83d6d86aa9334bb6a73a40
--- /dev/null
+++ b/Tp5/minesweeper/apl1test.py
@@ -0,0 +1,89 @@
+import thonnycontrib
+from thonnycontrib.backend.evaluator import Evaluator
+import thonnycontrib.backend.l1test_backend
+from thonny.plugins.cpython_backend.cp_back import MainCPythonBackend
+import thonnycontrib.backend.doctest_parser 
+from thonnycontrib.backend.doctest_parser import ExampleWithExpected, ExampleWithoutExpected
+import thonnycontrib.backend.ast_parser
+from thonnycontrib.backend.ast_parser import L1DocTest
+import thonnycontrib.backend.verdicts
+from thonnycontrib.backend.verdicts.ExceptionVerdict import ExceptionVerdict 
+
+import inspect
+import tempfile
+import os
+import sys
+        
+class MockBackend(MainCPythonBackend):
+    """
+    Fake backend.
+    """
+    def __init__(self):
+        ...
+
+    def send_message(self, msg) -> None:
+        ...
+
+# register backend
+thonnycontrib.backend.l1test_backend.BACKEND = MockBackend()
+
+def l1test_to_org(filename: str, source: str=""):
+    """
+    Return an org abstract of the tests presents in `filename` file.
+    """
+    abstract = {'total': 0,
+                'success': 0,
+                'failures': 0,
+                'errors': 0,
+                'empty': 0}
+
+    if source == "":
+        with open(filename, 'rt') as fin:
+            source = fin.read()
+    evaluator = Evaluator(filename=filename,
+                          source=source)
+    tests = evaluator.evaluate()
+    n = len(tests)
+    abstract['total'] = n
+    res = ""
+    for test in tests:
+        examples = test.get_examples()
+        res_examples = ""
+        nb_test, nb_test_ok = 0, 0
+        empty = True
+        for example in examples:
+            verdict = test.get_verdict_from_example(example)
+            if isinstance(example, ExampleWithExpected):
+                nb_test += 1
+                if verdict.isSuccess():
+                    nb_test_ok += 1
+                    abstract['success'] += 1
+                else:
+                    abstract['failures'] += 1
+                empty = False
+            if isinstance(verdict, ExceptionVerdict):
+                abstract['errors'] += 1
+                empty = False
+            res_examples += f"** {verdict}\n\n"
+            if not verdict.isSuccess():
+                res_examples += f"   {verdict.get_details()}\n\n"
+        if not empty: 
+            res += f"* {test.get_name()} ~ {nb_test_ok}/{nb_test} réussis\n\n"
+        else:
+            abstract['empty'] += 1
+            res += f"* {test.get_name()}\n\n Aucun test trouvé !\n\n"
+        res += res_examples
+    res = f"Tests exécutés : {abstract['total']}\nSuccès: {abstract['success']}, \
+Echecs: {abstract['failures']}, Erreurs: {abstract['errors']}, \
+Vide: {abstract['empty']}\n\n" + res
+    return res
+
+
+def testmod(modulename: str):
+    """
+    mimic the doctest.testmod function
+    for `modulename` module
+    """
+    print(l1test_to_org(modulename))
+
+
diff --git a/Tp5/minesweeper/cell.py b/Tp5/minesweeper/cell.py
new file mode 100755
index 0000000000000000000000000000000000000000..d18269849a5efe260a8f1df7aebfbb5750a8bf73
--- /dev/null
+++ b/Tp5/minesweeper/cell.py
@@ -0,0 +1,125 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+"""
+:mod:`cell` module
+
+:author: 
+
+:date: 
+
+
+"""
+    
+class Cell:
+    
+    def __init__(self):
+        """
+        initialize a new hidden cell of a minesweeper's grid.
+        existence of a bomb, number of bombs in neighborhood
+        have to be stated later.
+        
+        precondition: none
+        Examples:
+
+        $$$ cel = Cell()
+        $$$ cel.is_bomb
+        False
+        $$$ cel.is_revealed
+        False
+        $$$ cel.nbombs_in_neighborhood
+        0
+        """
+        
+        
+        self.is_bomb=False
+        self.is_revealed=False
+        self.nbombs_in_neighborhood=0
+
+    def incr_number_of_bombs_in_neighborhood(self):
+        """
+        :return: None
+        :side effect: increment the number of bombs in neighborhood of self
+        precondition: none
+        Examples:
+
+        $$$ cel = Cell()
+        $$$ cel.nbombs_in_neighborhood
+        0
+        $$$ cel.incr_number_of_bombs_in_neighborhood()
+        $$$ cel.nbombs_in_neighborhood
+        1
+        """
+        
+        
+        self.nbombs_in_neighborhood+=1
+
+    def reveal(self):
+        """
+        modify reveal state of self
+        precondition: none
+        Examples:
+
+        $$$ cel = Cell()
+        $$$ cel.is_revealed
+        False
+        $$$ cel.reveal()
+        $$$ cel.is_revealed
+        True
+        """
+        
+        self.is_revealed=True
+
+    def set_bomb(self):
+        """
+        put a bomb in self 
+
+        precondition: none
+        Examples:
+
+        $$$ cel = Cell()
+        $$$ cel.is_bomb
+        False
+        $$$ cel.set_bomb()
+        $$$ cel.is_bomb
+        True
+        """
+        
+        self.is_bomb=True
+
+    def __str__(self):
+        """
+        :return: a string representation of self state
+        :rtype: str
+        precondition: none
+        Examples:
+        
+        $$$ cel = Cell()
+        $$$ str(cel) == ' '
+        True
+        $$$ cel.reveal()
+        $$$ str(cel) == '0'
+        True
+        $$$ cel.incr_number_of_bombs_in_neighborhood()
+        $$$ str(cel) == '1'
+        True
+        $$$ cel.set_bomb()
+        $$$ str(cel) == 'B'
+        True
+        """
+        
+        
+        
+        if self.is_revealed:
+            if self.is_bomb:
+                return'B'
+            else:
+                return str(self.nbombs_in_neighborhood)
+        else:
+            
+            return' '
+           
+if (__name__ == '__main__'):
+    import apl1test
+    apl1test.testmod('cell.py')
+
diff --git a/Tp5/minesweeper/graphicalboard.py b/Tp5/minesweeper/graphicalboard.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ec294118a48acdd99b37dac365c852c6d55f1f0
--- /dev/null
+++ b/Tp5/minesweeper/graphicalboard.py
@@ -0,0 +1,174 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+"""
+:mod:`graphicalboard` module
+
+:author: `FIL - IEEA - Univ. Lille1.fr <http://portail.fil.univ-lille1.fr>`_
+
+:date:  2015, september, last revision: 2024, february
+
+This module implements some functions to draw a minesweeper game. The
+graphical board uses buttons to draw each cell and maps the left-click
+and right-click events to interact with the minesweeper.
+
+This module uses from :mod:`minesweeper`:
+
+* :method:`Minesweeper.reveal_all_cells_from`
+
+To draw and run a minesweeper game, one has to:
+
+* create a minesweeper game g
+* create a graphical board from the minesweeper g
+
+"""
+
+import os
+import tkinter as tk
+from functools import partial
+from cell import Cell
+from minesweeper import *
+
+# the list of icons
+img = []
+
+def create(game: Minesweeper):
+    """
+    This function creates the graphical board from a game. It also
+    launches the event loop. Thus, this is the only function to run to
+    have a functional graphical board.
+    """
+    global img
+    # create a new Tk window
+    win = tk.Tk()
+    # define the window title
+    win.title('Minesweeper ({:d} bombs)'.format(game.nbombs))
+    # load images
+    iconpath = os.path.join(os.path.dirname(os.path.abspath(__file__)), "icons")
+    img = [
+        tk.PhotoImage(file=os.path.join(iconpath, "0.gif")),
+        tk.PhotoImage(file=os.path.join(iconpath, "1.gif")),
+        tk.PhotoImage(file=os.path.join(iconpath, "2.gif")),
+        tk.PhotoImage(file=os.path.join(iconpath, "3.gif")),
+        tk.PhotoImage(file=os.path.join(iconpath, "4.gif")),
+        tk.PhotoImage(file=os.path.join(iconpath, "5.gif")),
+        tk.PhotoImage(file=os.path.join(iconpath, "6.gif")),
+        tk.PhotoImage(file=os.path.join(iconpath, "7.gif")),
+        tk.PhotoImage(file=os.path.join(iconpath, "8.gif")),
+        tk.PhotoImage(file=os.path.join(iconpath, "9.gif")),  # unrevealed
+        tk.PhotoImage(file=os.path.join(iconpath, "10.gif")), # bomb explosed
+        tk.PhotoImage(file=os.path.join(iconpath, "11.gif")), # bomb discovered
+        tk.PhotoImage(file=os.path.join(iconpath, "12.gif")), # flag
+        tk.PhotoImage(file=os.path.join(iconpath, "13.gif"))  # question
+    ]
+    # create the graphical board made of Tk buttons
+    width, height = game.width, game.height
+    board = []
+    for i in range(width):
+        board.insert(i, [])
+        for j in range(height):
+            button = tk.Button(win, padx=0, pady=0, width=19, height=19, image=img[9])
+            button.grid(column=i, row=j)
+            board[i].insert(j, button)
+            # bind the right-click event
+            button.bind("<Button-3>",
+                        partial(__changeflag, board=board, game=game, x=i, y=j))
+            # bind the left-click event
+            button.config(command=partial(__changestate, board, game, i, j))
+    # event loop
+    win.mainloop()
+
+def __test_end(board: list[list[tk.Button]], game: Minesweeper):
+    """
+    This function tests if the game is finished or not.  In the first
+    case, depending on the state of the game, all graphical cells are
+    disabled or events are unbinded.
+    """
+    state = game.state
+    if state == GameState.losing:
+        __disable_game(board, game)
+    elif state == GameState.winning:
+        __block_game(board, game)
+
+def __changestate(board: list[list[tk.Button]],
+                  game: Minesweeper,
+                  x: int, y: int):
+    """
+    This function is called on left-click on a button.
+
+    """
+    game.reveal_all_cells_from(x, y)
+    __redraw(board, game, x, y)
+    __test_end(board, game)
+
+def __changeflag(evt, board: list[list[tk.Button]],
+                 game: Minesweeper,
+                 x: int, y: int):
+    """
+    This function is called on right-click on a button.
+    """
+    cel = game.get_cell(x, y)
+    # if not cel.is_hypothetic():
+    #     cel.set_hypothetic()
+    # else:
+    #     cel.unset_hypothetic()
+    __redraw(board, game, x, y)
+    __test_end(board, game)
+
+
+def __block_game(board: list[list[tk.Button]],
+                 game: Minesweeper):
+    """
+    This function is called once the player wins. The chosen behavior
+    is to let the board as it and to unbind events.
+    """
+    width, height = (game.width, game.height)
+    for i in range(width):
+        for j in range(height):
+            button = board[i][j]
+            game.get_cell(i, j).reveal()
+            button.config(command="")
+            button.bind("<Button-3>", "")
+    __redraw(board, game, -1, -1)
+
+def __disable_game(board: list[list[tk.Button]],
+                   game: Minesweeper):
+    """
+    This function is called once the player looses. The chosen behavior
+    is to shade the board and to unbind events.
+    """
+    width, height = game.width, game.height
+    for i in range(width):
+        for j in range(height):
+            button = board[i][j]
+            button.config(state=tk.DISABLED)
+            button.bind("<Button-3>", "")
+
+
+def __redraw(board: list[list[tk.Button]],
+             game: Minesweeper,
+             x: int, y: int):
+    """
+    This function draws the board. Positions x and y are used to test
+    which bomb icon has to be drawn.
+    """
+    width, height = (game.width, game.height)
+    for i in range(width):
+        for j in range(height):
+            cel = game.get_cell(i, j)
+            button = board[i][j]
+            if cel.is_revealed:
+                if cel.is_bomb:
+                    new_img = img[10]
+                    if x == j and y == i:
+                        new_img = img[11]
+                else:
+                    new_img = img[cel.nbombs_in_neighborhood]
+                button.config(relief=tk.FLAT, image=new_img, command="")
+            else:
+                button.config(image=img[9])
+
+
+if __name__ == "__main__":
+    import apl1test
+    doctest.testmod('graphicalboard.py')
diff --git a/Tp5/minesweeper/icons/0.gif b/Tp5/minesweeper/icons/0.gif
new file mode 100644
index 0000000000000000000000000000000000000000..26d8a1024c3e988cc71d1f44e5eb9785a24920e8
Binary files /dev/null and b/Tp5/minesweeper/icons/0.gif differ
diff --git a/Tp5/minesweeper/icons/1.gif b/Tp5/minesweeper/icons/1.gif
new file mode 100644
index 0000000000000000000000000000000000000000..fc817675c6f607b73904f16c369713edf2f914fc
Binary files /dev/null and b/Tp5/minesweeper/icons/1.gif differ
diff --git a/Tp5/minesweeper/icons/10.gif b/Tp5/minesweeper/icons/10.gif
new file mode 100644
index 0000000000000000000000000000000000000000..10d928aa589648fbfd4e0c37a997cfa5e0eeaf2f
Binary files /dev/null and b/Tp5/minesweeper/icons/10.gif differ
diff --git a/Tp5/minesweeper/icons/11.gif b/Tp5/minesweeper/icons/11.gif
new file mode 100644
index 0000000000000000000000000000000000000000..51fb6a928424a34021239bbb290456976666bcc1
Binary files /dev/null and b/Tp5/minesweeper/icons/11.gif differ
diff --git a/Tp5/minesweeper/icons/12.gif b/Tp5/minesweeper/icons/12.gif
new file mode 100644
index 0000000000000000000000000000000000000000..2854de27bbf73d5d8a9c21db979ab75f144a8a9f
Binary files /dev/null and b/Tp5/minesweeper/icons/12.gif differ
diff --git a/Tp5/minesweeper/icons/13.gif b/Tp5/minesweeper/icons/13.gif
new file mode 100644
index 0000000000000000000000000000000000000000..947d01285d2ba0b83ccd14bf3fde61887798ad16
Binary files /dev/null and b/Tp5/minesweeper/icons/13.gif differ
diff --git a/Tp5/minesweeper/icons/2.gif b/Tp5/minesweeper/icons/2.gif
new file mode 100644
index 0000000000000000000000000000000000000000..2ef31056ccd581f2df4fe0f10e03f9ae831ac7ef
Binary files /dev/null and b/Tp5/minesweeper/icons/2.gif differ
diff --git a/Tp5/minesweeper/icons/3.gif b/Tp5/minesweeper/icons/3.gif
new file mode 100644
index 0000000000000000000000000000000000000000..4abac7be126376b0b3c7a8dd202c91e51c401810
Binary files /dev/null and b/Tp5/minesweeper/icons/3.gif differ
diff --git a/Tp5/minesweeper/icons/4.gif b/Tp5/minesweeper/icons/4.gif
new file mode 100644
index 0000000000000000000000000000000000000000..4e942e211f7d29940e2f0868ad8888d6dc396064
Binary files /dev/null and b/Tp5/minesweeper/icons/4.gif differ
diff --git a/Tp5/minesweeper/icons/5.gif b/Tp5/minesweeper/icons/5.gif
new file mode 100644
index 0000000000000000000000000000000000000000..9b6b607be42cfbd7aed2dd30c2bafc1ce9d3e4c9
Binary files /dev/null and b/Tp5/minesweeper/icons/5.gif differ
diff --git a/Tp5/minesweeper/icons/6.gif b/Tp5/minesweeper/icons/6.gif
new file mode 100644
index 0000000000000000000000000000000000000000..2cc9004893ede6904937f1f6c3fdfea9d517e0de
Binary files /dev/null and b/Tp5/minesweeper/icons/6.gif differ
diff --git a/Tp5/minesweeper/icons/7.gif b/Tp5/minesweeper/icons/7.gif
new file mode 100644
index 0000000000000000000000000000000000000000..aa755b7661dc088e2c73f8650f3b563bd3f2395e
Binary files /dev/null and b/Tp5/minesweeper/icons/7.gif differ
diff --git a/Tp5/minesweeper/icons/8.gif b/Tp5/minesweeper/icons/8.gif
new file mode 100644
index 0000000000000000000000000000000000000000..283c520e31c108effb9d477e4359a3654ce8470e
Binary files /dev/null and b/Tp5/minesweeper/icons/8.gif differ
diff --git a/Tp5/minesweeper/icons/9.gif b/Tp5/minesweeper/icons/9.gif
new file mode 100644
index 0000000000000000000000000000000000000000..50ac94d02f9e29ba36133da6281c3f0b9b2026ec
Binary files /dev/null and b/Tp5/minesweeper/icons/9.gif differ
diff --git a/Tp5/minesweeper/minesweeper.py b/Tp5/minesweeper/minesweeper.py
new file mode 100755
index 0000000000000000000000000000000000000000..f0ab0dcb8e370c08a4a00e182274c54ec4f7b71e
--- /dev/null
+++ b/Tp5/minesweeper/minesweeper.py
@@ -0,0 +1,204 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+"""
+:mod:`minesweeper` module
+
+:author: HERE YOUR NAME
+
+:date:  
+
+This module provides functions and a class for minesweeper's game's management.
+
+"""
+
+import random
+from enum import Enum
+from cell import Cell
+
+
+################################################
+# Type declaration
+################################################
+
+class GameState(Enum):
+    """
+    A class to define an enumerated type with three values :
+
+    * ``winning``
+    * ``losing``
+    * ``unfinished``
+
+    for the three state of minesweeper game.
+    """
+    winning = 1
+    losing = 2
+    unfinished = 3
+
+
+##############################################
+# Function for game's setup and management
+##############################################
+
+
+def neighborhood(x: int, y: int, width: int, height: int) -> list[tuple[int, int]]:
+    """
+    return the list of coordinates of the neighbors of position (x,y) in a
+             grid of size width*height
+
+    précondition: 0 <= x < width and 0 <= y < height
+
+    examples:
+
+    $$$ neighborhood(3, 3, 10, 10)
+    [(2, 2), (2, 3), (2, 4), (3, 2), (3, 4), (4, 2), (4, 3), (4, 4)]
+    $$$ neighborhood(0, 3, 10, 10)
+    [(0, 2), (0, 4), (1, 2), (1, 3), (1, 4)]
+    $$$ neighborhood(0, 0, 10, 10)
+    [(0, 1), (1, 0), (1, 1)]
+    $$$ neighborhood(9, 9, 10, 10)
+    [(8, 8), (8, 9), (9, 8)]
+    $$$ neighborhood(3, 9, 10, 10)
+    [(2, 8), (2, 9), (3, 8), (4, 8), (4, 9)]
+    """
+    res =[]
+    if x>0:
+        res.append(x-1,y)
+    if x< width-1:
+        res.append(x+1,y)
+    if y>0:
+        res.append(x,y-1)
+    if y<height-1:
+        res.append(x,y+1)
+    return res
+class Minesweeper():
+    """
+    $$$ game = Minesweeper(20, 10, 4)
+    $$$ game.width
+    20
+    $$$ game.height
+    10
+    $$$ game.nbombs
+    4
+    $$$ game == GameState.unfinished 
+    True
+    $$$ cel = game.get_cell(1, 2)
+    $$$ cel.is_revealed
+    False
+    $$$ 
+    """
+
+    def __init__(self, width: int=30, height: int=20, nbombs: int=99):
+        """
+        build a minesweeper grid of size `width*height` of cells
+        with `nbombs` bombs randomly placed.
+
+        for each build cell, this constructor sets the  number of bombs
+        in neighborhood.
+
+        precondition: width and height must be positive integers, and
+             nbombs <= width * height
+
+        example:
+
+        $$$ game = Minesweeper(20, 10, 4)
+        $$$ game.width
+        20
+        $$$ game.height
+        10
+        $$$ game.nbombs
+        4
+        $$$ game.state == GameState.unfinished 
+        True
+        """
+        ...
+
+    def get_cell(self, x: int, y: int) -> Cell:
+        """
+        return: the cell of coordinates (x,y) in the game's grid
+
+        precondition: 0 <= x < width of game and O <= y < height of game
+
+        $$$ game = Minesweeper(20, 10, 4)
+        $$$ sum(1 for x in range(20) for y in range(10) if game.get_cell(x, y).is_bomb)
+        4
+        """
+        ...
+
+    def _put_a_bomb_at(self, x: int, y: int):
+        """
+        this method change the cells's state at x, y to a bomb,
+
+        if the cell is already a bomb, nothing append.
+        otherwise it change cell's state and increments by one every
+        cells in its neighborhood.
+
+        precondition: 0 <= x < self.width and 0 <= y < self.height
+
+        exemples:
+
+        $$$ game = Minesweeper(10, 5, 0)
+        $$$ voisins = neighborhood(1, 1, game.width, game.height)
+        $$$ all(game.get_cell(x, y).nbombs_in_neighborhood == 0 for x, y in voisins)
+        True
+        $$$ game._put_a_bomb_at(1, 1)
+        $$$ all(game.get_cell(x, y).nbombs_in_neighborhood == 1 for x, y in voisins)
+        True
+        $$$ game._put_a_bomb_at(1, 1)
+        $$$ all(game.get_cell(x, y).nbombs_in_neighborhood == 1 for x, y in voisins)
+        True
+        """
+        ...
+
+    def all_cells_are_revealed_or_bomb(self) -> bool:
+        """
+        return True iff all cells are revealed or bomb.
+
+        précondition: none
+
+        """
+        ...
+
+    def reveal_all_cells_from(self, x, y):
+        """
+        recursively reveal all cells of game game from the initial cell (x,y).
+
+        * if the cell is a bomb one, update game's state to losing.
+        * otherwise if the cell's neighborhood doesn't contains bomb,
+          recursively reveal all neighboors.
+        * and finally, if all cell's are revealed, update game's state to
+          winning
+        
+        precondition: 0 <= x < width of game and O <= y < height of game
+
+        exemples:
+
+        $$$ game = Minesweeper(20, 10, 0)
+        $$$ game._put_a_bomb_at(1, 1)
+        $$$ game.state
+        GameState.unfinished
+        $$$ game.all_cells_are_revealed_or_bomb()
+        False
+        $$$ game.reveal_all_cells_from(5, 5)
+        $$$ game.all_cells_are_revealed_or_bomb()
+        False
+        $$$ game.reveal_all_cells_from(0, 0)
+        $$$ game.reveal_all_cells_from(1, 0)
+        $$$ game.reveal_all_cells_from(0, 1) 
+        $$$ game.all_cells_are_revealed_or_bomb()
+        True
+        $$$ game.state
+        GameState.winning
+        $$$ game = Minesweeper(20, 10, 0)
+        $$$ game._put_a_bomb_at(1, 1)
+        $$$ game.reveal_all_cells_from(1, 1)
+        $$$ game.state
+        GameState.losing
+        """
+        ...
+
+
+if (__name__ == '__main__'):
+    import apl1test
+    apl1test.testmod('minesweeper.py')
+