-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Closed as duplicate of#3512
Labels
Description
Pylint is giving me an error saying that two of my private classes don't have enough public methods. These classes are used for a very complex primary class, and these methods are there for a single primary method for each of these secondary classes. I could have put everything in a single function, but it was just unreadable and huge. I was wondering if this was intentional or if it was a bug, because I don't think it makes much sense to include this error for private classes. In any case, here is the program:
(PS: The error triger the class _MineManager
and the class _RevealManager
)
"""Manage the grid of cells in a Minesweeper game.
This module defines the `Grid` class, which manages the cells of the Minesweeper game,
including mine placement and revealing cells. It also includes managers for mine
placement and cell revealing.
"""
import secrets
from typing import Self
import pygame
from cell import Cell
class _MineManager: # The first error is here
"""Manager for mine placement and counting around cells.
This class handles the placement of mines on the grid, ensuring that a specified
cell is safe (does not contain a mine) and that the number of mines placed matches
the specified count. It also counts the number of mines around each cell after
mines are placed.
Attributes:
_grid (Grid): The grid containing the cells.
_cells (list[list[Cell]]): A 2D list of Cell objects representing the grid.
"""
def __init__(self: Self, grid: "Grid") -> None:
self._grid: Grid = grid
self._cells: list[list[Cell]] = grid.cells
def place_mines(self: Self, safe_x: int, safe_y: int) -> None:
"""Place mines on the grid, ensuring that the cell at (safe_x, safe_y) is safe.
This method places mines randomly on the grid, avoiding the cell at (safe_x,
safe_y) and its adjacent cells. It ensures that the number of mines placed
equals the specified mines_count in the grid.
Args:
safe_x (int): The x-coordinate of the cell that should not contain a mine.
safe_y (int): The y-coordinate of the cell that should not contain a mine.
"""
forbidden: set[tuple[int, int]] = {
(pos_x, pos_y)
for pos_x in range(safe_x - 1, safe_x + 2)
for pos_y in range(safe_y - 1, safe_y + 2)
if 0 <= pos_x < self._grid.cell_num and 0 <= pos_y < self._grid.cell_num
}
candidates: list[tuple[int, int]] = [
(pos_x, pos_y)
for pos_x in range(self._grid.cell_num)
for pos_y in range(self._grid.cell_num)
if (pos_x, pos_y) not in forbidden
]
cell: Cell
for pos_x, pos_y in secrets.SystemRandom().sample(
candidates, self._grid.mines_count
):
cell = self._cells[pos_x][pos_y]
cell.cellstate.is_mine = True
for pos_x in range(self._grid.cell_num):
for pos_y in range(self._grid.cell_num):
cell = self._cells[pos_x][pos_y]
cell.cellstate.mines_around = self._count_mines_around(pos_x, pos_y)
def _count_mines_around(self: Self, pos_x: int, pos_y: int) -> int:
"""Count the number of mines around a given cell.
This method counts the number of mines in the adjacent cells of the cell at
(pos_x, pos_y). It checks all eight surrounding cells and returns the count.
Args:
pos_x (int): The x-coordinate of the cell to check.
pos_y (int): The y-coordinate of the cell to check.
Returns:
int: The number of mines around the cell at (pos_x, pos_y).
"""
cells: list[list[Cell]] = self._cells
return sum(
cells[nx][ny].cellstate.is_mine
for dx in range(-1, 2)
for dy in range(-1, 2)
if 0 <= (nx := pos_x + dx) < self._grid.cell_num
and 0 <= (ny := pos_y + dy) < self._grid.cell_num
)
class _RevealManager: # The second error is here.
"""Manager for revealing cells in the Minesweeper game.
This class handles the logic for revealing cells when a cell is clicked. It manages
the game state, including mine placement and checking win conditions.
Attributes:
_grid (Grid): The grid containing the cells.
_cells (list[list[Cell]]): A 2D list of Cell objects representing the grid.
_mines_placed (bool): Flag indicating if mines have been placed on the grid.
"""
def __init__(self: Self, grid: "Grid") -> None:
self._grid: Grid = grid
self._cells: list[list[Cell]] = grid.cells
self._mines_placed: bool = False
def reveal_cell(self: Self, pos_x: int, pos_y: int) -> bool:
"""Reveal a cell at the specified position.
This method reveals the cell at (pos_x, pos_y). If the cell is a mine, it
returns True, indicating a mine was hit. If the cell is not a mine and has no
adjacent mines, it reveals all adjacent.
Args:
pos_x (int): x-coordinate of the cell to reveal.
pos_y (int): y-coordinate of the cell to reveal.
Returns:
bool: True if a mine was hit, False otherwise.
"""
cell: Cell = self._cells[pos_x][pos_y]
if not self._mines_placed:
self._grid.mines.place_mines(pos_x, pos_y)
self._mines_placed = True
if cell.reveal():
return True
if cell.cellstate.mines_around == 0:
self._reveal_adjacent(pos_x, pos_y)
return False
def _reveal_adjacent(self: Self, pos_x: int, pos_y: int) -> None:
"""Reveal all adjacent cells around the specified position.
This method reveals all cells in the 3x3 area around the cell at (pos_x, pos_y),
excluding the cell itself. It checks if each adjacent cell can be revealed
(not a mine and not already revealed) before revealing it.
Args:
pos_x (int): x-coordinate of the cell to check.
pos_y (int): y-coordinate of the cell to check.
"""
for dx in (-1, 0, 1):
for dy in (-1, 0, 1):
nx, ny = pos_x + dx, pos_y + dy
if self._can_reveal(nx, ny):
self._reveal_cell(nx, ny)
def _reveal_all_mines(self: Self) -> None:
"""Reveal all mines on the grid.
This method reveals all cells that contain mines. It is typically called when
the player hits a mine, to show all mines on the grid.
"""
for row in self._grid.cells:
for cell in row:
if cell.cellstate.is_mine:
cell.reveal()
def _can_reveal(self: Self, pos_x: int, pos_y: int) -> bool:
"""Check if a cell can be revealed.
This method checks if the cell at (pos_x, pos_y) can be revealed. A cell can be
revealed if it is within the bounds of the grid, not already revealed, and not a
mine.
Args:
pos_x (int): x-coordinate of the cell to check.
pos_y (int): y-coordinate of the cell to check.
Returns:
bool: True if the cell can be revealed, False otherwise.
"""
in_bounds_x: bool = 0 <= pos_x < self._grid.cell_num
in_bounds_y: bool = 0 <= pos_y < self._grid.cell_num
if not (in_bounds_x and in_bounds_y):
return False
cell: Cell = self._grid.cells[pos_x][pos_y]
return not cell.cellstate.is_revealed and not cell.cellstate.is_mine
def _reveal_cell(self: Self, pos_x: int, pos_y: int) -> None:
"""Reveal a cell and its adjacent cells if necessary.
This method reveals the cell at (pos_x, pos_y) and, if it has no adjacent mines,
it recursively reveals all adjacent cells.
Args:
pos_x (int): x-coordinate of the cell to reveal.
pos_y (int): y-coordinate of the cell to reveal.
"""
cell: Cell = self._grid.cells[pos_x][pos_y]
cell.reveal()
if not cell.cellstate.mines_around:
self._reveal_adjacent(pos_x, pos_y)
class Grid:
"""Manage the grid of cells in the Minesweeper game.
This class manages the cells of the Minesweeper game, including mine placement and
revealing cells. It also includes managers for mine placement and cell revealing.
Attributes:
_cell_size (int): Size of each cell in pixels.
cell_num (int): Number of cells in one dimension (grid is square).
mines_count (int): Total number of mines to place on the grid.
_shift (float): Vertical shift for the grid, used for UI layout.
cells (list[list[Cell]]): A 2D list of Cell objects representing the grid.
mines_placed (bool): Flag indicating if mines have been placed on the grid.
mines (MineManager): Manager for mine placement and counting around cells.
reveal (RevealManager): Manager for revealing cells in the game.
"""
def __init__(
self: Self, cell_size: int, cell_num: int, mines_count: int, shift: float
) -> None:
self._cell_size: int = cell_size
self.cell_num: int = cell_num
self.mines_count: int = mines_count
self._shift: float = shift
self.cells: list[list[Cell]] = [
[
Cell(pos_x, pos_y, self._cell_size, self._shift)
for pos_y in range(self.cell_num)
]
for pos_x in range(self.cell_num)
]
self.mines: _MineManager = _MineManager(self)
self.reveal: _RevealManager = _RevealManager(self)
def draw(self: Self, screen: pygame.Surface) -> None:
"""Draw the grid of cells on the given screen.
This method iterates through all cells in the grid and draws each cell on the
specified screen.
Args:
screen (pygame.Surface): The surface on which to draw the grid of cells.
"""
for row in self.cells:
for cell in row:
cell.draw(screen)
def check_win_condition(self: Self) -> bool:
"""Check if the player has won the game.
This method checks if all non-mine cells have been revealed. If all such cells
are revealed, it returns True, indicating the player has won, otherwise it
returns False.
Returns:
bool: True if the player has won, False otherwise.
"""
for row in self.cells:
for cell in row:
if not cell.cellstate.is_mine and not cell.cellstate.is_revealed:
return False
return True