Skip to content
Snippets Groups Projects
Commit 86668bc2 authored by Angy Wallot's avatar Angy Wallot
Browse files

Upload New File

parent 86ed63f4
Branches
No related tags found
No related merge requests found
#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
:mod:`minesweeper` module
:author: KINADINOVA Dariya
:date: 14/02/24
This module provides functions and a class for minesweeper's game's management.
"""
import random
from enum import Enum
from cell import Cell
################################################
# Type declaration
################################################
class GameState(Enum):
"""
A class to define an enumerated type with three values :
* ``winning``
* ``losing``
* ``unfinished``
for the three state of minesweeper game.
"""
winning = 1
losing = 2
unfinished = 3
##############################################
# Function for game's setup and management
##############################################
def neighborhood(x: int, y: int, width: int, height: int) -> list[tuple[int, int]]:
"""
return the list of coordinates of the neighbors of position (x,y) in a
grid of size width*height
précondition: 0 <= x < width and 0 <= y < height
examples:
$$$ neighborhood(3, 3, 10, 10)
[(2, 2), (2, 3), (2, 4), (3, 2), (3, 4), (4, 2), (4, 3), (4, 4)]
$$$ neighborhood(0, 3, 10, 10)
[(0, 2), (0, 4), (1, 2), (1, 3), (1, 4)]
$$$ neighborhood(0, 0, 10, 10)
[(0, 1), (1, 0), (1, 1)]
$$$ neighborhood(9, 9, 10, 10)
[(8, 8), (8, 9), (9, 8)]
$$$ neighborhood(3, 9, 10, 10)
[(2, 8), (2, 9), (3, 8), (4, 8), (4, 9)]
"""
res = [ (xn, yn) for xn, yn in [(x-1, y-1), (x-1, y), (x-1, y+1),
(x, y-1), (x, y+1),(x+1, y-1), (x+1, y),
(x+1, y+1)]
if 0 <= xn < width and 0 <=yn < height]
return res
class Minesweeper():
"""
$$$ game = Minesweeper(20, 10, 4)
$$$ game.width
20
$$$ game.height
10
$$$ game.nbombs
4
$$$ game.state == GameState.unfinished
True
$$$ cel = game.get_cell(1, 2)
$$$ cel.is_revealed
False
$$$
"""
def __init__(self, width: int=30, height: int=20, nbombs: int=99):
"""
build a minesweeper grid of size `width*height` of cells
with `nbombs` bombs randomly placed.
for each build cell, this constructor sets the number of bombs
in neighborhood.
precondition: width and height must be positive integers, and
nbombs <= width * height
example:
$$$ game = Minesweeper(20, 10, 4)
$$$ game.width
20
$$$ game.height
10
$$$ game.nbombs
4
$$$ game.state == GameState.unfinished
True
"""
self.width = width
self.height = height
self.nbombs = nbombs
self.state = GameState.unfinished
self.grid = [[Cell() for x in range(width)] for y in range(height)]
bombs = random.sample(range(width*height), nbombs)
for i in bombs:
x, y = (i % width), i // width
self._put_a_bomb_at(x, y)
def get_cell(self, x: int, y: int) -> Cell:
"""
return: the cell of coordinates (x,y) in the game's grid
precondition: 0 <= x < width of game and O <= y < height of game
$$$ game = Minesweeper(20, 10, 4)
$$$ sum(1 for x in range(20) for y in range(10) if game.get_cell(x, y).is_bomb)
4
"""
return self.grid[y][x]
def _put_a_bomb_at(self, x: int, y: int):
"""
this method change the cells's state at x, y to a bomb,
if the cell is already a bomb, nothing happens.
otherwise it changes cell's state and increments by one every
cells in its neighborhood.
precondition: 0 <= x < self.width and 0 <= y < self.height
exemples:
$$$ game = Minesweeper(10, 5, 0)
$$$ voisins = neighborhood(1, 1, game.width, game.height)
$$$ all(game.get_cell(x, y).nbombs_in_neighborhood == 0 for x, y in voisins)
True
$$$ game._put_a_bomb_at(1, 1)
$$$ all(game.get_cell(x, y).nbombs_in_neighborhood == 1 for x, y in voisins)
True
$$$ game._put_a_bomb_at(1, 1)
$$$ all(game.get_cell(x, y).nbombs_in_neighborhood == 1 for x, y in voisins)
True
"""
if self.get_cell(x, y).is_bomb == False:
self.get_cell(x, y).set_bomb()
for i, j in neighborhood(x, y, self.width, self.height):
self.get_cell(i, j).incr_number_of_bombs_in_neighborhood()
def all_cells_are_revealed_or_bomb(self) -> bool:
"""
return True iff all cells are revealed or bomb.
précondition: none
"""
for i in range(self.width):
for j in range(self.height):
self.get_cell(i, j)
if not self.get_cell(i, j).is_bomb and not self.get_cell(i, j).is_revealed:
return False
return True
def reveal_all_cells_from(self, x, y):
"""
recursively reveal all cells of game game from the initial cell (x,y).
* if the cell is a bomb one, update game's state to losing.
* otherwise if the cell's neighborhood doesn't contains bomb,
recursively reveal all neighboors.
* and finally, if all cell's are revealed, update game's state to
winning
precondition: 0 <= x < width of game and O <= y < height of game
exemples:
$$$ game = Minesweeper(20, 10, 0)
$$$ game._put_a_bomb_at(1, 1)
$$$ game.state
GameState.unfinished
$$$ game.all_cells_are_revealed_or_bomb()
False
$$$ game.reveal_all_cells_from(5, 5)
$$$ game.all_cells_are_revealed_or_bomb()
False
$$$ game.reveal_all_cells_from(0, 0)
$$$ game.reveal_all_cells_from(1, 0)
$$$ game.reveal_all_cells_from(0, 1)
$$$ game.all_cells_are_revealed_or_bomb()
True
$$$ game.state
GameState.winning
$$$ game = Minesweeper(20, 10, 0)
$$$ game._put_a_bomb_at(1, 1)
$$$ game.reveal_all_cells_from(1, 1)
$$$ game.state
GameState.losing
"""
if self.get_cell(x, y).is_bomb:
self.state = GameState.losing
elif not self.get_cell(x, y).is_revealed:
self.get_cell(x, y).reveal()
if self.get_cell(x, y).nbombs_in_neighborhood == 0:
for i, j in neighborhood(x, y, self.width, self.height):
self.reveal_all_cells_from(i, j)
else:
self.all_cells_are_revealed_or_bomb()
self.state = GameState.winning
# game = Minesweeper(20, 10, 4)
# game.state == GameState.unfinished
if (__name__ == '__main__'):
import apl1test
apl1test.testmod('minesweeper.py')
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment