# Pygame Minesweeper Clone

I'm still a relative beginner to coding and I would like some feedback on my Minesweeper clone written in Python using Pygame for the graphics. I'm wondering if it would be beneficial to use classes anywhere, or if there's simply a better way of doing what I'm trying to do. Thank you very much—any help is much appreciated.

import random, sys
import pygame as pg
import numpy as np
import collections

WIDTH = 200
GREY = (128,128,128) #rgb value
DARKGREY = (105,105,105)
WHITE = (200,200,200)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
GREEN = (0, 128, 0)
PURPLE = (128, 0, 128)
MAGENTA = (255,0,255)
ORANGE = (255, 165, 0)
YELLOW = (255,255,0)
BLACK = (0,0,0)
BOARD_SIZE = 20
WIDTH = HEIGHT = BOARD_SIZE * 20 #window dimensions
NUM_BOMBS = 40

colors = [RED, BLUE, GREEN, PURPLE, MAGENTA, ORANGE, YELLOW, BLACK]
#random.seed(1)

def init_board():
board = [[0] * BOARD_SIZE for _ in range(BOARD_SIZE)]
for i in range(NUM_BOMBS):
#while count(board) < 40:
row = random.randint(1, BOARD_SIZE-1)
column = random.randint(1, BOARD_SIZE-1)
board[row][column] = 9
# print(np.array(board))
# print("\n")
return board

def count(board):
ret = 0
for row in board[0]:
for col in board:
if board[row][col]==9:
print(board[row][j])
ret+=1
return ret

def create_grid():
board = init_board()
pg.init()
SQUARE_SIZE = 20
win = pg.display.set_mode((HEIGHT, WIDTH))
win.fill(GREY)
pg.display.set_caption("Minesweeper")
font = pg.font.SysFont('Arial', 25)
rect2 = BOMB_IMG.get_rect()
rect3 = FLAG_IMG.get_rect()
moveable = True
done = False
lost = False
called = [[]]
found = 0

for x in range(BOARD_SIZE):
for y in range(BOARD_SIZE):
# draw grid
rectColor = WHITE
rect = pg.Rect(y * SQUARE_SIZE, x * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE)
pg.draw.rect(win, rectColor, rect, 1)

while not done:
for event in pg.event.get():
if moveable and event.type == pg.MOUSEBUTTONDOWN:
Mouse_x, Mouse_y = pg.mouse.get_pos()
print(np.array(board))
if event.button == 1:
if board[Mouse_y // 20][Mouse_x // 20] != 9:
board = uncover_squares(board, [Mouse_y // 20, Mouse_x // 20])
# print(np.array(board))

for row in range(len(board)):
for col in range(len(board)):
if board[row][col] != 0 and board[row][col] != 9:
if board[row][col]==-1:
rect4 = pg.Rect(col * SQUARE_SIZE, row * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE)
pg.draw.rect(win, WHITE, rect4, 0)
else:
rect4 = pg.Rect(col * SQUARE_SIZE, row * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE)
pg.draw.rect(win, WHITE, rect4, 0)
render_num(win, font, board[row][col],
colors[board[row][col]], col*20, row*20)

if board[Mouse_y // 20][Mouse_x // 20] != -1:
rect6 = pg.Rect(Mouse_x//20 * 20, Mouse_y//20 * 20, SQUARE_SIZE, SQUARE_SIZE)

render_num(win,font, board[Mouse_y //20 ][Mouse_x//20], colors[board[Mouse_y //20][Mouse_x//20]], Mouse_x, Mouse_y)

#pg.draw.rect(win, DARKGREY, rect6, 0)
if board[Mouse_y //20 ][Mouse_x//20] == 9:
display_img(win, BOMB_IMG, Mouse_x, Mouse_y, rect2)
moveable= False
pg.display.set_caption("You lost!")
lost = True

if event.button == 3 and board[Mouse_y // 20][Mouse_x // 20] not in range(len(colors)):
if board[Mouse_y // 20][Mouse_x // 20] == 9:
found+=1
if found == NUM_BOMBS and not any(0 in x for x in board):
moveable = False
pg.display.set_caption("You won!")
if event.type == pg.QUIT:
done = True
pg.display.update() #update display

pg.quit()
sys.exit()

def render_num(screen, font, num, color, mouse_x, mouse_y):
screen.blit(font.render(str(num), True, color), (mouse_x // 20 * 20, mouse_y // 20 * 20))

def display_img(screen, img, mouse_x, mouse_y, rect):
screen.blit(img, (mouse_x//20 * 20, mouse_y//20 * 20), rect)

def uncover_squares(board, move):
neighbors = [(-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1)]
uncover(move[0], move[1], len(board), len(board[0]), board, neighbors)
return board

def uncover(i, j, m, n, board, neighbors):
if board[i][j] != 0:
return
mine_count = 0
for cell in neighbors:
#make sure the search is in bounds of array, then check if neighbor is a bomb
if 0 <= i + cell[0] < m and 0 <= j + cell[1] < n and board[i + cell[0]][j + cell[1]] == 9:
mine_count += 1
if mine_count == 0:
board[i][j] = -1
else:
board[i][j] = mine_count
return
for cell in neighbors:
if 0 <= i + cell[0] < m and 0 <= j + cell[1] < n:
uncover(i + cell[0], j + cell[1], m, n, board, neighbors) #call neighbors

if __name__ == "__main__":
create_grid()


## Nomenclature

create_grid does not just create the grid - it runs the entire game and then kills the process. It should be divided up into multiple functions, but that aside, it does not do what's on the tin.

## Forced-exit

Your use of sys.exit() is not a great idea. What if a unit tester (who would have a difficult time anyway, given the size of your function) wanted to test multiple runs of create_grid? Or what if a different caller wanted to run your game, and then perform some other cleanup after?

## Round-to-multiple

This:

mouse_x // 20 * 20, mouse_y // 20 * 20


is a little awkward. An alternative is

mouse_x - mouse_x%20, mouse_y - mouse_y%20


## Early-return

I would find

    if mine_count == 0:
board[i][j] = -1
else:
board[i][j] = mine_count
return
for cell in neighbors:
if 0 <= i + cell[0] < m and 0 <= j + cell[1] < n:
uncover(i + cell[0], j + cell[1], m, n, board, neighbors) #call neighbors


more legible as

    if mine_count == 0:
board[i][j] = -1
for cell in neighbors:
if 0 <= i + cell[0] < m and 0 <= j + cell[1] < n:
uncover(i + cell[0], j + cell[1], m, n, board, neighbors) #call neighbors
else:
board[i][j] = mine_count


# Remove commented out code

All the code that has been commented out distracts from the actual code. You commented it out for a reason: you don't need it anymore.

# Type Hints

These help the user see what is accepted by a function, and what is returned by a function. It's helpful when you need to remember what functions accept what types of values.

from typing import List

def count(board: List[List[int]]) -> int:
....


# create_grid

To be frank, this function is really messy. You indent nine times. I would break this into smaller functions that handle different parts of creating the grid.

• The commented-out code removal is particularly a good idea if you're using source control, which everyone should anyway. Jul 7, 2020 at 17:01
• The type hints suggestion is a good one but should have either a PEP reference or an example for the OP to go off of. Jul 7, 2020 at 17:02