This commit is contained in:
parent
68ccc3c183
commit
2fda876df3
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()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user