LordBaryhobal 2fda876df3
Some checks failed
Python unit tests / unittests (push) Failing after 5s
added ex5
2025-01-28 16:52:27 +01:00

191 lines
6.4 KiB
Python

"""
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)