I have completed my minesweeper game using Tkinter and would like to know ways to improve. I also have a question for extra features: does anybody know a way to have an opening start, like Google minesweeper does where it breaks open a bunch of squares?
I'm accepting all comments.
Please criticize: readability comments and performance comments accepted.
import tkinter as tk
import random
import sys
import applescript
import tkinter.messagebox as messagebox
def printArr():
for row in arr:
print(" ".join(str(cell) for cell in row))
print("")
def createBoard(length, width):
global arr
arr = [[0 for row in range(length)] for column in range(width)]
def placeMine(x, y):
ydim, xdim = len(arr), len(arr[0])
arr[y][x] = 'X'
if (x != (xdim - 1)):
if arr[y][x+1] != 'X':
arr[y][x+1] += 1 # center right
if (x != 0):
if arr[y][x-1] != 'X':
arr[y][x-1] += 1 # center left
if (x != 0) and (y != 0):
if arr[y-1][x-1] != 'X':
arr[y-1][x-1] += 1 # top left
if (y != 0) and (x != (xdim - 1)):
if arr[y-1][x+1] != 'X':
arr[y-1][x+1] += 1 # top right
if (y != 0):
if arr[y-1][x] != 'X':
arr[y-1][x] += 1 # top center
if (x != (xdim - 1)) and (y != (ydim - 1)):
if arr[y+1][x+1] != 'X':
arr[y+1][x+1] += 1 # bottom right
if (x != 0) and (y != (ydim - 1)):
if arr[y+1][x-1] != 'X':
arr[y+1][x-1] += 1 # bottom left
if (y != (ydim - 1)):
if arr[y+1][x] != 'X':
arr[y+1][x] += 1 # bottom center
def createMap(mines, x, y):
global arr
createBoard(y, x)
xlist = list(range(0, x))
ylist = list(range(0, y))
choiceslist = []
for xchoice in xlist:
for ychoice in ylist:
choiceslist.append((xchoice, ychoice))
if mines >= len(choiceslist):
print('bro thats too many mines')
sys.exit()
for _ in range(mines):
choice = random.choice(choiceslist)
choiceslist.remove(choice)
placeMine(choice[1], choice[0])
def subtractFlags():
current_amount = flags.get()
current_amount = current_amount.replace(('\u2691' + ' '), '')
flags.set(f'\u2691 {int(current_amount) - 1}')
def addFlags():
current_amount = flags.get()
current_amount = current_amount.replace(('\u2691' + ' '), '')
flags.set(f'\u2691 {int(current_amount) + 1}')
def lclick(event):
global cellsopened
colorcodes = {1 : 'blue',
2 : 'green',
3 : 'red',
4 : 'purple',
5 : 'maroon',
6 : 'teal',
7 : 'black',
8 : 'white',
0 : 'gray'}
widget = event.widget
info = widget.grid_info()
row, col = info['row'], info['column']
current = widget.cget('text')
arritem = arr[row][col]
if current != ' ':
return
if arritem == 'X':
gameOver(False)
else:
widget.configure(text = str(arritem), fg = colorcodes[arritem])
cellsopened += 1
window.after(69, checkWon)
def rclick(event):
global widget
widget = event.widget
info = widget.grid_info()
row, col = info['row'], info['column']
current = widget.cget('text')
if current == ' ':
widget.configure(text = '\u2691', fg = 'red')
subtractFlags()
elif current == '\u2691':
widget.configure(text = ' ')
addFlags()
def addGameButtons():
global zeros
zeros = 0
cols = dimensions[1]
rows = dimensions[0]
mines = 99
for row in range(rows):
for col in range(cols):
button = tk.Label(game, width = 1, text = ' ', bg = 'light gray')
if arr[row][col] == 0:
zeros += 1
button.config(text = '0', fg = 'gray')
button.grid(column = col, row = row, padx = 2, pady = 2)
button.bind("<Button-1>", lclick)
button.bind('<Button-2>', rclick)
def timerUpdate():
current = timer.get()
current = current.replace('⏱ ', '')
timer.set(f'⏱ {round(eval(current) + 1)}')
if won == False:
window.after(1000, timerUpdate)
def gameOver(won):
if won == False:
ans = messagebox.askyesno(message = 'You lost!\n\nDo you wish to play again?')
else:
current = timer.get()
time = current.replace('⏱ ', '')
ans = messagebox.askyesno(message = f'You won in {time} seconds!\n\nDo you wish to play again?')
if ans == True:
restart()
else:
window.destroy()
sys.exit()
def checkWon():
global won
if cellsopened == (dimensions[0] * dimensions[1]) - mines - zeros:
won = True
gameOver(True)
def chooseDifficulty():
dialog_text = 'set theDialogText to "Please select a difficulty:"\ndisplay dialog theDialogText buttons {"Easy (9x9, 10 mines)", "Medium (13x15, 40 mines)", "Expert (30x16, 99 mines)"} default button "Expert (30x16, 99 mines)"'
out = applescript.run(dialog_text)
returned = (out.out).replace('button returned:', '')
if returned == 'Expert (30x16, 99 mines)':
dimensions = (30, 16)
mines = 99
elif returned == 'Medium (13x15, 40 mines)':
dimensions = (13, 15)
mines = 40
elif returned == 'Easy (9x9, 10 mines)':
dimensions = (9, 9)
mines = 10
outres = {'dimensions' : dimensions, 'mines' : mines}
return outres
def main():
global window, game, flags, flagcounter, infobar, timer, cellsopened, mines, dimensions, won
won = False
cellsopened = 0
#========== CON0FIG SECTION ==========
mines = 99
dimensions = (16, 30)
#========== CONFIG SECTION ==========
#==============================
difchoice = chooseDifficulty()
mines = difchoice['mines']
dimensions = difchoice['dimensions']
#==============================
createMap(mines = mines, x = dimensions[0], y = dimensions[1])
window = tk.Tk()
#center(window)
#window.eval('tk::PlaceWindow . middle')
window.config(bg = 'gray')
window.title('Minesweeper')
window.resizable(False, False)
infobar = tk.Frame(bg = 'red')
infobar.pack(side = 'top')
game = tk.Frame()
game.pack(side = 'bottom')
flags = tk.StringVar()
flags.set('\u2691' + f' {mines}')
timer = tk.StringVar()
timer.set('⏱' + ' 0')
timecounter = tk.Label(infobar, bg = 'gray', textvariable = timer, width = 5)
timecounter.propagate(False)
timecounter.grid(row = 0, column = 0, sticky = 'w')
flagcounter = tk.Label(infobar, bg = 'gray', textvariable = flags)
flagcounter.grid(row = 0, column = 1, sticky = 'e')
addGameButtons()
window.after(10, timerUpdate)
window.mainloop()
def restart():
global window, game, flags, flagcounter, infobar, timer, cellsopened, mines, dimensions, won
window.destroy()
del window, game, flags, flagcounter, infobar, timer, cellsopened, mines, dimensions, won
main()
if __name__ == '__main__':
main()