Forum Archive

I created 2D minesweeper game, Try out!

Tonnoaw

I created 2D minesweeper game
I've learned Python for a month and I decided to create 2D minesweeper game using scene module in Pythonista. I want some recommendations from any experts to make my code more cleaner and remove some unnecessary steps.
Also try out my game :)
```
from scene import *
import sound
import random

randomly install the mines on the board

def setTheMine(board):
for i in range(int(len(board))):
board[random.randint(0,len(board) - 1)][random.randint(0,len(board) - 1)] = "#"
return board

class App(Scene):
def setup(self):
self.x, self.y = 0, 0
#turn cheat to True to see the mines while playing
self.cheat = False
#create 2d matrix board
# '-' = empty space and '#' = mine
self.board = [["-" for i in range(10)] for i in range(10)]
self.background_color = '#828adb'
#install the mine
self.board = setTheMine(self.board)
#assign tile coordinates
self.tileDict = {}
for row in range(len(self.board)):
for block in range(len(self.board[0])):
posist = str(block)+str(row)
posX_value = int(self.size.w / 2) - int((len(self.board) * 48) / 2) + (48 * block)
posY_value = int(self.size.h / 2) + int((len(self.board) * 48) / 2) - (48 * row)
x = self.tileDict.setdefault(posist, (posX_value, posY_value))
#tile color according values
self.tileColor = {
"0": 'pzl:Green3',
"1": 'pzl:Blue3',
"2": 'pzl:Yellow3',
"3": 'pzl:Red3',
"4": 'pzl:Red3',
"5": 'pzl:Red3',
"6": 'pzl:Red3',
"7": 'pzl:Red3',
"-": 'pzl:Gray3',
"#": 'pzl:Gray3'
}
self.labelFont = ('Adidas Unity', 50)
self.gameStart = True
#display 2D graphic board
self.displayBoard()

def touch_began(self, touch):
    self.x, self.y = touch.location

def displayBoard(self):
    grid = Node(parent=self)
    row1 = 0
    for row in self.board:
        column = 0
        for block in row:
            posX , posY = self.tileDict.get(str(column)+str(row1))
            tile = SpriteNode(Texture(self.tileColor.get(block)), position = (posX, posY), scale = 1.5)
            grid.add_child(tile)
            if self.cheat:
                if block == '#':
                    self.mine = SpriteNode(Texture('emj:Bomb'), position = (posX, posY), scale = 0.5)
                    self.add_child(self.mine)
            if block is not "-" and block is not "#":
                self.mineLabel = LabelNode(self.board[row1][column], self.labelFont, position = (posX, posY), scale = 0.5)
                self.add_child(self.mineLabel)
            column += 1
        row1 += 1

def update(self):
    #check for touch input. is touch input on the board 
    found = False
    for y in range(len(self.board)):
        for x in range(len(self.board[0])):
            testCase = str(x)+str(y)
            #x and y coordinates for each tile
            testPosX, testPosY = self.tileDict.get(testCase)
            if testPosX - 24 < self.x < testPosX + 24 and testPosY - 24 < self.y < testPosY + 24: 
                self.x, self.y = 0, 0
                sound.play_effect('8ve:8ve-beep-shinymetal')
                if self.gameStart:
                    found = True
                #tile that user touched
                tileSelected = testCase
                break
        if found:
            break
    if found:
        #check if user touched on the mine
        if self.board[int(tileSelected[1])][int(tileSelected[0])] is "#":
            self.gameStart = False
            self.gameStatus = 'lose'
        else:
        #else reveal the number of mines that surround the tile
            if self.board[int(tileSelected[1])][int(tileSelected[0])] is not "#":
                self.board = zeroScanning(self.board, (int(tileSelected[0]), int(tileSelected[1])))
        self.foundSpace = False
        #check if all tiles are revealed
        for row in self.board:
            for tile in row:
                if tile is '-':
                    self.foundSpace = True
                    break
            if self.foundSpace:
                break
        #if there is no empty tile left = win!
        if self.foundSpace == False:
            self.gameStatus = 'win'
            self.gameStart = False
        if self.gameStart:
            self.displayBoard()
        #if game ends
        else:
            #reveals the mine
            self.cheat = True
            self.gameStart = False
            self.displayBoard()
            if self.gameStatus == 'lose':
                self.statusLabel = LabelNode('LOSE!', ('Anantason', 25), position = (self.size.x / 2, 750), scale = 3, color = '#ff0000')
            if self.gameStatus == 'win':
                self.statusLabel = LabelNode('WIN!', ('Anantason', 25), position = (self.size.x / 2, 750), scale = 3, color = '#7aff13')
            self.add_child(self.statusLabel)
    #reset the touch inout
    self.x, self.y = 0, 0

def countMines(board, coor):
tileX, tileY = coor
mineCount = 0
#check surrounding tiles
for y in range(-1, 2):
for x in range(-1, 2):
try:
#prevent of negative index
if int(tileX) + x != -1 and int(tileY) + y != -1:
if board[int(tileY) + y][int(tileX) + x] == "#":
mineCount += 1
except IndexError:
pass
return str(mineCount)

def zeroScanning(board, coordinate):
PosX, PosY = coordinate
board[PosY][PosX] = countMines(board, (PosX, PosY))
if countMines(board, (PosX, PosY)) == '0':
for testX in range(-1, 2):
for testY in range(-1, 2):
PosX1 = PosX + testX
PosY1 = PosY + testY
try:
if (PosX1 != -1 and PosY1 != -1):
if countMines(board, (PosX1, PosY1)) == "0" and board[PosY1][PosX1] != '0':
zeroScanning(board, (PosX1, PosY1))
else:
board[PosY1][PosX1] = countMines(board, (PosX1, PosY1))
except IndexError:
pass
return board

if name == 'main':
run(App(), LANDSCAPE) ```

mikael

@Tonnoaw, very nice-looking and functional game, congratulations!

Unfortunately, the whole board does not fit on an iPhone screen.

mikael

@Tonnoaw, some improvement auggestions, on a quick read through:

  • You can use self.size to fit the board to screen.
  • setTheMine and the functions at the bottom could be in the class, for consistency.
  • Using continuously-running update to wait for touches seems curious, instead of just triggering the update from the touch method.
  • Also, maybe I am not getting all the details here, but the string based coordinates, converting with ints etc. looks like it could be optimized, storing more directly what you need in the matrix.
  • For readability, I would recommend learning the Python conventions: variables, functions and methods are lower_case, while classes are immediately recognizable since only they are CamelCased.

Besides the size thing, these are of course all just focused on efficiency and style.

Tonnoaw

@mikael Thanks for your suggestions . I'll apply your suggestions in my next projects and practices

PinhoBD

Very interesting game. For similar game idea for young children, I would change mines into coins (or even Easter eggs) and count how many touches it takes to collect how many coins.

PinhoBD

@Tonnoaw I would also try to import game_menu.py and build a menu with title and option selection buttons. So, rather than setting “cheat” mode in the script, define a mode button for a non-programmer to change mode of operation.