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()

\$\endgroup\$
  • 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\$ – Josiah Dec 28 '19 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\$ – uchaosis Dec 29 '19 at 10:45
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()
\$\endgroup\$
  • \$\begingroup\$ IMO, the module docstring should be above the imports and I'd also use """ Module docstring here. """ instead of multiple #s. \$\endgroup\$ – Grajdeanu Alex. Jan 2 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\$ – uchaosis Jan 3 at 8:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.