Compare commits
	
		
			7 Commits
		
	
	
		
			7312f8a293
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0dcccd2b74 | |||
| 35f194f649 | |||
| 2fda876df3 | |||
| 68ccc3c183 | |||
| 3c100a08fe | |||
| 9c6bf54f20 | |||
| 70daa208d0 | 
							
								
								
									
										77
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										77
									
								
								README.md
									
									
									
									
									
								
							| @@ -12,31 +12,31 @@ Voici mes réponses pour l'examen 2025 d'_Algorithmes et Structures de données_ | ||||
| <table> | ||||
| <tr> | ||||
| <td><strong>But</strong></td> | ||||
| <td>...</td> | ||||
| <td>Compter le nombre de valeurs positives différentes ayant leur opposé dans une liste</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Input</strong></td> | ||||
| <td>...</td> | ||||
| <td>La liste des demi-touches (entiers relatifs non-nuls)</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Output</strong></td> | ||||
| <td>...</td> | ||||
| <td>Le nombre de touches distinctes dont les valeurs positives et négatives sont dans la liste</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Signature</strong></td> | ||||
| <td> | ||||
|  | ||||
| ```python | ||||
| def function1( | ||||
|     *args | ||||
| ) -> Any: | ||||
| def countKey( | ||||
|     pieces: list[int] | ||||
| ) -> int: | ||||
| ``` | ||||
|  | ||||
| </td> | ||||
| </tr> | ||||
| </table> | ||||
|  | ||||
| [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,31 +46,33 @@ def function1( | ||||
| <table> | ||||
| <tr> | ||||
| <td><strong>But</strong></td> | ||||
| <td></td> | ||||
| <td>Trouver le chemin le plus court entre deux points, en considérant que certaines routes ne sont praticables que de nuit/jour</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Input</strong></td> | ||||
| <td>...</td> | ||||
| <td>Nœud de départ (index), nœud d'arrivée (index) et liste des routes / arêtes (nœud 1, nœud 2, restriction)</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Output</strong></td> | ||||
| <td>...</td> | ||||
| <td>Liste des nœuds (index) à parcourir</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Signature</strong></td> | ||||
| <td> | ||||
|  | ||||
| ```python | ||||
| def function2( | ||||
|     *args | ||||
| ) -> Any: | ||||
| def findSafestPath( | ||||
|     start: int, | ||||
|     end: int, | ||||
|     intersections: list[tuple[int, int, int]] | ||||
| ) -> list[int]: | ||||
| ``` | ||||
|  | ||||
| </td> | ||||
| </tr> | ||||
| </table> | ||||
|  | ||||
| [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) | ||||
|  | ||||
| @@ -80,31 +82,32 @@ def function2( | ||||
| <table> | ||||
| <tr> | ||||
| <td><strong>But</strong></td> | ||||
| <td>...</td> | ||||
| <td>Trouver dans un réseau de console un sous-graphe connexe de taille donnée</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Input</strong></td> | ||||
| <td>...</td> | ||||
| <td>Taille n du sous-graphe recherché, liste des connexions</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Output</strong></td> | ||||
| <td>...</td> | ||||
| <td>Liste de n consoles toutes interconnectées</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Signature</strong></td> | ||||
| <td> | ||||
|  | ||||
| ```python | ||||
| def function3( | ||||
|     *args | ||||
| ) -> Any: | ||||
| def findTightlyLinkedConsoles( | ||||
|     n: int, | ||||
|     consoles: list[tuple[int, int]] | ||||
| ) -> list[int]: | ||||
| ``` | ||||
|  | ||||
| </td> | ||||
| </tr> | ||||
| </table> | ||||
|  | ||||
| [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) | ||||
|  | ||||
| @@ -114,31 +117,33 @@ def function3( | ||||
| <table> | ||||
| <tr> | ||||
| <td><strong>But</strong></td> | ||||
| <td>...</td> | ||||
| <td>Compter le nombre de séquences de N coups infligeant un total de H dégâts</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Input</strong></td> | ||||
| <td>...</td> | ||||
| <td>Nombre N de coups dans la séquence, nombre C de coups possibles (dégâts 1 à C inclus), total H de dégâts</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Output</strong></td> | ||||
| <td>...</td> | ||||
| <td>Nombre de séquences possibles</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Signature</strong></td> | ||||
| <td> | ||||
|  | ||||
| ```python | ||||
| def function4( | ||||
|     *args | ||||
| ) -> Any: | ||||
| def computeNbrOfDifferentSequences( | ||||
|     N: int, | ||||
|     C: int, | ||||
|     H: int | ||||
| ) -> int: | ||||
| ``` | ||||
|  | ||||
| </td> | ||||
| </tr> | ||||
| </table> | ||||
|  | ||||
| [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) | ||||
|  | ||||
| @@ -148,30 +153,32 @@ def function4( | ||||
| <table> | ||||
| <tr> | ||||
| <td><strong>But</strong></td> | ||||
| <td>...</td> | ||||
| <td>Trouver les placements de pièces Minitris formant un rectangle plein</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Input</strong></td> | ||||
| <td>...</td> | ||||
| <td>Largeur de la grille, longueur de la grille, liste des pièces à poser</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Output</strong></td> | ||||
| <td>...</td> | ||||
| <td>Liste des positions des pièces</td> | ||||
| </tr> | ||||
| <tr> | ||||
| <td><strong>Signature</strong></td> | ||||
| <td> | ||||
|  | ||||
| ```python | ||||
| def function5( | ||||
|     *args | ||||
| ) -> Any: | ||||
| def playMinitrisFastAndWell( | ||||
|     board_width: int, | ||||
|     board_height: int, | ||||
|     pieces: list[tuple[tuple[int,int], tuple[int,int]]] | ||||
| ) -> list[list[int, int]]: | ||||
| ``` | ||||
|  | ||||
| </td> | ||||
| </tr> | ||||
| </table> | ||||
|  | ||||
| [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) | ||||
|   | ||||
							
								
								
									
										57
									
								
								src/Ex1.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/Ex1.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
|  | ||||
| """ | ||||
| Nom/Prénom: Heredero/Louis | ||||
| Explications: | ||||
|  | ||||
| Une approche serait de trier la liste des valeurs par ordre croissant | ||||
| Une fois triée, nous pouvons comparer les valeurs absolues des 2 extrémités : | ||||
| - Si elles sont égales, nous avons une touche complète supplémentaire (nous les enlevons de la liste) | ||||
| - Sinon, nous enlevons celle avec la plus grande valeur absolue | ||||
|  | ||||
| NB: Afin de ne compter que les paires distinctes, nous pouvons soit transformer | ||||
| la liste en `set` (retire les valeurs à double), soit garder une liste des valeurs | ||||
| de touches complètes trouvées | ||||
| Cela permettrait aussi peut-être d'améliorer l'efficacité de l'algorithme de la manière suivante: | ||||
| - Créer deux `set` : valeurs positives et valeurs négatives (stocker la valeur | ||||
|   absolue des valeurs négatives dans le `set`) | ||||
| - Calculer l'intersection des deux `set` : | ||||
|   `intersection = positives.intersection(negatives)` | ||||
| - Le nombre de touches complètes est la taille de cette intersection : | ||||
|   `complete = len(intersection)` | ||||
|  | ||||
| Par exemple : | ||||
| list = [-3, -2, -1, 1, 2, 5] -> |-3| != |5| -> on enlève 5 | ||||
| list = [-3, -2, -1, 1, 2] -> |-3| != |2| -> on enlève -3 | ||||
| list = [-2, -1, 1, 2] -> |-2| = |2| -> une touche complète | ||||
| list = [-1, 1] -> |-1| = |1| -> une autre touche complète | ||||
| list = [] | ||||
| """ | ||||
|  | ||||
|  | ||||
| def countKey(pieces: list[int]) -> int: | ||||
|     pieces = sorted(pieces) | ||||
|     complete: int = 0 | ||||
|  | ||||
|     while len(pieces) >= 2: | ||||
|         a, b = pieces[0], pieces[-1] | ||||
|  | ||||
|         # Si toutes les pièces restantes sont de même signe, on peut s'arrêter | ||||
|         if a >= 0 or b <= 0: | ||||
|             break | ||||
|  | ||||
|         abs_a, abs_b = abs(a), abs(b) | ||||
|         if abs_a == abs_b: | ||||
|             complete += 1 | ||||
|             pieces = pieces[1:-1] | ||||
|         elif abs_a < abs_b: | ||||
|             pieces.pop(-1) | ||||
|         else: | ||||
|             pieces.pop(0) | ||||
|  | ||||
|     return complete | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     #print(countKey([-3, 4, 2, 8, 9, 1, -3, -8, -4, 2, 8, 2, -8, 1, 3]) == 3) | ||||
|     print(countKey([-3, 4, 2, 8, 9, 1, -3, -8, -4, 2, 8, 2, -8, 1, 3]) == 4) | ||||
|     print(countKey([-4, 5, 6, -1500, 4, 7, 49, 60, -60, 82, -41, 1500, 1, -7]) == 4) | ||||
							
								
								
									
										96
									
								
								src/Ex2.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/Ex2.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
|  | ||||
| """ | ||||
| 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œuds : | ||||
| 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]) | ||||
							
								
								
									
										76
									
								
								src/Ex3.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/Ex3.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
|  | ||||
| """ | ||||
| Nom/Prénom: Heredero/Louis | ||||
| Explications: | ||||
|  | ||||
| Comme indiqué dans la donnée de l'exercice, il s'agit ici de trouvé un sous-graphe | ||||
| connexe dans un graphe quelconque de consoles interconnectées | ||||
|  | ||||
| Pour ce faire, nous pouvons procéder ainsi : | ||||
| Étape 1 : | ||||
| - Compter le nombre de voisins de chaque nœud (console) | ||||
| - Éliminer ceux ayant moins de voisins que la taille de sous-graphe recherché | ||||
|   - Supprimer également les arêtes connectées à ces nœuds | ||||
| - Recommencer jusqu'à ce que : | ||||
|   a) il y ait moins de nœuds que la taille du sous-graphe recherché | ||||
|      -> il n'est donc pas possible de trouver un sous-graphe connexe de taille n | ||||
|   b) aucun nœud du graphe n'ait moins de voisins que voulu (aucune suppression de nœud) | ||||
|      -> il peut exister un sous-graphe connexe de taille n (Cf. étape 2) | ||||
|  | ||||
| Étape 2 : | ||||
| - Pour chaque nœud du graphe : | ||||
|   - Calculer l'ensemble des nœuds communs entre ses voisins (et lui-même), | ||||
|     et les voisins de ses voisins (et eux-mêmes)' | ||||
|     C'est-à-dire, si N1 est un nœud et nb(N1) = {N2, N3, ...} est l'ensemble de ses voisins, | ||||
|     on cherche l'intersection de {N1} U nb(N1), {N2} U nb(N2), {N3} U nb(N3), etc. | ||||
|   - Si cet ensemble contient au moins n éléments, il s'agit alors d'un sous-graphe connexe. | ||||
|     On peut ainsi en extraire les n premiers nœuds comme résultat | ||||
| """ | ||||
|  | ||||
|  | ||||
| def findTightlyLinkedConsoles(n: int, consoles: list[tuple[int, int]]) -> list[int]: | ||||
|     nodes: dict[int, set[int]] = {} | ||||
|  | ||||
|     for i1, i2 in consoles: | ||||
|         if i1 not in nodes: | ||||
|             nodes[i1] = set() | ||||
|         if i2 not in nodes: | ||||
|             nodes[i2] = set() | ||||
|         nodes[i1].add(i2) | ||||
|         nodes[i2].add(i1) | ||||
|  | ||||
|     while True: | ||||
|         removed: set[int] = set() | ||||
|         for node, neighbors in nodes.items(): | ||||
|             if len(neighbors) < n - 1: | ||||
|                 removed.add(node) | ||||
|  | ||||
|         if len(removed) == 0: | ||||
|             break | ||||
|  | ||||
|         new_nodes = {} | ||||
|         for node, neighbors in nodes.items(): | ||||
|             if node in removed: | ||||
|                 continue | ||||
|             new_nodes[node] = neighbors - removed | ||||
|  | ||||
|         nodes = new_nodes | ||||
|  | ||||
|         if len(nodes) < n: | ||||
|             return [] | ||||
|  | ||||
|     for node, neighbors in nodes.items(): | ||||
|         common: set[int] = neighbors | {node} | ||||
|         for nb in neighbors: | ||||
|             common = common.intersection(nodes[nb] | {nb}) | ||||
|  | ||||
|         if len(common) >= n: | ||||
|             return list(sorted(common))[:n] | ||||
|  | ||||
|     return [] | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     print(findTightlyLinkedConsoles(3, [(0, 1), (0, 4), (2, 1), (3, 1), (4, 2), (2, 3)]) == [1, 2, 3]) | ||||
|     print(findTightlyLinkedConsoles(4, [(0, 1), (0, 4), (2, 1), (3, 1), (4, 2), (2, 3)]) == []) | ||||
|     print(findTightlyLinkedConsoles(4, [(0, 1), (0, 4), (2, 1), (3, 1), (4, 2), (2, 3), (1, 4), (4, 3)]) == [1, 2, 3, 4]) | ||||
							
								
								
									
										75
									
								
								src/Ex4.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/Ex4.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
|  | ||||
| """ | ||||
| Nom/Prénom: Heredero/Louis | ||||
| Explications: | ||||
|  | ||||
| Une approche pour résoudre ce problème grâce à la programmation dynamique consiste | ||||
| à se représenter les différentes séquences comme un arbre de décisions, où chaque nœud | ||||
| représente une séquence, et chaque arête un coup supplémentaire | ||||
| On peut alors parcourir l'arbre en profondeur (DFS) jusqu'à trouver une séquence | ||||
| de longueur N et de valeur H (en élaguant les branches dépassant H ou N) | ||||
| On peut également mémoïser un certain nombre de valeurs : en effet, pour une même | ||||
| combinaison N, C, H, le résultat sera toujours le même. Cela optimise donc grandement | ||||
| le calcul récursif puisque cela évite de calculer les états partagés (sous-branches | ||||
| identiques) | ||||
|  | ||||
| Bonus : | ||||
| Nous cherchons la valeur N la plus petite telle que : | ||||
|   `computeNbrOfDifferentSequences(N, 5, 100) > 2^16` | ||||
| """ | ||||
|  | ||||
| mem: dict[tuple[int, int, int], int] = {} | ||||
|  | ||||
| def computeNbrOfDifferentSequences(N: int, C: int, H: int) -> int: | ||||
|     if N == 0: | ||||
|         if H == 0: | ||||
|             return 1 | ||||
|         return 0 | ||||
|  | ||||
|     if H < 0: | ||||
|         return 0 | ||||
|  | ||||
|     key: tuple[int, int, int] = (N, C, H) | ||||
|     if key in mem: | ||||
|         return mem[key] | ||||
|  | ||||
|     total: int = 0 | ||||
|     for c in range(1, C + 1): | ||||
|         total += computeNbrOfDifferentSequences(N - 1, C, H - c) | ||||
|  | ||||
|     mem[key] = total | ||||
|     return total | ||||
|  | ||||
|  | ||||
| def bonus(): | ||||
|     # Finding N2 | ||||
|     N1 = 100 | ||||
|     N2 = 1_000_000_000 | ||||
|     C = 5 | ||||
|     H = 100 | ||||
|     lim = 2 ** 16 | ||||
|  | ||||
|     while computeNbrOfDifferentSequences(N2, C, H) <= lim: | ||||
|         N2 *= 2 | ||||
|         print(f"*= 2 -> {N2}") | ||||
|  | ||||
|     print(f"{N2=}") | ||||
|  | ||||
|     # Finding N | ||||
|     while N2 - N1 > 1: | ||||
|         Nm = (N1 + N2) // 2 | ||||
|         value = computeNbrOfDifferentSequences(Nm, C, H) | ||||
|         if value == lim: | ||||
|             return Nm | ||||
|  | ||||
|         if value < lim: | ||||
|             N1 = Nm | ||||
|         else: | ||||
|             N2 = Nm | ||||
|  | ||||
|     return N2 | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     print(computeNbrOfDifferentSequences(1, 6, 3) == 1) | ||||
|     print(computeNbrOfDifferentSequences(2, 6, 7) == 6) | ||||
|     #print(bonus()) | ||||
							
								
								
									
										190
									
								
								src/Ex5.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								src/Ex5.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,190 @@ | ||||
| """ | ||||
| Nom/Prénom: Heredero/Louis | ||||
|  | ||||
| """ | ||||
| from typing import Optional | ||||
|  | ||||
|  | ||||
| #   UTILITY FUNCTIONS | ||||
| #       Those functions are provided as helpers, you do not have to use them | ||||
| #       The displayBoardSequence function will be used to visualize the results of your algorithms during the evaluation | ||||
|  | ||||
| def printPiece(piece:tuple[tuple[int,int], tuple[int,int]]) -> None: | ||||
|     print(piece[0][1], piece[1][1]) | ||||
|     print(piece[0][0], piece[1][0]) | ||||
|  | ||||
| def printBoard(board:list[list[int, int]]) -> None: | ||||
|  | ||||
|     if len(board) <= 0 or len(board[0]) <= 0: | ||||
|         print("Empty board -> skipping printing") | ||||
|         return None | ||||
|  | ||||
|     for i in range(len(board)-1): | ||||
|         if len(board[i]) != len(board[i+1]): | ||||
|             print("Board is not a rectangle --> skipping printing") | ||||
|             return None | ||||
|  | ||||
|     for y in range(len(board[0])-1, -1, -1): | ||||
|         line = "" | ||||
|         for x in range(len(board)): | ||||
|             line += str(board[x][y],) | ||||
|         print(line) | ||||
|  | ||||
|  | ||||
| def placeAt(loc, board, piece): | ||||
|     for x in range(2): | ||||
|         for y in range(2): | ||||
|             if piece[x][y] == 0: | ||||
|                 continue | ||||
|             board[loc[0] + x][loc[1] + y] = piece[x][y] | ||||
|  | ||||
|  | ||||
| def displayBoardSequence(board_width:int, board_height:int, pieces:list[tuple[tuple[int,int], tuple[int,int]]], placements:list[list[int, int]]): | ||||
|     """Display a sequence of move on the board by placing the blocs at the given locations | ||||
|         ==> /!\ This is an unsafe function that simply place the 1 ones of the given blocs at the given placements, it will crash while trying to place blocs outside the board | ||||
|         ==> /!\ it does not check the validity of the placement | ||||
|       => It's main use is for debugging | ||||
|     """ | ||||
|     board = [None] * board_width | ||||
|     for i in range(board_width): | ||||
|         board[i] = [0] * board_height | ||||
|  | ||||
|     for i in range(len(pieces)): | ||||
|         print( "----", i, "----") | ||||
|         print("Piece to place") | ||||
|         printPiece(pieces[i]) | ||||
|         print("Board state after placing piece @ "+ str(placements[i])) | ||||
|         placeAt(placements[i], board, pieces[i]) | ||||
|         printBoard(board) | ||||
|  | ||||
| """ | ||||
| Explications: | ||||
|  | ||||
| Une approche possible consiste à représenter la partie sous la forme d'un arbre | ||||
| dans lequel les nœuds sont les états de la grille et les arêtes sont les coups | ||||
| possibles (i.e. placement d'une pièce) | ||||
|  | ||||
| Ainsi, l'état initial serait celui de la grille vide, et chaque "niveau" de l'arbre | ||||
| représenterait une pièce différente pouvant être posée | ||||
|  | ||||
| On peut ensuite parcourir l'arbre en largeur (BFS) ce qui nous garantit de trouver une solution optimale en premier. | ||||
| On peut également le parcourir en profondeur (DFS) jusqu'à trouver une solution, puis en parcourant le reste de l'arbre | ||||
| à la recherche d'une meilleure solution. Cela permettrait également d'appliquer | ||||
| de la mémoïsation (principe de programmation dynamique) afin d'optimiser la recherche | ||||
|  | ||||
| Afin d'accélérer la recherche nous pouvons appliquer les optimisations suivantes: | ||||
| - élagage des branches inintéressantes : dès qu'une pièce laisse un espace vide sous elle, il devient impossible de le remplir | ||||
| - tri des branches selon la hauteur de la pièce une fois posée : prioriser les branches plaçant les pièces le plus bas possible | ||||
| - combinaisons de pièces en macro-blocs : combiner les pièces pouvant s'assembler (p.ex. coin et carré) | ||||
|  | ||||
| """ | ||||
|  | ||||
| def find_y(board: list[list[int]], height: int, piece: tuple[tuple[int,int], tuple[int,int]], x: int) -> int: | ||||
|     y_max = height - 2 | ||||
|     if piece[0][1] == 0 and piece[1][1] == 0: | ||||
|         y_max = height - 1 | ||||
|  | ||||
|     y_min = 0 | ||||
|     if piece[0][0] == 0 and piece[1][0] == 0: | ||||
|         y_min = -1 | ||||
|  | ||||
|     lowest = 0 | ||||
|     for y in range(y_max, y_min - 1, -1): | ||||
|         valid = True | ||||
|         for dy in range(2): | ||||
|             for dx in range(2): | ||||
|                 if piece[dx][dy] == 1 and board[x + dx][y + dy] == 1: | ||||
|                     valid = False | ||||
|                     break | ||||
|             if not valid: | ||||
|                 break | ||||
|  | ||||
|         if valid: | ||||
|             lowest = y | ||||
|         else: | ||||
|             break | ||||
|     return lowest | ||||
|  | ||||
| def copy_board(board: list[list[int]]) -> list[list[int]]: | ||||
|     new_board: list[list[int]] = [ | ||||
|         row.copy() | ||||
|         for row in board | ||||
|     ] | ||||
|     return new_board | ||||
|  | ||||
| def playMinitris( | ||||
|     board_width: int, | ||||
|     board_height: int, | ||||
|     board: list[list[int]], | ||||
|     pieces: list[tuple[tuple[int,int], tuple[int,int]]], | ||||
|     path: list[tuple[int, int]] | ||||
| ) -> Optional[list[tuple[int, int]]]: | ||||
|     if len(pieces) == 0: | ||||
|         for y in range(board_height): | ||||
|             row = [] | ||||
|             for x in range(board_width): | ||||
|                 row.append(board[x][y]) | ||||
|  | ||||
|             if row != [row[0]] * board_width: | ||||
|                 return None | ||||
|         return path | ||||
|  | ||||
|     first_piece: tuple[tuple[int, int], tuple[int, int]] = pieces[0] | ||||
|     rest: list[tuple[tuple[int, int], tuple[int, int]]] = pieces[1:] | ||||
|  | ||||
|     x_min = 0 | ||||
|     if first_piece[0][0] == 0 and first_piece[0][1] == 0: | ||||
|         x_min = -1 | ||||
|  | ||||
|     x_max = board_width - 2 | ||||
|     if first_piece[1][0] == 0 and first_piece[1][1] == 0: | ||||
|         x_max = board_width - 1 | ||||
|  | ||||
|     for x in range(x_min, x_max + 1): | ||||
|         y = find_y(board, board_height, first_piece, x) | ||||
|         new_board = copy_board(board) | ||||
|         pos = (x, y) | ||||
|         path2 = path + [pos] | ||||
|         placeAt(pos, new_board, first_piece) | ||||
|  | ||||
|         # If space below piece | ||||
|         if first_piece[0][1] == 1 and first_piece[1][1] and first_piece[0][0] != first_piece[1][0]: | ||||
|             if new_board[x][y] == 0 or new_board[x + 1][y] == 0: | ||||
|                 return None | ||||
|  | ||||
|         res: Optional[list[tuple[int, int]]] = playMinitris(board_width, board_height, new_board, rest, path2) | ||||
|         if res is not None: | ||||
|             return res | ||||
|  | ||||
|     return None | ||||
|  | ||||
| #   This is the function to fill | ||||
| def playMinitrisFastAndWell( | ||||
|     board_width: int, | ||||
|     board_height: int, | ||||
|     pieces: list[tuple[tuple[int,int], tuple[int,int]]] | ||||
| ) -> list[list[int, int]]: | ||||
|     board: list[list[int]] = [ | ||||
|         [0] * board_height | ||||
|         for _ in range(board_width) | ||||
|     ] | ||||
|     result: Optional[list[tuple[int, int]]] = playMinitris( | ||||
|         board_width, | ||||
|         board_height, | ||||
|         board, | ||||
|         pieces, | ||||
|         [] | ||||
|     ) | ||||
|     if result is None: | ||||
|         return [] | ||||
|     return list(map(list, result)) | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     displayBoardSequence(3, 4, [((1, 0), (0, 0)), ((1, 0), (0, 0)), ((1, 0), (0, 0))], [(0, 0), (1, 0), (2, 0)]) | ||||
|     displayBoardSequence(3, 4, [((1, 0), (0, 0)), ((1, 1), (0, 1)), ((1, 1), (0, 0))], [(1, 0), (0, 0), (2, 0)]) | ||||
|  | ||||
|     w = 3 | ||||
|     h = 4 | ||||
|     pieces = [((1, 0), (0, 0)), ((1, 0), (0, 0)), ((1, 0), (0, 0))] | ||||
|     seq = playMinitrisFastAndWell(w, h, pieces) | ||||
|     displayBoardSequence(w, h, pieces, seq) | ||||
| @@ -1,8 +1,26 @@ | ||||
| import unittest | ||||
|  | ||||
| from src.Ex1 import countKey | ||||
|  | ||||
|  | ||||
| class MyTestCase(unittest.TestCase): | ||||
|     def test_simple1(self): | ||||
|         pass | ||||
|         self.assertEqual( | ||||
|             countKey([-3, 4, 2, 8, 9, 1, -3, -8, -4, 2, 8, 2, -8, 1, 3]), | ||||
|             4 | ||||
|         ) | ||||
|  | ||||
|     def test_simple2(self): | ||||
|         self.assertEqual( | ||||
|             countKey([-4, 5, 6, -1500, 4, 7, 49, 60, -60, 82, -41, 1500, 1, -7]), | ||||
|             4 | ||||
|         ) | ||||
|  | ||||
|     def test_simple3(self): | ||||
|         self.assertEqual( | ||||
|             countKey([-3, -2, -1, 1, 2, 5]), | ||||
|             2 | ||||
|         ) | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     unittest.main() | ||||
|   | ||||
| @@ -1,8 +1,20 @@ | ||||
| import unittest | ||||
|  | ||||
| from src.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() | ||||
|   | ||||
| @@ -1,8 +1,27 @@ | ||||
| import unittest | ||||
|  | ||||
| from src.Ex3 import findTightlyLinkedConsoles | ||||
|  | ||||
|  | ||||
| class MyTestCase(unittest.TestCase): | ||||
|     def test_simple1(self): | ||||
|         pass | ||||
|         self.assertEqual( | ||||
|             findTightlyLinkedConsoles(3,[(0,1),(0,4),(2,1),(3,1),(4,2),(2,3)]), | ||||
|             [1,2,3] | ||||
|         ) | ||||
|  | ||||
|     def test_simple2(self): | ||||
|         self.assertEqual( | ||||
|             findTightlyLinkedConsoles(4,[(0,1),(0,4),(2,1),(3,1),(4,2),(2,3)]), | ||||
|             [] | ||||
|         ) | ||||
|  | ||||
|     def test_simple3(self): | ||||
|         self.assertEqual( | ||||
|             findTightlyLinkedConsoles(4,[(0,1),(0,4),(2,1),(3,1),(4,2),(2,3),(1,4),(4,3)]), | ||||
|             [1,2,3,4] | ||||
|         ) | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     unittest.main() | ||||
|   | ||||
| @@ -1,8 +1,21 @@ | ||||
| import unittest | ||||
|  | ||||
| from src.Ex4 import computeNbrOfDifferentSequences | ||||
|  | ||||
|  | ||||
| class MyTestCase(unittest.TestCase): | ||||
|     def test_simple1(self): | ||||
|         pass | ||||
|         self.assertEqual( | ||||
|             computeNbrOfDifferentSequences(1, 6, 3), | ||||
|             1 | ||||
|         ) | ||||
|  | ||||
|     def test_simple2(self): | ||||
|         self.assertEqual( | ||||
|             computeNbrOfDifferentSequences(2, 6, 7), | ||||
|             6 | ||||
|         ) | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     unittest.main() | ||||
|   | ||||
| @@ -1,8 +1,21 @@ | ||||
| import unittest | ||||
|  | ||||
| from src.Ex5 import playMinitrisFastAndWell | ||||
|  | ||||
|  | ||||
| class MyTestCase(unittest.TestCase): | ||||
|     def test_simple1(self): | ||||
|         pass | ||||
|         self.assertEqual( | ||||
|             playMinitrisFastAndWell(3, 4, [((1, 0), (0, 0)), ((1, 0), (0, 0)), ((1, 0), (0, 0))]), | ||||
|             [[0, 0], [1, 0], [2, 0]] | ||||
|         ) | ||||
|  | ||||
|     def test_simple2(self): | ||||
|         self.assertEqual( | ||||
|             playMinitrisFastAndWell(3, 4, [((1, 0), (0, 0)), ((1, 1), (0, 1)), ((1, 1), (0, 0))]), | ||||
|             [[1, 0], [0, 0], [2, 0]] | ||||
|         ) | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     unittest.main() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user