mirror of
https://github.com/Fare-spec/cours.git
synced 2025-12-07 10:50:36 +00:00
TP8
This commit is contained in:
1
university/tp8/.python-version
Normal file
1
university/tp8/.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.13
|
||||||
0
university/tp8/README.md
Normal file
0
university/tp8/README.md
Normal file
177
university/tp8/board.py
Normal file
177
university/tp8/board.py
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
from random import sample
|
||||||
|
from typing import List, Tuple
|
||||||
|
|
||||||
|
Coord = Tuple[int, int]
|
||||||
|
Grid = List[List[int]]
|
||||||
|
GridBool = List[List[bool]]
|
||||||
|
|
||||||
|
|
||||||
|
def gen_grid(n: int, m: int, v: int = 0) -> Grid:
|
||||||
|
"""
|
||||||
|
Create a list of list to represent the board
|
||||||
|
"""
|
||||||
|
return [[v for _ in range(m)] for _ in range(n)]
|
||||||
|
|
||||||
|
|
||||||
|
def place_mine(grid, n):
|
||||||
|
h = len(grid)
|
||||||
|
w = len(grid[0])
|
||||||
|
total = h * w
|
||||||
|
|
||||||
|
indices = sample(range(total), n)
|
||||||
|
|
||||||
|
for k in indices:
|
||||||
|
i = k // w
|
||||||
|
j = k % w
|
||||||
|
grid[i][j] = 1
|
||||||
|
|
||||||
|
|
||||||
|
def get_neighbours(coord: Coord, grid: Grid) -> List[Coord]:
|
||||||
|
"""
|
||||||
|
Returns all the neighbors of a cell (y, x),
|
||||||
|
with y being the vertical axis and x the horizontal one.
|
||||||
|
Includes diagonals, excludes the cell itself.
|
||||||
|
Returns (y, x) pairs (row index, then column index).
|
||||||
|
"""
|
||||||
|
y, x = coord
|
||||||
|
max_y = len(grid) - 1
|
||||||
|
max_x = len(grid[0]) - 1
|
||||||
|
|
||||||
|
neighbours: List[Coord] = []
|
||||||
|
|
||||||
|
for ny in range(y - 1, y + 2):
|
||||||
|
for nx in range(x - 1, x + 2):
|
||||||
|
if ny == y and nx == x:
|
||||||
|
continue # skip the cell itself
|
||||||
|
if 0 <= ny <= max_y and 0 <= nx <= max_x:
|
||||||
|
neighbours.append((ny, nx))
|
||||||
|
|
||||||
|
return neighbours
|
||||||
|
|
||||||
|
|
||||||
|
def test_mine(grid: Grid, coord: Coord) -> bool:
|
||||||
|
"""Return True if there is a mine at coord in the grid."""
|
||||||
|
y, x = coord
|
||||||
|
return grid[y][x] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def count_near_mines(grid: Grid, coord: Coord) -> int:
|
||||||
|
"""Return the number of mines surrounding the cell at coord."""
|
||||||
|
count = 0
|
||||||
|
nearest_cells = get_neighbours(coord, grid)
|
||||||
|
for y, x in nearest_cells:
|
||||||
|
if grid[y][x] == 1:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
def display_mines(grid: Grid) -> None:
|
||||||
|
"""Print the complete mine grid: '*' for mines, '-' for empty."""
|
||||||
|
for row in grid:
|
||||||
|
for cell in row:
|
||||||
|
print("*" if cell == 1 else "-", end=" ")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def display_known(grid: Grid, known: GridBool) -> None:
|
||||||
|
"""
|
||||||
|
Print what the player currently knows:
|
||||||
|
- '?' for unknown cells
|
||||||
|
- number of adjacent mines for known empty cells
|
||||||
|
- '*' for known mines (optional but usually useful)
|
||||||
|
"""
|
||||||
|
for y, row in enumerate(grid):
|
||||||
|
for x, cell in enumerate(row):
|
||||||
|
if not known[y][x]:
|
||||||
|
print("?", end=" ")
|
||||||
|
else:
|
||||||
|
if cell == 1:
|
||||||
|
print("*", end=" ")
|
||||||
|
else:
|
||||||
|
mines = count_near_mines(grid, (y, x))
|
||||||
|
print(mines, end=" ")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
def get_coords(known_grid: GridBool) -> Coord:
|
||||||
|
"""
|
||||||
|
Get coordinates from the user
|
||||||
|
"""
|
||||||
|
max_y = len(known_grid) - 1
|
||||||
|
max_x = len(known_grid[0]) - 1
|
||||||
|
|
||||||
|
guess_x = -1
|
||||||
|
guess_y = -1
|
||||||
|
|
||||||
|
while True:
|
||||||
|
while not (0 <= guess_x <= max_x):
|
||||||
|
guess_x = int(input(f"Enter x between 0 and {max_x}: "))
|
||||||
|
|
||||||
|
while not (0 <= guess_y <= max_y):
|
||||||
|
guess_y = int(input(f"Enter y between 0 and {max_y}: "))
|
||||||
|
|
||||||
|
if not known_grid[guess_y][guess_x]:
|
||||||
|
return (guess_y, guess_x)
|
||||||
|
|
||||||
|
guess_x = -1
|
||||||
|
guess_y = -1
|
||||||
|
|
||||||
|
|
||||||
|
def is_end(known_grid: GridBool, grid: Grid) -> bool:
|
||||||
|
"""
|
||||||
|
return True if all cells has been discovered
|
||||||
|
"""
|
||||||
|
for y, row in enumerate(known_grid):
|
||||||
|
for x, known in enumerate(row):
|
||||||
|
if not test_mine(grid, (y, x)) and not known:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def linked_cells(grid: Grid, known_grid: GridBool, coord: Coord) -> list[Coord]:
|
||||||
|
"""
|
||||||
|
Return all cells that must be revealed when the player opens `coord`,
|
||||||
|
assuming:
|
||||||
|
- `coord` is not a mine,
|
||||||
|
- `coord` is currently unknown,
|
||||||
|
- we must emulate Minesweeper expansion.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- If `coord` has at least one adjacent mine: return only [coord].
|
||||||
|
- If `coord` has zero adjacent mines:
|
||||||
|
* reveal the entire connected region of zero-mine cells,
|
||||||
|
* plus all numbered border cells around that region.
|
||||||
|
The function returns coordinates only; it does not modify `known_grid`.
|
||||||
|
"""
|
||||||
|
y0, x0 = coord
|
||||||
|
frontier: list[Coord] = [coord]
|
||||||
|
visited: set[Coord] = set()
|
||||||
|
result: list[Coord] = []
|
||||||
|
|
||||||
|
while frontier:
|
||||||
|
cy, cx = frontier.pop()
|
||||||
|
if (cy, cx) in visited:
|
||||||
|
continue
|
||||||
|
visited.add((cy, cx))
|
||||||
|
|
||||||
|
mines_around = count_near_mines(grid, (cy, cx))
|
||||||
|
result.append((cy, cx))
|
||||||
|
|
||||||
|
if mines_around == 0:
|
||||||
|
for ny, nx in get_neighbours((cy, cx), grid):
|
||||||
|
if (ny, nx) not in visited and not known_grid[ny][nx]:
|
||||||
|
frontier.append((ny, nx))
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
grid = gen_grid(7, 5, 0)
|
||||||
|
place_mine(grid, 10)
|
||||||
|
|
||||||
|
known = [[False for _ in range(5)] for _ in range(7)]
|
||||||
|
known[1][1] = True
|
||||||
|
|
||||||
|
display_mines(grid)
|
||||||
|
print()
|
||||||
|
display_known(grid, known)
|
||||||
0
university/tp8/drawer.py
Normal file
0
university/tp8/drawer.py
Normal file
67
university/tp8/main.py
Normal file
67
university/tp8/main.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
from mailbox import linesep
|
||||||
|
|
||||||
|
import board as bd
|
||||||
|
|
||||||
|
|
||||||
|
def creerGrille(N, M, v=0):
|
||||||
|
return bd.gen_grid(N, M, v)
|
||||||
|
|
||||||
|
|
||||||
|
def placerMine(grid, X):
|
||||||
|
bd.place_mine(grid, X)
|
||||||
|
|
||||||
|
|
||||||
|
def TestMine(grid, i, j):
|
||||||
|
return bd.test_mine(grid, (i, j))
|
||||||
|
|
||||||
|
|
||||||
|
def compteMinesVoisines(positionsMines, i, j):
|
||||||
|
return bd.get_neighbours((i, j), positionsMines)
|
||||||
|
|
||||||
|
|
||||||
|
def afficheSolution(positionsMines):
|
||||||
|
bd.display_mines(positionsMines)
|
||||||
|
|
||||||
|
|
||||||
|
def afficheJeu(positionsMines, casesDevoilees):
|
||||||
|
bd.display_known(positionsMines, casesDevoilees)
|
||||||
|
|
||||||
|
|
||||||
|
def getCoords(known_grid, N, M):
|
||||||
|
return bd.get_coords(known_grid)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
N, M = 8, 8
|
||||||
|
grid = creerGrille(N, M)
|
||||||
|
mines_number = 0
|
||||||
|
while not (0 < mines_number <= N * M - 1):
|
||||||
|
mines_number = int(
|
||||||
|
input(f"Please input a number of mines between 0-{N * M - 1}: ")
|
||||||
|
)
|
||||||
|
# In theory we should put the line below after the first pick on the board or else the user could loose on their first attempt
|
||||||
|
placerMine(grid, mines_number)
|
||||||
|
known_grid = [[False for _ in range(N)] for _ in range(M)]
|
||||||
|
going = True
|
||||||
|
while going:
|
||||||
|
afficheJeu(grid, known_grid)
|
||||||
|
y, x = getCoords(known_grid, N, M)
|
||||||
|
known_grid[y][x] = True
|
||||||
|
# If the user lost:
|
||||||
|
if TestMine(grid, y, x):
|
||||||
|
print("You lost")
|
||||||
|
afficheJeu(grid, known_grid)
|
||||||
|
print("Here is the solution: ")
|
||||||
|
afficheSolution(grid)
|
||||||
|
going = False
|
||||||
|
# If he won
|
||||||
|
elif bd.is_end(known_grid, grid):
|
||||||
|
print("Congrats ! You've just won")
|
||||||
|
going = False
|
||||||
|
else:
|
||||||
|
linked = bd.linked_cells(grid, known_grid, (y, x))
|
||||||
|
for coord in linked:
|
||||||
|
known_grid[coord[1]][coord[0]] = True
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
7
university/tp8/pyproject.toml
Normal file
7
university/tp8/pyproject.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[project]
|
||||||
|
name = "tp8"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
dependencies = []
|
||||||
8
university/tp8/uv.lock
generated
Normal file
8
university/tp8/uv.lock
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tp8"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
Reference in New Issue
Block a user