Compare commits
	
		
			5 Commits
		
	
	
		
			7312f8a293
			...
			2fda876df3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2fda876df3 | |||
| 68ccc3c183 | |||
| 3c100a08fe | |||
| 9c6bf54f20 | |||
| 70daa208d0 | 
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							| @@ -36,7 +36,7 @@ def function1( | |||||||
| </tr> | </tr> | ||||||
| </table> | </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) | [Tests](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/tests/test_ex1.py) | ||||||
|  |  | ||||||
| @@ -46,7 +46,7 @@ def function1( | |||||||
| <table> | <table> | ||||||
| <tr> | <tr> | ||||||
| <td><strong>But</strong></td> | <td><strong>But</strong></td> | ||||||
| <td></td> | <td>...</td> | ||||||
| </tr> | </tr> | ||||||
| <tr> | <tr> | ||||||
| <td><strong>Input</strong></td> | <td><strong>Input</strong></td> | ||||||
| @@ -70,7 +70,7 @@ def function2( | |||||||
| </tr> | </tr> | ||||||
| </table> | </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) | [Tests](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/tests/test_ex2.py) | ||||||
|  |  | ||||||
| @@ -104,7 +104,7 @@ def function3( | |||||||
| </tr> | </tr> | ||||||
| </table> | </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) | [Tests](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/tests/test_ex3.py) | ||||||
|  |  | ||||||
| @@ -138,7 +138,7 @@ def function4( | |||||||
| </tr> | </tr> | ||||||
| </table> | </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) | [Tests](https://git.kb28.ch/HEL/AlgoDS-Examen2025/src/branch/main/tests/test_ex4.py) | ||||||
|  |  | ||||||
| @@ -172,6 +172,6 @@ def function5( | |||||||
| </tr> | </tr> | ||||||
| </table> | </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) | [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œ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]) | ||||||
							
								
								
									
										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 des ses voisins (et eux-mêmes)' | ||||||
|  |     C'est-à-dire, si N1 et un nœuds 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 c'est 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 | import unittest | ||||||
|  |  | ||||||
|  | from Ex1 import countKey | ||||||
|  |  | ||||||
|  |  | ||||||
| class MyTestCase(unittest.TestCase): | class MyTestCase(unittest.TestCase): | ||||||
|     def test_simple1(self): |     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__': | if __name__ == '__main__': | ||||||
|     unittest.main() |     unittest.main() | ||||||
|   | |||||||
| @@ -1,8 +1,20 @@ | |||||||
| import unittest | import unittest | ||||||
|  |  | ||||||
|  | from Ex2 import findSafestPath | ||||||
|  |  | ||||||
|  |  | ||||||
| class MyTestCase(unittest.TestCase): | class MyTestCase(unittest.TestCase): | ||||||
|     def test_simple1(self): |     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__': | if __name__ == '__main__': | ||||||
|     unittest.main() |     unittest.main() | ||||||
|   | |||||||
| @@ -1,8 +1,27 @@ | |||||||
| import unittest | import unittest | ||||||
|  |  | ||||||
|  | from Ex3 import findTightlyLinkedConsoles | ||||||
|  |  | ||||||
|  |  | ||||||
| class MyTestCase(unittest.TestCase): | class MyTestCase(unittest.TestCase): | ||||||
|     def test_simple1(self): |     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__': | if __name__ == '__main__': | ||||||
|     unittest.main() |     unittest.main() | ||||||
|   | |||||||
| @@ -1,8 +1,21 @@ | |||||||
| import unittest | import unittest | ||||||
|  |  | ||||||
|  | from Ex4 import computeNbrOfDifferentSequences | ||||||
|  |  | ||||||
|  |  | ||||||
| class MyTestCase(unittest.TestCase): | class MyTestCase(unittest.TestCase): | ||||||
|     def test_simple1(self): |     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__': | if __name__ == '__main__': | ||||||
|     unittest.main() |     unittest.main() | ||||||
|   | |||||||
| @@ -1,8 +1,21 @@ | |||||||
| import unittest | import unittest | ||||||
|  |  | ||||||
|  | from Ex5 import playMinitrisFastAndWell | ||||||
|  |  | ||||||
|  |  | ||||||
| class MyTestCase(unittest.TestCase): | class MyTestCase(unittest.TestCase): | ||||||
|     def test_simple1(self): |     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__': | if __name__ == '__main__': | ||||||
|     unittest.main() |     unittest.main() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user