ConsoleMinesweeper

ConsoleMinesweeper

python script to play the classic minesweeper game in your console.

How to play

input the row and column in the following form: row,col
this will dig in the specified place.

if you want to mark a spot you know there is a bomb,
put an ‘m’ following the row and column, like this: row,colm

press ‘h’ for help in-game and ‘q’ to quit.

Source Code: main.py

from random import randint, choice
import os
import re


class MineField:
    def __init__(self, size, n_bombs):
        self.size = size
        self.digged = 0
        self.n_bombs = n_bombs
        self.grid = [[0 for j in range(size)] for i in range(size)]
        self.grid_mask = [['·' for j in range(size)] for i in range(size)]
        self.plant_bombs()
        # self.assign_numbers()
        self.first_dig()
        
    def __str__(self):
            
        _str = '    ';
        
        for i in range(1, self.size+1):
            _str += str(i) + ' '
            if i < 10:
                _str += ' '
        _str += '\n'
        
        _str += ' ' + '―' * (self.size * 3 + 3) + '\n'
        
        for i, row in enumerate(self.grid_mask, 1):
            if i < 10:
                _str += ' '
            _str += str(i) + '| '
            for char in row:
                _str += str(char) + '  '
            _str += '\n'
        return _str
        
    def plant_bombs(self):
        bombs_planted = 0
        while bombs_planted < self.n_bombs:
            row = randint(0, self.size - 1)
            column = randint(0, self.size - 1)
            
            if self.grid[row][column] == '*':
                continue
            
            self.grid[row][column] = '*'
            
            self.fill_neighbors(row, column)
                        
            bombs_planted += 1
            
    def fill_neighbors(self, row, column):
            if row != 0:
                if column != 0:
                    if self.grid[row-1][column-1] != '*':
                        self.grid[row-1][column-1] += 1
                        
                if self.grid[row-1][column] != '*':
                    self.grid[row-1][column] += 1
                    
                if column != self.size-1:
                    if self.grid[row-1][column+1] != '*':
                        self.grid[row-1][column+1] += 1
                        
            if column != 0:
                if self.grid[row][column-1] != '*':
                    self.grid[row][column-1] += 1
                    
            if column != self.size-1:
                if self.grid[row][column+1] != '*':
                    self.grid[row][column+1] += 1
                    
            if row != self.size-1:
                if column != 0:
                    if self.grid[row+1][column-1] != '*':
                        self.grid[row+1][column-1] += 1
                        
                if self.grid[row+1][column] != '*':
                    self.grid[row+1][column] += 1
                    
                if column != self.size-1:
                    if self.grid[row+1][column+1] != '*':
                        self.grid[row+1][column+1] += 1
                
    def first_dig(self):
        zeros = []
        for row in range(self.size):
            for column in range(self.size):
                if self.grid[row][column] == 0:
                    zeros.append((row, column))
                    
        spot_to_dig = choice(zeros)
        self.dig(spot_to_dig[0], spot_to_dig[1])
        
    def dig(self, row, col):
        spot = self.grid[row][col]
        if spot == '*':
            return False
        elif spot != self.grid_mask[row][col]:
            self.grid_mask[row][col] = spot
            self.digged += 1
            if (spot == 0):
                self.clear_zeros(row, col)
            return True
            
    def clear_zeros(self, row, col):
        if row != 0:
            if col != 0:
                self.dig(row-1, col-1)
                    
            self.dig(row-1, col)
                
            if col != self.size-1:
                self.dig(row-1, col+1)
                    
        if col != 0:
            self.dig(row, col-1)
            
        if col != self.size-1:
            self.dig(row, col+1)
                
        if row != self.size-1:
            if col != 0:
                self.dig(row+1, col-1)
                    
            self.dig(row+1, col)
                
            if col != self.size-1:
                self.dig(row+1, col+1)
                    
    def mark_spot(self, row, col):
        spot_mask = self.grid_mask[row][col]
        if spot_mask == 'X':
            self.grid_mask[row][col] = '·'
        elif spot_mask == '·':
            self.grid_mask[row][col] = 'X'
            
    def show_bombs(self):
        for row in range(self.size):
            for column in range(self.size):
                if self.grid[row][column] == '*':
                    self.grid_mask[row][column] = '*'
            

def print_help():
    print('――――― Console Minesweeper ―――――')
    print('To dig in a spot, input the row\nand the column you want to dig in.')
    print('For example, if I wanted to dig\nin the row 2, column 4, I would\ntype: 2,4')
    print('―――――――――――――――――――――――――――――――――')
    print('If you just want to mark a spot\nwhere you know is a bomb, type the\ncoordinates followed with an "m".\nExample: 1,4m')
    print('\n')
    

def play(field):        
    pattern = re.compile(r"^[0-9]+,[0-9]+m?$")
    os.system('cls' if os.name == 'nt' else 'clear')
    print(field)
    move = 'h'
    alive = True
    while alive:
        
        move = input("Your move ('h' for help, 'q' to quit): ")
        os.system('cls' if os.name == 'nt' else 'clear')
        
        if move == 'h':
            print_help()
        elif move == 'q':
            print('Bye!\n')
            alive = 'bye'
            break
        elif re.fullmatch(pattern, move):
                row, column= [int(i)-1 for i in move.strip('m').split(',')]
                if (row > field.size) |( column > field.size):
                    print('Invalid coordinates\n')
                elif move[-1] == 'm':
                    field.mark_spot(row, column)
                else:
                    alive = field.dig(row, column)
                    if field.digged == (field.size**2 - field.n_bombs):
                        break
        else:
            print('Invalid input\n')
        
        print(field)
        
    if alive:
        if alive == True:
            print(field)
            print("You won, congratulations!!!")
    else:
        os.system('cls' if os.name == 'nt' else 'clear')
        field.show_bombs()
        print(field)
        print("You lost :(")
        

field = MineField(10, 10)
play(field)