diff --git a/README.md b/README.md
index 8e9f264..13a5bb1 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ def function1(
-[Source](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/src/ex1_.py)
+[Source](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/src/Ex1.py)
/
[Tests](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/tests/test_ex1.py)
@@ -46,7 +46,7 @@ def function1(
But |
- |
+... |
Input |
@@ -70,7 +70,7 @@ def function2(
-[Source](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/src/ex2_.py)
+[Source](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/src/Ex2.py)
/
[Tests](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/tests/test_ex2.py)
@@ -104,7 +104,7 @@ def function3(
-[Source](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/src/ex3_.py)
+[Source](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/src/Ex3.py)
/
[Tests](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/tests/test_ex3.py)
@@ -138,7 +138,7 @@ def function4(
-[Source](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/src/ex4_.py)
+[Source](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/src/Ex4.py)
/
[Tests](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/tests/test_ex4.py)
@@ -172,6 +172,6 @@ def function5(
-[Source](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/src/ex5_.py)
+[Source](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/src/Ex5.py)
/
[Tests](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/tests/test_ex5.py)
diff --git a/src/Ex2.py b/src/Ex2.py
new file mode 100644
index 0000000..175c9ff
--- /dev/null
+++ b/src/Ex2.py
@@ -0,0 +1,95 @@
+
+"""
+Nom/Prénom: Heredero/Louis
+Explications:
+Tout d'abord, le réseau de routes et d'intersections peut être représenté par un graphe,
+dans lequel les interséctions sont les nœuds et les routes les arêtes.
+
+Ainsi, nous cherchons à trouver le chemin le plus court du point de départ au
+point d'arrivée, tel que les routes marquées 1 ne soient parcourues que de jour
+(c'est-à-dire si leurs index dans le chemin parcouru est pair), et celles marquées -1
+seulement de nuit (index impair). En plus des routes données, nous pouvons aussi
+rester sur un même nœud pour une nuit.
+Afin de bien gérer les différences d'états entre jour et nuit, nous pouvons intégrer le demi-jour
+associé à chaque visite de nœud. Ainsi, une même intersection peut avoir deux nœud:
+un pour une visite de jour, et un de nuit.
+
+Pour résoudre ce problème, comme nous ne pouvons pas établir d'heuristique mesurant
+notre distance au point d'arrivée, nous pouvons utiliser un algorithme BFS assez simple:
+- Initialiser la liste des nœuds à traiter avec le nœud de départ et le demi-jour de départ (`DAY`)
+- Tant que nous n'avons pas trouvé le nœud d'arrivée:
+ - Pour chaque nœud à traiter :
+ - Visiter les nœuds voisins non visités (liés par une route praticable)
+ - Indiquer pour chaque voisin son parent
+ - L'ajouter à la nouvelle liste des nœuds à traiter
+ - Alterner le demi-jour courant
+ - Recommencer avec la nouvelle liste de nœuds à traiter
+"""
+from typing import Optional
+
+ALWAYS = 0
+DAY = 1
+NIGHT = -1
+
+
+def get_path(visited: dict[tuple[int, int], Optional[int]], end: int, end_time: int) -> list[int]:
+ path: list[int] = []
+ parent: Optional[int] = end
+
+ time: int = end_time
+ while parent is not None:
+ path.append(parent)
+ parent = visited[(parent, time)]
+ time = -time
+
+ return list(reversed(path))
+
+def findSafestPath(start: int, end: int, intersections: list[tuple[int, int, int]]) -> list[int]:
+ edges: dict[int, dict[int, set[int]]] = {
+ ALWAYS: {},
+ DAY: {},
+ NIGHT: {}
+ }
+
+ for i1, i2, mode in intersections:
+ edge_dict: dict[int, set[int]] = edges[mode]
+ if i1 not in edge_dict:
+ edge_dict[i1] = set()
+ if i2 not in edge_dict:
+ edge_dict[i2] = set()
+ edge_dict[i1].add(i2)
+ edge_dict[i2].add(i1)
+
+ visited: dict[tuple[int, int], Optional[int]] = {
+ (start, DAY): None
+ }
+ to_process: list[int] = [start]
+ time = DAY
+ while len(to_process) != 0:
+ to_process2 = []
+ time2 = -time
+ for idx in to_process:
+ always: set[int] = edges[ALWAYS].get(idx, set())
+ matching_time: set[int] = edges[time].get(idx, set())
+ neighbors: set[int] = always | matching_time
+ neighbors.add(idx)
+
+ for neighbor in neighbors:
+ key = (neighbor, time2)
+ # Skip if already visited
+ if key in visited:
+ continue
+ visited[key] = idx
+ if neighbor == end:
+ return get_path(visited, end, time2)
+ to_process2.append(neighbor)
+
+ to_process = to_process2
+ time = time2
+
+ return []
+
+
+if __name__ == '__main__':
+ print(findSafestPath(0,2,[(0, 1, -1), (1, 2, 0)]) == [0, 0, 1, 2])
+ print(findSafestPath(0, 5, [(0,1,0), (0,2,1), (2,1,-1), (1,3,-1), (2,4,-1), (3,5,1), (3,4,0), (4,5,-1)]) == [0, 1, 3, 5])
diff --git a/tests/test_ex2.py b/tests/test_ex2.py
index b24a888..54e6601 100644
--- a/tests/test_ex2.py
+++ b/tests/test_ex2.py
@@ -1,8 +1,20 @@
import unittest
+from Ex2 import findSafestPath
+
+
class MyTestCase(unittest.TestCase):
def test_simple1(self):
- pass
+ self.assertEqual(
+ findSafestPath(0, 2, [(0, 1, -1), (1, 2, 0)]),
+ [0, 0, 1, 2]
+ )
+
+ def test_simple2(self):
+ self.assertEqual(
+ findSafestPath(0, 5, [(0,1,0), (0,2,1), (2,1,-1), (1,3,-1), (2,4,-1), (3,5,1), (3,4,0), (4,5,-1)]),
+ [0, 1, 3, 5]
+ )
if __name__ == '__main__':
unittest.main()