diff --git a/labs-solutions/10-graphs-exercises.ipynb b/labs-solutions/10-graphs-exercises.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..07a349c9fef513b4e309f8227814d1e66fa7c9a5
--- /dev/null
+++ b/labs-solutions/10-graphs-exercises.ipynb
@@ -0,0 +1,732 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "497036b2-a410-477c-bcfa-e1dd1b02cd8a",
+   "metadata": {},
+   "source": [
+    "# UE5 Fundamentals of Algorithms\n",
+    "# Lab 10: Graphs"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5876dd49-e808-4107-9b93-08ad6600bd34",
+   "metadata": {},
+   "source": [
+    "---"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3e08eb9a-9d3d-466a-afbe-aa1219475dc7",
+   "metadata": {},
+   "source": [
+    "For the following exercices, your are given a graph data structure as a dictionnary:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 81,
+   "id": "f22d7834-bff7-4e23-b15d-4250ce39a1df",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "dict_keys(['a', 'b', 'c', 'd', 'e', 'f'])"
+      ]
+     },
+     "execution_count": 81,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g = { \"a\" : [\"d\"],\n",
+    "      \"b\" : [\"c\"],\n",
+    "      \"c\" : [\"b\", \"c\", \"d\", \"e\"],\n",
+    "      \"d\" : [\"a\", \"c\"],\n",
+    "      \"e\" : [\"c\"], \n",
+    "      \"f\" : [] \n",
+    "    }"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "7c9b9b7f-0292-4761-aa41-0ebe6cfab49c",
+   "metadata": {},
+   "source": [
+    "## Exercise 1: Understand the graph properties\n",
+    "\n",
+    "Give the graph `g` given previously, answer the following questions:\n",
+    "\n",
+    "1. Draw the graph using a node-link diagram\n",
+    "2. Does it have a cycle?\n",
+    "3. Is it connected? If not, how many components?\n",
+    "4. Does it have a self loop?\n",
+    "5. What is the matrix representation?"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 83,
+   "id": "4931afb4-c133-45a3-b124-5581d2e4d11a",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "M = [[0, 0, 1], [0, 0, 0], [0, 0, 0]]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "613dbe01-f1a1-4811-96f0-fc893cc6ae24",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "Write a function that caculates the degree using the dictionnary data structure:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 65,
+   "id": "64570050-4358-4f6f-bc47-d40cc11caccb",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-9d8017340cffddbb",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def degree_dict(g, node):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    return len(g[node]) if node in g else -\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8ef7c05c-0003-4eca-ac67-e4c712fa2b45",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "Write a function that caculates the degree using the matrix data structure:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 73,
+   "id": "b6c61914-196b-4a4c-9309-70d08cd32bc5",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-99551fa4c7868af9",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def degree_matrix(M, node):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    return sum(M[list(g.keys()).index(node)])\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "81622da3-97c5-470e-af37-deb760cc009c",
+   "metadata": {},
+   "source": [
+    "Write tests"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 75,
+   "id": "bcaeac38-5109-444b-ab06-474bd570459a",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-eef2d364b9c640f2",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "### BEGIN SOLUTION\n",
+    "assert degree_dict(g, 'a') == degree_matrix(M, 'a')\n",
+    "### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2d324f2e-25d4-488b-9178-c29f52caee1a",
+   "metadata": {},
+   "source": [
+    "## Exercise 2: Check if the graph has a cycle\n",
+    "\n",
+    "Here is a `dfs` algorithm for a graph, write a function `has_cycle` to detect if the graph `g` has a cycle."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 38,
+   "id": "b9611934-1a07-4cc5-b3be-1b18e6f50e57",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "a d c b e "
+     ]
+    }
+   ],
+   "source": [
+    "def dfs(graph, start_node):\n",
+    "    visited = set()\n",
+    "    stack = [start_node]\n",
+    "\n",
+    "    while stack:\n",
+    "        node = stack.pop()\n",
+    "        if node not in visited:\n",
+    "            print(node, end=' ')\n",
+    "            visited.add(node)\n",
+    "            for neighbor in reversed(graph[node]):\n",
+    "                if neighbor not in visited:\n",
+    "                    stack.append(neighbor)\n",
+    "                    \n",
+    "dfs(g, 'a') # start from node 'a'."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 130,
+   "id": "17ffa86d-f149-4746-9658-5c6aeb71f6d7",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-fa50a2164fb6d1e6",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def has_cycle(graph):\n",
+    "    ### BEING SOLUTION\n",
+    "    visited = set()\n",
+    "\n",
+    "    for start_node in graph:\n",
+    "        if start_node not in visited:\n",
+    "            stack = [(start_node, None)]\n",
+    "            while stack:\n",
+    "                node, parent = stack.pop()\n",
+    "                if node in visited:\n",
+    "                    continue\n",
+    "                visited.add(node)\n",
+    "                \n",
+    "                for neighbor in graph[node]:\n",
+    "                    if neighbor not in visited:\n",
+    "                        stack.append((neighbor, node)) \n",
+    "                    elif neighbor != parent:\n",
+    "                        return True\n",
+    "    return False\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "849df1e9-aef3-4883-a3c0-d7f0e937315e",
+   "metadata": {},
+   "source": [
+    "Write tests."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 131,
+   "id": "d815ba3f-8dc5-4a2c-a908-4545e66ebbb5",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "assert has_cycle(g)\n",
+    "assert not has_cycle({'a': []})\n",
+    "assert has_cycle({'a': ['a']}) "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "bad893ca-312e-4500-bc2e-d9420dca47a3",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## Exercise 3: Check if the graph is connected\n",
+    "\n",
+    "Check if all nodes can be reached."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 123,
+   "id": "714e9dff-adb1-44d9-a2de-a5eb5fc6f618",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-a4d3225ec6fd0d1d",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def is_connected(graph):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    if not graph:\n",
+    "        return True \n",
+    "    \n",
+    "    visited = set()\n",
+    "    stack = [list(graph.keys())[0]]\n",
+    "\n",
+    "    while stack:\n",
+    "        node = stack.pop()\n",
+    "        if node not in visited:\n",
+    "            visited.add(node)\n",
+    "            for neighbor in graph[node]:\n",
+    "                if neighbor not in visited:\n",
+    "                    stack.append(neighbor)\n",
+    "\n",
+    "    return len(visited) == len(graph)\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d63ff45a-76d7-42d1-ae43-045cdc714b92",
+   "metadata": {},
+   "source": [
+    "Write tests."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 126,
+   "id": "b59faa9a-5ed6-4795-a650-ba0bc4c6b39e",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "assert not is_connected(g)\n",
+    "assert is_connected({'a': []})\n",
+    "assert not is_connected({'a': [], 'b': []})"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "05c4b848-67a1-4aa7-9ae6-182ccca5ad82",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "## Exercise 4: Check if the graph has self-loop\n",
+    "\n",
+    "Check if there is at least a node that links to itself."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 45,
+   "id": "ff86ab1e-574f-45d8-b9d7-cd14d7d9e81b",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-716f2eec18989a2d",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def has_self_loop(graph):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    for node in graph:\n",
+    "        if node in graph[node]:\n",
+    "            return True\n",
+    "    return False\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "385d5be2-036f-448b-9b34-2a5f478eb9db",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "Write tests."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 128,
+   "id": "07f67cfb-3edd-4160-a42e-679f97fedbc8",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "assert has_self_loop(g)\n",
+    "assert has_self_loop({'a': ['a']})\n",
+    "assert not has_self_loop({'a': []})"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "be1da5d3-b677-41dc-b52a-428e84e834ed",
+   "metadata": {},
+   "source": [
+    "## Exercise 5: Use embedded marking for visited nodes\n",
+    "\n",
+    "Instead of using an external list to store visited node, use the data structure below that stores the information internally."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 129,
+   "id": "fb11aa28-e1a8-4700-99a6-c7f0e8477be8",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "g2 = {\n",
+    "    'a': {'neighbors': ['d'], 'visited': False},\n",
+    "    'b': {'neighbors': ['c'], 'visited': False},\n",
+    "    'c': {'neighbors': ['b', 'c', 'd', 'e'], 'visited': False},\n",
+    "    'd': {'neighbors': ['a', 'c'], 'visited': False},\n",
+    "    'e': {'neighbors': ['c'], 'visited': False},\n",
+    "    'f': {'neighbors': [''], 'visited': False}\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 52,
+   "id": "5bcb46e4-d175-4356-b33b-ff44ce7df2a1",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "a d c b e "
+     ]
+    }
+   ],
+   "source": [
+    "def dfs2(graph, start_node):\n",
+    "    stack = [start_node]\n",
+    "\n",
+    "    while stack:\n",
+    "        node = stack.pop()\n",
+    "        if not graph[node]['visited']:\n",
+    "            print(node, end=' ')\n",
+    "            graph[node]['visited'] = True\n",
+    "            for neighbor in reversed(graph[node]['neighbors']):\n",
+    "                if neighbor and not graph[neighbor]['visited']:\n",
+    "                    stack.append(neighbor)\n",
+    "                    \n",
+    "dfs2(g2, 'a')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 34,
+   "id": "e4fcaef5-cfb0-4d10-a8ca-930696b29c89",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-646e92ff31a3a804",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "### BEGIN SOLUTION\n",
+    "\n",
+    "### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "50882bb9-8de8-4e0a-93db-38aedfa77ca0",
+   "metadata": {},
+   "source": [
+    "Write tests."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "7fb9a85c-976f-4054-948a-a58d6e4504ea",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-585b0274f5419089",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "### BEGIN SOLUTION\n",
+    "\n",
+    "### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ce9470d2-7b5c-48a9-9ebf-7060a023a379",
+   "metadata": {},
+   "source": [
+    "## Exercise 6: Use a Graph POO data structure\n",
+    "\n",
+    "Instead of using a Dict, use a POO class (that may use a dict internally) and associated methods to solve the above problems (as new methods of the class)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 35,
+   "id": "bcb18a5c-d96b-464e-aa14-bc096f1ecfd4",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-1a9fb3e8e3fca660",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "class Graph:\n",
+    "    def __init__(self):\n",
+    "        pass\n",
+    "    def has_cycle(self):\n",
+    "        pass\n",
+    "    def is_connected(self):\n",
+    "        pass\n",
+    "    def has_self_loop(self):\n",
+    "        pass\n",
+    "### BEGIN SOLUTION\n",
+    "\n",
+    "### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5ad7b301-ef0f-431c-8587-9318d635c05b",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-20547345ca6ee2a9",
+     "locked": false,
+     "schema_version": 3,
+     "solution": false,
+     "task": false
+    },
+    "tags": []
+   },
+   "source": [
+    "Write tests."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 151,
+   "id": "5d5fa858-9403-420d-a4e3-d00b90fd0d31",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-573b352161e48cfd",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "([0, 1, 2], [True, True, True])\n"
+     ]
+    }
+   ],
+   "source": [
+    "### BEGIN SOLUTION\n",
+    "\n",
+    "def is_oriented_matrix(M):\n",
+    "    \n",
+    "    if len(M) != len(M[0]):\n",
+    "        return False\n",
+    "    \n",
+    "    for i in range(len(M)):\n",
+    "        for j in range(i, len(M)):\n",
+    "            if M[i][j] != M[j][i]:\n",
+    "                return False\n",
+    "    return True\n",
+    "\n",
+    "def is_self_loop(M):\n",
+    "    for i in range(len(M)):\n",
+    "        if M[i][i] == 1:\n",
+    "            return True\n",
+    "    return False\n",
+    "\n",
+    "def bfs_matrix(M, start_node): # start_node is an index\n",
+    "    n = len(M)\n",
+    "    visited = [False] * n\n",
+    "    queue = [start_node]\n",
+    "    visited[start_node] = True\n",
+    "    \n",
+    "    path = []\n",
+    "    \n",
+    "    while queue:\n",
+    "        current = queue.pop(0)\n",
+    "        path.append(current)\n",
+    "        \n",
+    "        for neighbor in range(n):\n",
+    "            if M[current][neighbor] == 1 and not visited[neighbor]:\n",
+    "                queue.append(neighbor)\n",
+    "                visited[neighbor] = True \n",
+    "                \n",
+    "    return path, visited\n",
+    "\n",
+    "def is_oriented_dict(graph):\n",
+    "    \n",
+    "    for key, values in graph.items():\n",
+    "        for v in values:\n",
+    "            if key not in graph[v]:\n",
+    "                return False\n",
+    "\n",
+    "    return True\n",
+    "\n",
+    "print(bfs_matrix([[1, 1, 1], [1, 1, 1], [1, 1, 1]], 0))\n",
+    "        \n",
+    "assert not is_self_loop(M)\n",
+    "assert is_self_loop([[1]])\n",
+    "assert is_oriented_matrix([[1]])\n",
+    "assert not is_oriented_matrix([[1, 0], [1, 1]])\n",
+    "\n",
+    "assert is_oriented_dict(g)\n",
+    "assert is_oriented_dict({'a': []})\n",
+    "assert is_oriented_dict({'a': ['a']})\n",
+    "assert not is_oriented_dict({'a': ['b'], 'b': []})\n",
+    "### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 144,
+   "id": "c097160a-6c52-4caf-9e50-70688ae51106",
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "{'a': ['d'],\n",
+       " 'b': ['c'],\n",
+       " 'c': ['b', 'c', 'd', 'e'],\n",
+       " 'd': ['a', 'c'],\n",
+       " 'e': ['c'],\n",
+       " 'f': []}"
+      ]
+     },
+     "execution_count": 144,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "g"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "22c67937-aef2-4961-8f60-00cc2501ccb8",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.10.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/labs-solutions/11-graphs-matrix-exercises.ipynb b/labs-solutions/11-graphs-matrix-exercises.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..d9d5085a2182ea4b81c68e4e62981fcbc8f87b94
--- /dev/null
+++ b/labs-solutions/11-graphs-matrix-exercises.ipynb
@@ -0,0 +1,720 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "5112bda3-5774-4eee-9dcb-5893136ac5dc",
+   "metadata": {},
+   "source": [
+    "# UE5 Fundamentals of Algorithms\n",
+    "# Lab 11: Graphs/Matrix"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3054b63b-5b4a-4b07-a129-71951eaee576",
+   "metadata": {},
+   "source": [
+    "---"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6892d60b-b418-486b-a1db-008652e04457",
+   "metadata": {},
+   "source": [
+    "## How to Get Out of a Maze?\n",
+    "\n",
+    "In this exercise, you will write an algorithm to navigate out of a maze. The maze will be represented as a 2D matrix (discrete coordinates), which will serve as the data structure for storage and traversal. The starting point of the traversal will always be at the coordinates `(0, 0)` in the top-left corner, and the endpoint will be the bottom-right corner (in the case of the example below, `(9, 9)`). Walls with a value of `1` are impassable, and you also cannot move outside the matrix. Movement is possible in 8 directions: up, down, left, right, and their corresponding diagonals. In this exercise, you will explore three traversal methods. Note that there can be multiple different, but all valid, solutions."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "56b3cb10-e5e1-4c1e-8a38-948b97b2ad47",
+   "metadata": {},
+   "source": [
+    "**Question 1.1 -** Run the program below, which loads the maze and includes a display function. Identify one of the paths leading to the exit. Modify the code to display walls using `#` instead of `1`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 116,
+   "id": "5de72656-1ea0-4662-afd7-7d3c60c77612",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "0  0  0  0  #  0  0  0  0  0  \n",
+      "0  0  0  0  #  0  0  0  0  0  \n",
+      "0  0  0  0  #  0  0  0  0  0  \n",
+      "0  0  0  0  #  0  0  0  0  0  \n",
+      "0  0  0  0  #  0  0  0  0  0  \n",
+      "0  0  0  0  0  0  0  0  0  0  \n",
+      "0  0  0  0  #  0  0  0  0  0  \n",
+      "0  0  0  0  #  0  0  0  0  0  \n",
+      "0  0  0  0  #  0  0  0  0  0  \n",
+      "0  0  0  0  0  0  0  0  0  0  \n"
+     ]
+    }
+   ],
+   "source": [
+    "maze = [[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],\n",
+    "              [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],\n",
+    "              [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],\n",
+    "              [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],\n",
+    "              [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],\n",
+    "              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
+    "              [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],\n",
+    "              [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],\n",
+    "              [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],\n",
+    "              [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]\n",
+    "\n",
+    "\n",
+    "def display_maze(l):\n",
+    "    for row in l:\n",
+    "        for item in row:\n",
+    "            print(\"#  \" if item == 1 else f\"{item}  \", end=\"\")\n",
+    "        print()\n",
+    "    \n",
+    "display_maze(maze)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d56f24af-bf15-488c-8439-4a02b183964a",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "Give a valid path (that links the top left corner with the bottom-right corner) using the cell coordinates. E.g. `(0, 0), (1, 0) ... (9, 9)`\n",
+    "```\n",
+    "* 0 0 0 1 0 0 0 0 0\n",
+    "* 0 0 0 1 0 0 0 0 0\n",
+    "* 0 0 0 1 0 0 0 0 0\n",
+    "* 0 0 0 1 0 0 0 0 0\n",
+    "* 0 0 0 1 0 0 0 0 0\n",
+    "* * * * * * 0 0 0 0\n",
+    "0 0 0 0 1 * 0 0 0 0\n",
+    "0 0 0 0 1 * 0 0 0 0\n",
+    "0 0 0 0 1 * 0 0 0 0\n",
+    "0 0 0 0 0 * * * * *\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 34,
+   "id": "480e043f-9041-4462-a2b8-cdfd0b5a9253",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-b7d96dc4683b1703",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "path = [\n",
+    "    ### BEGIN SOLUTION\n",
+    "    (0, 0), (1, 0), (2, 0), (3, 0), (4, 0), \n",
+    "    (5, 0), (5, 1), (5, 2), (5, 3), (5, 4),\n",
+    "    (5, 5), (6, 5), (7, 5), (8, 5), (9, 5),\n",
+    "    (9, 6), (9, 7), (9, 8), (9, 9)\n",
+    "    ### END SOLUTION\n",
+    "]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4b8613a0-7a9d-40b3-9786-33811bb079fc",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "Write an algorithm that draws the path on a given maze (using `*`). Make sur you coupy the maze values using `[row[:] for row in maze]`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 35,
+   "id": "82eb2cca-bc6b-46b0-8bb6-867023701fe0",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-35b9fc43128af1f4",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def display_path_on_maze(maze, path):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    maze_copy = [row[:] for row in maze]\n",
+    "    \n",
+    "    for (x, y) in path:\n",
+    "        maze_copy[x][y] = '*'\n",
+    "    \n",
+    "    for row in maze_copy:\n",
+    "        print(' '.join(str(cell) for cell in row))\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 36,
+   "id": "3e47e24b-7c77-4a73-b793-7294058f966d",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "* 0 0 0 1 0 0 0 0 0\n",
+      "* 0 0 0 1 0 0 0 0 0\n",
+      "* 0 0 0 1 0 0 0 0 0\n",
+      "* 0 0 0 1 0 0 0 0 0\n",
+      "* 0 0 0 1 0 0 0 0 0\n",
+      "* * * * * * 0 0 0 0\n",
+      "0 0 0 0 1 * 0 0 0 0\n",
+      "0 0 0 0 1 * 0 0 0 0\n",
+      "0 0 0 0 1 * 0 0 0 0\n",
+      "0 0 0 0 0 * * * * *\n"
+     ]
+    }
+   ],
+   "source": [
+    "display_path_on_maze(maze, path)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "10287b07-ed9e-4a62-844f-1831d1be46a5",
+   "metadata": {},
+   "source": [
+    "Write a function that checks if a path is valid for the maze given previously.\n",
+    "\n",
+    "1. The path must start at (0, 0) and end at (n-1, m-1).\n",
+    "2. Each cell in the path must be accessible (value 0).\n",
+    "3. The movement between consecutive cells must be valid (one of 8 directions).\n",
+    "4. The next cell must be within bounds and accessible."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 39,
+   "id": "3968dce5-9ed6-45f9-8814-c097851c9291",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-aac5bf14c89fadb2",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def is_valid_path(maze, path):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    n, m = len(maze), len(maze[0])\n",
+    "    \n",
+    "    if path[0] != (0, 0) or path[-1] != (n-1, m-1):\n",
+    "        return False\n",
+    "    \n",
+    "    directions = [(-1, 0), (1, 0), (0, -1), (0, 1), \n",
+    "                  (-1, -1), (-1, 1), (1, -1), (1, 1)]\n",
+    "    \n",
+    "    for i in range(len(path) - 1):\n",
+    "        x, y = path[i]\n",
+    "        nx, ny = path[i + 1]\n",
+    "        \n",
+    "        if maze[x][y] != 0:\n",
+    "            return False\n",
+    "        \n",
+    "        if (nx - x, ny - y) not in directions:\n",
+    "            return False\n",
+    "        \n",
+    "        if not (0 <= nx < n and 0 <= ny < m) or maze[nx][ny] != 0:\n",
+    "            return False\n",
+    "    \n",
+    "    x, y = path[-1]\n",
+    "    if maze[x][y] != 0:\n",
+    "        return False\n",
+    "    return True\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 40,
+   "id": "27c1fa22-bfa1-4c10-820c-13b3ffc35f1d",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "assert is_valid_path(maze, path)\n",
+    "assert not is_valid_path(maze, [(0, 0)])\n",
+    "assert not is_valid_path(maze, [(-1, -1)])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3887d798-7d13-4bfd-9d5b-e9357e998bca",
+   "metadata": {},
+   "source": [
+    "**Question 1.2 -** Write a function `neighbors` that returns all the neighbors of a cell (i.e., all other cells accessible from this cell)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 117,
+   "id": "2363dbae-a62e-4f3a-b399-c8f172b6bd09",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-a45a6dc113f72b51",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def neighbors(maze, x, y):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    # directions: up, down, left, right, and diagonals\n",
+    "    directions = [(-1, 0), (1, 0), (0, -1), (0, 1), \n",
+    "                  (-1, -1), (-1, 1), (1, -1), (1, 1)]\n",
+    "    \n",
+    "    valid_neighbors = []\n",
+    "    for dx, dy in directions:\n",
+    "        nx, ny = x + dx, y + dy\n",
+    "        \n",
+    "        if 0 <= nx < len(maze[0]) and 0 <= ny < len(maze) and maze[ny][nx] == 0: # check if in perimeter\n",
+    "            valid_neighbors.append((nx, ny)) \n",
+    "    \n",
+    "    return valid_neighbors\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 118,
+   "id": "9554f674-f8c2-4784-9de0-7f553db68802",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "assert set(neighbors(maze, 0, 0)) == {(1, 0), (1, 1), (0, 1)} # the top left cell has 3 neighbors\n",
+    "assert set(neighbors(maze, 1, 1)) == {(0, 0), (0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1), (2, 2)}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 119,
+   "id": "c08bf511-16e4-4d7e-9641-9e6aec991cc9",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "[(3, 5), (5, 5), (3, 4), (3, 6), (5, 4), (5, 6)]"
+      ]
+     },
+     "execution_count": 119,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "neighbors(maze, 4, 5)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a8290326-ce83-4824-b7eb-22b45983032d",
+   "metadata": {},
+   "source": [
+    "Write a function that calls the `neighbors` and returns all the values from a given point, make sure they are all zeros."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 120,
+   "id": "1048003e-f4e2-4ace-a953-818750f36934",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-8ff7571fa5dfcf65",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def neighbor_values_from_point(maze, x, y):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    valid_neighbors = neighbors(maze, x, y)\n",
+    "    valid_neighbors_values = [maze[ny][nx] for nx, ny in valid_neighbors]\n",
+    "    return valid_neighbors_values\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 121,
+   "id": "1285617c-f3c8-4c03-a002-2e18bb8efc00",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "assert all(v == 0 for v in neighbor_values_from_point(maze, 4, 5))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "7b0c72c0-c1bc-4783-8ea9-e83860763070",
+   "metadata": {},
+   "source": [
+    "Propose an algorithm to determine if there is a path connecting the entry and the exit. In this question, you will use a depth-first search (DFS) approach with a recursive implementation. The stopping condition for your search algorithm is reached if you are on the exit cell or if all neighbors have been visited. The algorithm should return `True` if a path exists, or `False` if it does not. You may use the `neighbors` function written previously."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 122,
+   "id": "5c839ee8-6339-4235-975a-fdc0190e5821",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-6e874315f3dd952b",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def exist_dfs(maze, x0=0, y0=0, visited=None):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    if visited is None:\n",
+    "        visited = set()\n",
+    "    \n",
+    "    # base case\n",
+    "    if (x0, y0) == (len(maze[0]) - 1, len(maze) - 1):\n",
+    "        return True\n",
+    "    \n",
+    "    visited.add((x0, y0))\n",
+    "    \n",
+    "    for x, y in neighbors(maze, x0, y0):\n",
+    "        if (x, y) not in visited: \n",
+    "            if exist_dfs(maze, x, y, visited):\n",
+    "                return True\n",
+    "    \n",
+    "    return False\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "995d1bf9-5fd1-4011-828f-26044ee8a184",
+   "metadata": {},
+   "source": [
+    "Write tests."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 123,
+   "id": "23e09473-c451-43fe-aa63-c8151c1ce01b",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "assert exist_dfs(maze)\n",
+    "assert not exist_dfs([[0, 1], [1, 1]])\n",
+    "assert exist_dfs([[0, 1], [0, 0]])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "db449e33-b3b2-4525-a8fe-cd5fffa0bb23",
+   "metadata": {},
+   "source": [
+    "**Exercise 1.3 -** We now use a _breadth-first search (BFS)_ algorithm with an _iterative_ implementation to determine if an exit exists."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 124,
+   "id": "b4ca764e-e1f7-4e17-9b03-dcadde3434c9",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-07e7d9e3ea11477c",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def exist_bfs(maze):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    todo = [(0,0)] # queue\n",
+    "    dejaVu = [[False] * len(ligne) for ligne in maze]\n",
+    "    dejaVu[0][0] = True\n",
+    "    while todo and todo[0] != (len(maze[0])-1, len(maze)-1):\n",
+    "        x0, y0 = todo.pop(0)\n",
+    "        for x, y in neighbors(maze, x0, y0):\n",
+    "            if not dejaVu[y][x]:\n",
+    "                dejaVu[y][x] = True\n",
+    "                todo.append((x, y))\n",
+    "    return len(todo) > 0\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1a131d46-7f70-4c3d-b8c3-4df7c6f8ce1e",
+   "metadata": {},
+   "source": [
+    "Compare to the dfs."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 125,
+   "id": "8380bc3a-fbd6-4405-a08d-1770af316bf8",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "assert exist_bfs(maze)\n",
+    "assert exist_bfs([[0, 1], [1, 1]]) == exist_dfs([[0, 1], [1, 1]])\n",
+    "assert exist_bfs([[0, 1], [0, 0]]) == exist_dfs([[0, 1], [0, 0]])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "10e47cd3-948e-44a5-9172-9ad540978310",
+   "metadata": {},
+   "source": [
+    "Now implement a backtracking approach to provide the path to the exit. To achieve this use a dictionnary to store the `previous` step before processing the node.\n",
+    "\n",
+    "1. If no path exists (`previous` is empty), return None.\n",
+    "2. Initialize the path with the end position: `path = [end]`.\n",
+    "3. While the current position is not the start, set `(x, y) = previous[y][x]` and append `(x, y)` to the path.\n",
+    "4. Reverse the path to get it from start to end."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 155,
+   "id": "5f54b7dc-4a6a-4382-9c0e-eb57bbd38dc1",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-8284f5069db6e454",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def path_bfs(maze):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    todo = [(0, 0)] \n",
+    "    debug = [row[:] for row in maze]  \n",
+    "    antecedente = [[None] * len(ligne) for ligne in maze] \n",
+    "    antecedente[0][0] = (0, 0)\n",
+    "    nb_traitements = 1\n",
+    "    debug[0][0] = 1\n",
+    "\n",
+    "    while todo:\n",
+    "        x0, y0 = todo.pop(0) \n",
+    "        if (x0, y0) == (len(maze[0]) - 1, len(maze) - 1):\n",
+    "            break\n",
+    "        \n",
+    "        for x, y in neighbors(maze, x0, y0):\n",
+    "            if antecedente[y][x] is None and maze[y][x] == 0:  \n",
+    "                antecedente[y][x] = (x0, y0) \n",
+    "                todo.append((x, y))  \n",
+    "                nb_traitements += 1\n",
+    "                debug[y][x] = nb_traitements\n",
+    "\n",
+    "    if not todo:  # no path found\n",
+    "        return None\n",
+    "\n",
+    "    chemin = [(len(maze[0]) - 1, len(maze) - 1)]\n",
+    "    x, y = chemin[0]\n",
+    "    while (x, y) != (0, 0):  \n",
+    "        x, y = antecedente[y][x]\n",
+    "        chemin.append((x, y))\n",
+    "\n",
+    "    chemin.reverse() \n",
+    "    return chemin\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "327f6930-2a62-4b38-a5a4-8f53f3a360a1",
+   "metadata": {},
+   "source": [
+    "# Bonus questions"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "03a90755-ff99-4839-a0be-ab09854f340e",
+   "metadata": {
+    "tags": []
+   },
+   "source": [
+    "Write a function that generates an empty maze:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 153,
+   "id": "4ef141a5-e2fb-4fd4-b186-6e5bd1f0f463",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-75d2a2d7b53d889d",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "def generate_empty_maze(rows, cols):\n",
+    "    ### BEGIN SOLUTION\n",
+    "    return [[0 for _ in range(cols)] for _ in range(rows)]\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 154,
+   "id": "f19ddefd-b4e8-497d-a704-97a3e0c3de6b",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "assert generate_empty_maze(0, 0) == []\n",
+    "assert generate_empty_maze(1, 1) == [[0]]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e840bdc4-080b-4ae7-8ca6-58d349449df3",
+   "metadata": {},
+   "source": [
+    "Write a function that generates a maze with 1) valid start and end points, and 2) a % of `X` in it."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 145,
+   "id": "e297a6ad-e21d-4ff7-8aa3-17959b335dcb",
+   "metadata": {
+    "nbgrader": {
+     "grade": false,
+     "grade_id": "cell-8ce417acecb9808b",
+     "locked": false,
+     "schema_version": 3,
+     "solution": true,
+     "task": false
+    },
+    "tags": []
+   },
+   "outputs": [],
+   "source": [
+    "import random\n",
+    "def generate_maze_with_x(rows, cols, x_percent=50):\n",
+    "    ### BEGIN SOLUTION\n",
+    "\n",
+    "    maze = [[0 for _ in range(cols)] for _ in range(rows)]\n",
+    "    \n",
+    "    total_cells = rows * cols\n",
+    "    num_x_cells = int((x_percent / 100) * total_cells)\n",
+    "    \n",
+    "    for _ in range(num_x_cells):\n",
+    "        while True:\n",
+    "            r = random.randint(0, rows - 1)\n",
+    "            c = random.randint(0, cols - 1)\n",
+    "            if maze[r][c] == 0 and (r, c) != (0, 0) and (r, c) != (rows-1, cols-1):\n",
+    "                maze[r][c] = 'X'\n",
+    "                break\n",
+    "    return maze\n",
+    "    ### END SOLUTION"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 146,
+   "id": "a7d1c148-aa31-4be6-a718-7c6f064f1112",
+   "metadata": {
+    "tags": []
+   },
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.10.9"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}