4
\$\begingroup\$

I’ve just created Minesweeper game, which work perfectly fine (for me). Any suggestions on how to improve this code would be greatly appreciate, in terms of:

  • Object-oriented
  • Array
  • Error handling
  • Function
  • More efficient approach
  • Others: Style(PEP8), comments, clean code, recursion, etc

Explanation of game:

  • Minesweeper is an array(table) of hidden mines and non-mine cells.
  • You can reveal any cells, one by one. Once you found the mines, you lose.
  • If you can revealed all non-mine cells, you win.
  • Each non-mine cell show you number of neighbor cell that contain mines.
  • Read more Wikipedia, or you can try it on your computer.

My approach:

Clean version


import random
class Minesweeper:
 def __init__(self,width=9,height=10,mine_numbers=12):
 self.width = width
 self.height = height
 self.mine_numbers = mine_numbers 
 self.table = [None]*self.width*self.height
 self.user_cell = False
 self.user_row = False
 self.user_column = False
 self.user_reveal = []
 def game_create(self):
 print(f'Default size is {self.width}*{self.height}, {self.mine_numbers} mines')
 default_size = input('Play default size?(Y/N): ')
 if default_size.lower() == 'n':
 correct_input = False 
 while not correct_input:
 try:
 self.width = int(input('Enter width: '))
 self.height = int(input('Enter height: '))
 self.mine_numbers = int(input('Enter number of mines: '))
 if self.mine_numbers >= self.width*self.height or self.mine_numbers == 0:
 print('ERROR: Number of mines can not be 0 or equal/exceed table size')
 elif self.width > 99 or self.height > 99:
 print('ERROR: Maximum table size is 99*99')
 else:
 self.table = [None]*self.width*self.height
 self.user_reveal = []
 correct_input = True
 return self.width,self.height,self.mine_numbers,self.table,self.user_reveal
 except ValueError:
 print('ERROR: Try again, number only') 
 else:
 self.table = [None]*self.width*self.height
 self.user_reveal = []
 return self.width,self.height,self.mine_numbers,self.table,self.user_reveal
 def user_input(self):
 correct_input = False
 while not correct_input:
 try:
 self.user_cell = input('Enter {[column][row]} in 4 digits eg. 0105: ')
 int(self.user_cell)
 if len(self.user_cell) != 4:
 print('ERROR: Only 4 digits allowed')
 elif int(self.user_cell[2:]) > self.height or self.user_cell[2:] == '00':
 print('ERROR: Row out of range')
 elif int(self.user_cell[:2]) > self.width or self.user_cell[:2] == '00':
 print('ERROR: Column of range')
 elif self.user_cell in self.user_reveal:
 print('ERROR: Already revealed')
 else:
 correct_input = True
 except ValueError:
 print('ERROR: Try again, number only')
 if self.user_cell:
 self.user_row = int(self.user_cell[2:])
 self.user_column = int(self.user_cell[:2])
 self.user_reveal.append(self.user_cell)
 return self.user_cell,self.user_row,self.user_column
 def mines_generator(self):
 user_location = ((self.user_row-1)*self.width)+self.user_column-1
 possible_location = [i for i in range(self.width*self.height) if i != user_location]
 mines_location = random.sample(possible_location,self.mine_numbers)
 for i in mines_location:
 self.table[i] = 9
 return self.table
 def two_dimension_array(self):
 for i in range(self.height):
 self.table[i] = self.table[0+(self.width*i):self.width+(self.width*i)] 
 del self.table[self.height:]
 return self.table
 def complete_table(self):
 temporary_table = [[None for _ in range(self.width)] for _ in range(self.height)]
 for i in range(self.height):
 for j in range(self.width):
 if self.table[i][j] == 9:
 temporary_table[i][j] = 9
 continue
 else:
 counter = 0
 for k in range(i-1,i+2):
 if 0 <= k <= self.height-1:
 for l in range(j-1,j+2):
 if 0 <= l <= self.width-1:
 if self.table[k][l] == 9:
 counter += 1
 continue
 temporary_table[i][j] = counter
 self.table = temporary_table
 return self.table
 def adjacent_zero(self,zero_cell): 
 if self.table[int(zero_cell[2:])-1][int(zero_cell[:2])-1] == 0:
 for i in range(int(zero_cell[2:])-1-1,int(zero_cell[2:])-1+2):
 if 0 <= i < self.height:
 for j in range(int(zero_cell[:2])-1-1,int(zero_cell[:2])-1+2):
 if 0 <= j < self.width:
 if str(j+1).zfill(2)+str(i+1).zfill(2) not in self.user_reveal:
 self.user_reveal.append(str(j+1).zfill(2)+str(i+1).zfill(2))
 if self.table[i][j] == 0:
 self.adjacent_zero(str(j+1).zfill(2)+str(i+1).zfill(2))
 return self.user_reveal
 def first_turn(self):
 self.user_input()
 self.mines_generator()
 self.two_dimension_array()
 self.complete_table()
 self.adjacent_zero(self.user_cell)
 def print_table(self):
 print('\n'*10)
 for row in range(self.height+1):
 cell = '|'
 for column in range(self.width+1):
 if row == 0:
 cell += f'{column:2}|'
 continue
 elif column == 0:
 cell += f'{row:2}|'
 continue
 elif str(column).zfill(2)+str(row).zfill(2) in self.user_reveal:
 cell += f'{self.table[row-1][column-1]:2}|'
 continue 
 else:
 cell += '{:>3}'.format('|')
 print(cell)
 def end_game(self):
 def reveal_mine():
 for i,j in enumerate(self.table):
 for k,l in enumerate(j):
 if l == 9:
 self.table[i][k] = ‘XX’
 if str(k+1).zfill(2)+str(i+1).zfill(2) not in self.user_reveal:
 self.table[i][k] = ‘**’
 self.user_reveal.append(str(k+1).zfill(2)+str(i+1).zfill(2))
 if self.user_cell:
 if self.table[self.user_row-1][self.user_column-1] == 9:
 end_game = True
 reveal_mine()
 self.print_table()
 print('YOU LOSE!')
 elif len(self.user_reveal) == (self.width*self.height)-self.mine_numbers:
 end_game = True
 reveal_mine()
 self.print_table()
 print('YOU WIN!')
 else:
 end_game = False
 else:
 end_game = False
 return end_game
 def restart_game(self):
 restart = input('Restart?(Y/N): ')
 if restart.lower() == 'y':
 return True
 else: 
 return False
def main():
 minesweeper = Minesweeper()
 while True: 
 minesweeper.game_create()
 minesweeper.print_table()
 minesweeper.first_turn()
 while not minesweeper.end_game():
 minesweeper.print_table()
 minesweeper.user_input()
 minesweeper.adjacent_zero(minesweeper.user_cell)
 if not minesweeper.restart_game():
 break
if __name__ == '__main__':
 main()

Comment version


import random
class Minesweeper:
 def __init__(self,width=9,height=10,mine_numbers=12):
 # Table generate: change via tables_ize()
 self.width = width
 self.height = height
 self.mine_numbers = mine_numbers 
 self.table = [None]*self.width*self.height
 # User cell input
 self.user_cell = False
 self.user_row = False
 self.user_column = False
 self.user_reveal = []
 '''
 {user_reveal} is changed by
 - {game_create()}: reset user_reveal
 - {user_input()}: append input cell, cannot reveal all adjacent 0 here (first turn - table not yet generated)
 - {adjacent_zero()}: reveal all adjacent 0
 - {end_game()}: append all mines
 '''
 '''
 {*_user_*},{user_cell} = {[column][row]}, index is 1 more than {*_cell_*} index
 {*_cell_*} = {[row][column]}, index is 1 less than {*_user_*} index
 '''
 def game_create(self):
 print(f'Default size is {self.width}*{self.height}, {self.mine_numbers} mines')
 default_size = input('Play default size?(Y/N): ')
 if default_size.lower() == 'n':
 correct_input = False
 while not correct_input:
 try:
 self.width = int(input('Enter width: '))
 self.height = int(input('Enter height: '))
 self.mine_numbers = int(input('Enter number of mines: '))
 if self.mine_numbers >= self.width*self.height or self.mine_numbers == 0:
 print('ERROR: Number of mines can not be 0 or equal/exceed table size')
 elif self.width > 99 or self.height > 99:
 print('ERROR: Maximum table size is 99*99')
 else:
 self.table = [None]*self.width*self.height
 self.user_reveal = []
 correct_input = True
 return self.width,self.height,self.mine_numbers,self.table,self.user_reveal
 except ValueError:
 print('ERROR: Try again, number only')
 else:
 self.table = [None]*self.width*self.height
 self.user_reveal = []
 return self.width,self.height,self.mine_numbers,self.table,self.user_reveal
 def user_input(self):
 correct_input = False
 while not correct_input:
 try:
 self.user_cell = input('Enter {[column][row]} in 4 digits eg. 0105: ')
 int(self.user_cell)
 if len(self.user_cell) != 4:
 print('ERROR: Only 4 digits allowed')
 elif int(self.user_cell[2:]) > self.height or self.user_cell[2:] == '00':
 print('ERROR: Row out of range')
 elif int(self.user_cell[:2]) > self.width or self.user_cell[:2] == '00':
 print('ERROR: Column of range')
 elif self.user_cell in self.user_reveal:
 print('ERROR: Already revealed')
 else:
 correct_input = True
 except ValueError:
 print('ERROR: Try again, number only')
 self.user_row = int(self.user_cell[2:])
 self.user_column = int(self.user_cell[:2])
 if self.user_cell:
 self.user_reveal.append(self.user_cell)
 return self.user_cell,self.user_row,self.user_column
 def mines_generator(self):
 # Exclude first cell from mines generator
 user_location = ((self.user_row-1)*self.width)+self.user_column-1
 possible_location = [i for i in range(self.width*self.height) if i != user_location]
 mines_location = random.sample(possible_location,self.mine_numbers)
 # Assign 'Location with mine' with 9
 for i in mines_location:
 self.table[i] = 9
 return self.table
 def two_dimension_array(self):
 # Save table into 2D array
 for i in range(self.height):
 self.table[i] = self.table[0+(self.width*i):self.width+(self.width*i)]
 # Remove unnessessary elements
 del self.table[self.height:]
 return self.table
 def complete_table(self):
 # Create temporary 2D array
 temporary_table = [[None for _ in range(self.width)] for _ in range(self.height)]
 # For every table[i][j]
 for i in range(self.height):
 for j in range(self.width):
 # If table[i][j] is bomb, continue
 if self.table[i][j] == 9:
 temporary_table[i][j] = 9
 continue
 else:
 counter = 0
 # For every adjacent neighbor arrays
 for k in range(i-1,i+2):
 # Error handling: list index out of range
 if 0 <= k <= self.height-1:
 for l in range(j-1,j+2):
 # Error handling: list index out of range
 if 0 <= l <= self.width-1:
 # Count every adjacent mines
 if self.table[k][l] == 9:
 counter += 1
 continue
 temporary_table[i][j] = counter
 self.table = temporary_table
 return self.table
 def adjacent_zero(self,zero_cell): 
 # If value is 0 
 if self.table[int(zero_cell[2:])-1][int(zero_cell[:2])-1] == 0:
 # For all neighbor elements
 for i in range(int(zero_cell[2:])-1-1,int(zero_cell[2:])-1+2):
 # Error handling: index out of range
 if 0 <= i < self.height:
 for j in range(int(zero_cell[:2])-1-1,int(zero_cell[:2])-1+2):
 if 0 <= j < self.width:
 # If neighbor element of 0 is not yet append, append all adjacent element
 if str(j+1).zfill(2)+str(i+1).zfill(2) not in self.user_reveal:
 self.user_reveal.append(str(j+1).zfill(2)+str(i+1).zfill(2))
 # If neighbor is also 0, do a recursion
 if self.table[i][j] == 0:
 self.adjacent_zero(str(j+1).zfill(2)+str(i+1).zfill(2))
 def first_turn(self):
 self.user_input()
 self.mines_generator()
 self.two_dimension_array()
 self.complete_table()
 self.adjacent_zero()
 def print_table(self):
 # Clear UI
 print('\n'*10)
 for row in range(self.height+1):
 cell = '|'
 for column in range(self.width+1):
 # Top-row label
 if row == 0:
 cell += f'{column:2}|' # (Note: try 02 instead of 2)
 continue
 # First column label
 elif column == 0:
 cell += f'{row:2}|'
 continue
 # Revealed cell
 elif str(column).zfill(2)+str(row).zfill(2) in self.user_reveal:
 cell += f'{self.table[row-1][column-1]:2}|'
 continue 
 # Not yet revealed cell
 else:
 cell += '{:>3}'.format('|')
 print(cell)
 def end_game(self):
 # If end: reveal all mines, nested function
 def reveal_mine():
 for i,j in enumerate(self.table):
 for k,l in enumerate(j):
 if l == 9:
 self.table[i][k] = ‘XX’
 if str(k+1).zfill(2)+str(i+1).zfill(2) not in self.user_reveal:
 self.table[i][k] = ‘**’
 self.user_reveal.append(str(k+1).zfill(2)+str(i+1).zfill(2))
 # If user choose cell: check if end
 if self.user_cell:
 if self.table[self.user_row-1][self.user_column-1] == 9:
 end_game = True
 reveal_mine()
 self.print_table()
 print('YOU LOSE!')
 elif len(self.user_reveal) == (self.width*self.height)-self.mine_numbers:
 end_game = True
 reveal_mine()
 self.print_table()
 print('YOU WIN!')
 else:
 end_game = False
 # If no cell selected: end = False
 else:
 end_game = False
 return end_game
 def restart_game(self): 
 restart = input('Restart?(Y/N): ')
 if restart.lower() == 'y':
 return True
 else: 
 return False
def main():
 minesweeper = Minesweeper()
 while True: 
 minesweeper.game_create()
 minesweeper.print_table()
 minesweeper.first_turn()
 while not minesweeper.end_game():
 minesweeper.print_table()
 minesweeper.user_input()
 minesweeper.adjacent_zero(minesweeper.user_cell)
 if not minesweeper.restart_game():
 break
if __name__ == '__main__':
 main()
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Dec 28, 2019 at 8:18
\$\endgroup\$
2
  • 1
    \$\begingroup\$ I don't have time for a full review, but one thing I notice is that you're using strings as comments. If you have a comment (something to help explain why the code is what it is) use a # character to start it. The weird """ sections in Python are intended for docstrings, which should only exist once at the start of the function and tell the function what it is for and how to use it. They are not comments and importantly, are not ignored by python. \$\endgroup\$ Commented Dec 28, 2019 at 10:03
  • \$\begingroup\$ @Josiah Thank you. I will leave it like this to remind me. And since no one reply to my post, it would be great if you could give me a full review (whenever is ok) :) \$\endgroup\$ Commented Dec 29, 2019 at 10:45

1 Answer 1

3
\$\begingroup\$

I think implementing a game is a good way to learn a new programming language. If you are up for it, you could try coding a graphical version of Minesweeper using a game library such as Pygame or Arcade to learn even more about the language.

Below are my suggestions for improvements, in no particular order:

Comments

When I review code, I read the code and the comments in tandem. The comments should help me understand what the code does. But for this code I don't think they do, because they are on a quite low level. Comments that tells me essentially the same thing as the code itself aren't very interesting. What the comments instead should communicate is the intent of the code. The why not the how.

I suggest instead of putting comments on individual lines, delete them all and replace them with a big comment on top of the module where you describe the architecture of the Minesweeper class:

import random
# Minesweeper
# -----------
# Minesweeper is a solitaire game in which the goal is to reveal all
# mines on a board. The Minesweeper class implements the game. It has
# the following attributes:
#
# * width: width of board
# ...
# * table: board representation
#
# This example shows how the class should be used:
#
# ms = Minesweeper()
# while True:
# ms.game_create()
# ...

It gives the reader an overview of what the code is all about.

Yes/no prompts

A convention expressed in this Stackoverflow answer is to let the default choice in a yes/no prompt be capitalized. So if the prompt is

Play default size? [Y/n]:

the user knows that by just pressing return, the default size is choosen. Yes, I know it is a "small detail" but to make great software you have to consider these little things.

Integer prompt function

If the user does not want to use the default size, he or she is prompted for the width, height and the number of mines. Consider making a function encapsulating the prompting code:

def prompt_int(name, lo, hi):
 while True:
 s = input(f'Enter {name} [{lo}..{hi}]: ')
 try:
 v = int(s)
 except ValueError:
 print('ERROR: Try again, number only')
 continue
 if not (lo <= v <= hi):
 print('ERROR: Allowed range %d..%d' % (lo, hi))
 continue
 return v

Unused return values

The return value of the game_create, user_input and mines_generator methods are unused.

Only initialize attributes in one place

The attributes table, width, height and mine_numbers are initialized both in the constructor and in the game_create method. Better to put the game_create code in the constructor so that the initialization only happens once. After all, it is a constructor so it makes sense that the game is created in it.

Avoid magic numbers

It looks like if a cell contains 9 it is a mine. It is better to avoid magic numbers and to use constants instead. Declare

class Minesweeper:
 CLEAR = 0
 MINE = 9

and then to check if a cell contains a mine write

self.table[i][j] == Minesweeper.MINE

Board representation

It is unclear to me why the board representation (table) is initialized as a one-dimensional array and then changed to a two-dimensional one. Why not make it two-dimensional from the start?

Counting mines with min and max

Use min and max to count mines in the following way:

def complete_table(self):
 width, height, table = self.width, self.height, self.table
 for i in range(height):
 for j in range(width):
 if table[i][j] == Minesweeper.MINE:
 continue
 table[i][j] = 0
 for i2 in range(max(0, i - 1), min(i + 2, height)):
 for j2 in range(max(0, j -1), min(j + 2, width)):
 if table[i2][j2] == Minesweeper.MINE:
 table[i][j] += 1

This usage of min and max for bounds-checking is a common pattern. The first line is just to make the code shorter - so that self doesn't have to be repeated everywhere.

Perfer storing data in a machine friendly format

If some input has to be converted in some way to become usable by the computer, then it should be stored in the converted format. That way, the conversion doesn't have to be repeated each time the data is used.

For example, user_column and user_row are inputted using 1-based indexing. Which is fine because that makes sense for humans. But for Python, 0-based indexing is more convenient so the values should be converted as soon as they are read from the user. The conversion simply means subtracting 1 from them.

The same goes for the user_reveal attribute. It stores a sequence like this:

['0101', '0201', '0404']

but it should be stored in a Python-friendly format, like this:

[(0, 0), (1, 0), (3, 3)]

Don't store data twice

user_row and user_column are after the first turn the same as the last element of the user_reveal list. This allows us to remove the user_row and user_column attributes and instead get the same data by referring to user_reveal[-1].

Don't delay returns

In end_game there is the following:

if ...:
 end_game = True
 ...
elif ...:
 end_game = True
 ...
else:
 end_game = False

It is better to express it as follows

if ...:
 ...
 return True
if ...:
 ...
 return True
return False

because it makes the code flow "straighter."

Avoid nested functions

Nested functions have their uses, but they also makes the code harder to follow. IMO, they should be avoided.

Names

Last but not least, the names of many objects can be improved. For methods and functions, it is preferable to include a verb to make the name "active." Here are the renames I suggest:

  • first_turn => run_first_turn
  • print_table => print_minefield
  • mines_generator => place_mines
  • mine_numbers => mine_count
  • user_input => read_location
  • end_game => is_game_over
  • user_reveal => revealed_locations (for collection types, you want the name to end with an S)
  • adjacent_zero => reveal_safe_locations
  • reveal_mine => reveal_all_mines
  • complete_table => place_mine_counts
  • table => minefield

Final code

import itertools
import random
# Minesweeper
# -----------
# Minesweeper is a solitaire game in which the goal is to reveal all
# mines on a board. The Minesweeper class implements the game. It has
# the following attributes:
#
# * width: width of board
# ...
# * minefield: board representation
#
# This example shows how the class should be used:
#
# ms = Minesweeper()
# while True:
# ms.game_create()
# ...
def prompt_int(name, lo, hi):
 while True:
 s = input(f'Enter {name} [{lo}..{hi}]: ')
 try:
 v = int(s)
 except ValueError:
 print('ERROR: Try again, number only')
 continue
 if not (lo <= v <= hi):
 print('ERROR: Allowed range %d..%d' % (lo, hi))
 continue
 return v
class Minesweeper:
 CLEAR = 0
 MINE = 9
 def __init__(self, width = 9, height = 10, mine_count = 12):
 self.revealed_locations = []
 print(f'Default size is {width}*{height}, {mine_count} mines')
 default_size = input('Play default size? [Y/n]: ')
 if default_size.lower() == 'n':
 self.width = prompt_int('width', 0, 99)
 self.height = prompt_int('height', 0, 99)
 self.mine_count = prompt_int('number of mines',
 0, self.width * self.height - 1)
 else:
 self.width = width
 self.height = height
 self.mine_count = mine_count
 self.minefield = [[Minesweeper.CLEAR] * self.width
 for _ in range(self.height)]
 def read_location(self):
 while True:
 s = input('Enter {[column][row]} in 4 digits eg. 0105: ')
 if len(s) != 4:
 print('ERROR: Only 4 digits allowed')
 continue
 try:
 row = int(s[2:]) - 1
 column = int(s[:2]) - 1
 except ValueError:
 print('ERROR: Try again, number only')
 continue
 if not (0 <= row < self.height):
 print('ERROR: Row out of range')
 elif not (0 <= column < self.width):
 print('ERROR: Column of range')
 elif (row, column) in self.revealed_locations:
 print('ERROR: Already revealed')
 else:
 break
 self.revealed_locations.append((row, column))
 def place_mines(self):
 locs = set(itertools.product(range(self.height), range(self.width)))
 locs -= {self.revealed_locations[-1]}
 locs = random.sample(locs, self.mine_count)
 for row, column in locs:
 self.minefield[row][column] = Minesweeper.MINE
 def place_mine_counts(self):
 width, height, minefield = self.width, self.height, self.minefield
 for i in range(height):
 for j in range(width):
 if minefield[i][j] == Minesweeper.MINE:
 continue
 minefield[i][j] = Minesweeper.CLEAR
 for i2 in range(max(0, i - 1), min(i + 2, height)):
 for j2 in range(max(0, j -1), min(j + 2, width)):
 if minefield[i2][j2] == Minesweeper.MINE:
 minefield[i][j] += 1
 def reveal_safe_locations(self, row, column):
 width, height, minefield = self.width, self.height, self.minefield
 if minefield[row][column] == Minesweeper.CLEAR:
 for i in range(max(0, row - 1), min(row + 2, height)):
 for j in range(max(0, column - 1), min(column + 2, width)):
 if (i, j) not in self.revealed_locations:
 self.revealed_locations.append((i, j))
 if minefield[i][j] == Minesweeper.CLEAR:
 self.reveal_safe_locations(i, j)
 def run_first_turn(self):
 self.read_location()
 self.place_mines()
 self.place_mine_counts()
 row, column = self.revealed_locations[-1]
 self.reveal_safe_locations(row, column)
 def print_minefield(self):
 print('\n'*10)
 for row in range(self.height + 1):
 cell = '|'
 for column in range(self.width + 1):
 if row == 0 and column == 0:
 cell += ' .|'
 elif row == 0:
 cell += f'{column:2}|'
 elif column == 0:
 cell += f'{row:2}|'
 elif (row - 1, column - 1) in self.revealed_locations:
 cell += f'{self.minefield[row-1][column-1]:2}|'
 else:
 cell += '{:>3}'.format('|')
 print(cell)
 def reveal_all_mine(self):
 for i in range(self.height):
 for j in range(self.width):
 if self.minefield[i][j] == Minesweeper.MINE:
 self.minefield[i][j] = 'XX'
 if (i, j) not in self.revealed_locations:
 self.minefield[i][j] = '**'
 self.revealed_locations.append((i, j))
 def is_game_over(self):
 row, column = self.revealed_locations[-1]
 if self.minefield[row][column] == Minesweeper.MINE:
 self.reveal_all_mines()
 self.print_minefield()
 print('YOU LOSE!')
 return True
 unmined_locations_count = self.width * self.height - self.mine_count
 if len(self.revealed_locations) == unmined_locations_count:
 self.reveal_all_mines()
 self.print_minefield()
 print('YOU WIN!')
 return True
 return False
 def restart_game(self):
 restart = input('Restart? [y/N]: ')
 return restart.lower() == 'y'
def main():
 while True:
 ms = Minesweeper()
 ms.print_minefield()
 ms.run_first_turn()
 while not ms.is_game_over():
 ms.print_minefield()
 ms.read_location()
 row, column = ms.revealed_locations[-1]
 ms.reveal_safe_locations(row, column)
 if not ms.restart_game():
 break
if __name__ == '__main__':
 main()
answered Jan 2, 2020 at 15:14
\$\endgroup\$
2
  • \$\begingroup\$ IMO, the module docstring should be above the imports and I'd also use """ Module docstring here. """ instead of multiple #s. \$\endgroup\$ Commented Jan 2, 2020 at 16:21
  • \$\begingroup\$ First of all, thank you. But when the user restart, I want user_input() become default_size so that they don’t have to configure the setting every time. But in your suggestion, we have only init(), and in main(): Minesweeper is also in a while loop. How should I solve this problem? (That’s why I have game_create() separated from init(), and in main(): Minesweeper is placed outside the while loop. But I feel that my solution is not the right one.) // I’ve read your comment 2-3 times, but I need some more times to really understand them all :)) \$\endgroup\$ Commented Jan 3, 2020 at 8:36

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.