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
|
||||
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é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
|
||||
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] = {}
|
||||
@ -37,6 +41,35 @@ def computeNbrOfDifferentSequences(N: int, C: int, H: int) -> int:
|
||||
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,21 @@
|
||||
import unittest
|
||||
|
||||
from 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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user