This commit is contained in:
		
							
								
								
									
										35
									
								
								src/Ex4.py
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								src/Ex4.py
									
									
									
									
									
								
							| @@ -8,10 +8,14 @@ Une approche pour résoudre ce problème grâce à la programmation dynamique co | |||||||
| représente une séquence, et chaque arête un coup supplémentaire | 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 | 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) | de longueur N et de valeur H (en élaguant les branches dépassant H ou N) | ||||||
| On peut également mémoiser un certain nombre de valeurs : en effet, pour une même | 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 | 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 | le calcul récursif puisque cela évite de calculer les états partagés (sous-branches | ||||||
| identiques) | 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] = {} | mem: dict[tuple[int, int, int], int] = {} | ||||||
| @@ -37,6 +41,35 @@ def computeNbrOfDifferentSequences(N: int, C: int, H: int) -> int: | |||||||
|     return 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__': | if __name__ == '__main__': | ||||||
|     print(computeNbrOfDifferentSequences(1, 6, 3) == 1) |     print(computeNbrOfDifferentSequences(1, 6, 3) == 1) | ||||||
|     print(computeNbrOfDifferentSequences(2, 6, 7) == 6) |     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,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