Skip to content

Commit 66652f2

Browse files
authored
Merge pull request #7 from ripexz/refactor
Minor refactor
2 parents 2226997 + 8736522 commit 66652f2

File tree

3 files changed

+145
-125
lines changed

3 files changed

+145
-125
lines changed

.editorconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# http://EditorConfig.org
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
indent_style = space
8+
indent_size = 4
9+
end_of_line = lf
10+
insert_final_newline = true
11+
trim_trailing_whitespace = true

README.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Python Tkinter Minesweeper v0.5.1
1+
Python Tkinter Minesweeper
22
===========================
33

44
Minesweeper game written in Python using Tkinter GUI library.
@@ -17,10 +17,7 @@ Contents:
1717

1818
To Do:
1919
----------
20-
- Fix bugs
21-
- Clean up duplicate code
2220
- Have specific number of mines, rather than random
2321
- Time counter
2422
- Highscore table
25-
- Adjustable grid and mine count
26-
- ...and then some...
23+
- Adjustable grid and mine count via UI

minesweeper.py

Lines changed: 132 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
11
# Python Version 2.7.3
22
# File: minesweeper.py
33

4-
from Tkinter import *
5-
import tkMessageBox
6-
import random
4+
from tkinter import *
5+
from tkinter import messagebox as tkMessageBox
76
from collections import deque
7+
import random
8+
import platform
9+
import time
10+
11+
SIZE_X = 10
12+
SIZE_Y = 10
13+
14+
STATE_DEFAULT = 0
15+
STATE_CLICKED = 1
16+
STATE_FLAGGED = 2
17+
18+
BTN_CLICK = "<Button-1>"
19+
BTN_FLAG = "<Button-2>" if platform.system() == 'Darwin' else "<Button-3>"
820

921
class Minesweeper:
1022

1123
def __init__(self, master):
1224

1325
# import images
14-
self.tile_plain = PhotoImage(file = "images/tile_plain.gif")
15-
self.tile_clicked = PhotoImage(file = "images/tile_clicked.gif")
16-
self.tile_mine = PhotoImage(file = "images/tile_mine.gif")
17-
self.tile_flag = PhotoImage(file = "images/tile_flag.gif")
18-
self.tile_wrong = PhotoImage(file = "images/tile_wrong.gif")
19-
self.tile_no = []
26+
self.tiles = {
27+
"plain": PhotoImage(file = "images/tile_plain.gif"),
28+
"clicked": PhotoImage(file = "images/tile_clicked.gif"),
29+
"mine": PhotoImage(file = "images/tile_mine.gif"),
30+
"flag": PhotoImage(file = "images/tile_flag.gif"),
31+
"wrong": PhotoImage(file = "images/tile_wrong.gif"),
32+
"numbers": []
33+
}
2034
for x in range(1, 9):
21-
self.tile_no.append(PhotoImage(file = "images/tile_"+str(x)+".gif"))
35+
self.tiles["numbers"].append(PhotoImage(file = "images/tile_"+str(x)+".gif"))
2236

2337
# set up frame
2438
frame = Frame(master)
@@ -36,167 +50,165 @@ def __init__(self, master):
3650
# create buttons
3751
self.buttons = dict({})
3852
self.mines = 0
39-
x_coord = 1
40-
y_coord = 0
41-
for x in range(0, 100):
42-
mine = 0
43-
# tile image changeable for debug reasons:
44-
gfx = self.tile_plain
45-
# currently random amount of mines
46-
if random.uniform(0.0, 1.0) < 0.1:
47-
mine = 1
48-
self.mines += 1
49-
# 0 = Button widget
50-
# 1 = if a mine y/n (1/0)
51-
# 2 = state (0 = unclicked, 1 = clicked, 2 = flagged)
52-
# 3 = button id
53-
# 4 = [x, y] coordinates in the grid
54-
# 5 = nearby mines, 0 by default, calculated after placement in grid
55-
self.buttons[x] = [ Button(frame, image = gfx),
56-
mine,
57-
0,
58-
x,
59-
[x_coord, y_coord],
60-
0 ]
61-
self.buttons[x][0].bind('<Button-1>', self.lclicked_wrapper(x))
62-
self.buttons[x][0].bind('<Button-3>', self.rclicked_wrapper(x))
63-
64-
# calculate coords:
65-
y_coord += 1
66-
if y_coord == 10:
67-
y_coord = 0
68-
x_coord += 1
69-
70-
# lay buttons in grid
71-
for key in self.buttons:
72-
self.buttons[key][0].grid( row = self.buttons[key][4][0], column = self.buttons[key][4][1] )
73-
74-
# find nearby mines and display number on tile
75-
for key in self.buttons:
76-
nearby_mines = 0
77-
if self.check_for_mines(key-9):
78-
nearby_mines += 1
79-
if self.check_for_mines(key-10):
80-
nearby_mines += 1
81-
if self.check_for_mines(key-11):
82-
nearby_mines += 1
83-
if self.check_for_mines(key-1):
84-
nearby_mines += 1
85-
if self.check_for_mines(key+1):
86-
nearby_mines += 1
87-
if self.check_for_mines(key+9):
88-
nearby_mines += 1
89-
if self.check_for_mines(key+10):
90-
nearby_mines += 1
91-
if self.check_for_mines(key+11):
92-
nearby_mines += 1
93-
# store mine count in button data list
94-
self.buttons[key][5] = nearby_mines
95-
#if self.buttons[key][1] != 1:
96-
# if nearby_mines != 0:
97-
# self.buttons[key][0].config(image = self.tile_no[nearby_mines-1])
53+
for x in range(0, SIZE_X):
54+
for y in range(0, SIZE_Y):
55+
if y == 0:
56+
self.buttons[x] = {}
57+
58+
id = str(x) + "_" + str(y)
59+
isMine = False
60+
61+
# tile image changeable for debug reasons:
62+
gfx = self.tiles["plain"]
63+
64+
# currently random amount of mines
65+
if random.uniform(0.0, 1.0) < 0.1:
66+
isMine = True
67+
self.mines += 1
68+
69+
self.buttons[x][y] = {
70+
"id": id,
71+
"isMine": isMine,
72+
"state": STATE_DEFAULT,
73+
"coords": {
74+
"x": x,
75+
"y": y
76+
},
77+
"widget": Button(frame, image = gfx),
78+
"mines": 0 # calculated after grid is built
79+
}
80+
81+
self.buttons[x][y]["widget"].bind(BTN_CLICK, self.lclicked_wrapper(x, y))
82+
self.buttons[x][y]["widget"].bind(BTN_FLAG, self.rclicked_wrapper(x, y))
83+
84+
# lay buttons in grid
85+
self.buttons[x][y]["widget"].grid( row = x, column = y )
86+
87+
# loop again to find nearby mines and display number on tile
88+
for x in range(0, SIZE_X):
89+
for y in range(0, SIZE_Y):
90+
nearby_mines = 0
91+
nearby_mines += 1 if self.check_for_mines(x-1, y-1) else 0 #top right
92+
nearby_mines += 1 if self.check_for_mines(x-1, y) else 0 #top middle
93+
nearby_mines += 1 if self.check_for_mines(x-1, y+1) else 0 #top left
94+
nearby_mines += 1 if self.check_for_mines(x, y-1) else 0 #left
95+
nearby_mines += 1 if self.check_for_mines(x, y+1) else 0 #right
96+
nearby_mines += 1 if self.check_for_mines(x+1, y-1) else 0 #bottom right
97+
nearby_mines += 1 if self.check_for_mines(x+1, y) else 0 #bottom middle
98+
nearby_mines += 1 if self.check_for_mines(x+1, y+1) else 0 #bottom left
99+
100+
self.buttons[x][y]["mines"] = nearby_mines
98101

99102
#add mine and count at the end
100103
self.label2 = Label(frame, text = "Mines: "+str(self.mines))
101-
self.label2.grid(row = 11, column = 0, columnspan = 5)
104+
self.label2.grid(row = SIZE_X+1, column = 0, columnspan = SIZE_Y/2)
102105

103106
self.label3 = Label(frame, text = "Flags: "+str(self.flags))
104-
self.label3.grid(row = 11, column = 4, columnspan = 5)
107+
self.label3.grid(row = SIZE_X+1, column = SIZE_Y/2-1, columnspan = SIZE_Y/2)
105108

106109
## End of __init__
107110

108-
def check_for_mines(self, key):
111+
def check_for_mines(self, x, y):
109112
try:
110-
if self.buttons[key][1] == 1:
111-
return True
113+
return self.buttons[x][y]["isMine"]
112114
except KeyError:
113115
pass
114116

115-
def lclicked_wrapper(self, x):
116-
return lambda Button: self.lclicked(self.buttons[x])
117+
def lclicked_wrapper(self, x, y):
118+
return lambda Button: self.lclicked(self.buttons[x][y])
117119

118-
def rclicked_wrapper(self, x):
119-
return lambda Button: self.rclicked(self.buttons[x])
120+
def rclicked_wrapper(self, x, y):
121+
return lambda Button: self.rclicked(self.buttons[x][y])
120122

121123
def lclicked(self, button_data):
122-
if button_data[1] == 1: #if a mine
123-
# show all mines and check for flags
124-
for key in self.buttons:
125-
if self.buttons[key][1] != 1 and self.buttons[key][2] == 2:
126-
self.buttons[key][0].config(image = self.tile_wrong)
127-
if self.buttons[key][1] == 1 and self.buttons[key][2] != 2:
128-
self.buttons[key][0].config(image = self.tile_mine)
124+
if button_data["isMine"] == True:
129125
# end game
130126
self.gameover()
131127
else:
132-
#change image
133-
if button_data[5] == 0:
134-
button_data[0].config(image = self.tile_clicked)
135-
self.clear_empty_tiles(button_data[3])
128+
# change image
129+
if button_data["mines"] == 0:
130+
button_data["widget"].config(image = self.tiles["clicked"])
131+
self.clear_empty_tiles(button_data["id"])
136132
else:
137-
button_data[0].config(image = self.tile_no[button_data[5]-1])
133+
button_data["widget"].config(image = self.tiles["numbers"][button_data["mines"]-1])
138134
# if not already set as clicked, change state and count
139-
if button_data[2] != 1:
140-
button_data[2] = 1
135+
if button_data["state"] != STATE_CLICKED:
136+
button_data["state"] = STATE_CLICKED
141137
self.clicked += 1
142138
if self.clicked == 100 - self.mines:
143139
self.victory()
144140

145141
def rclicked(self, button_data):
146142
# if not clicked
147-
if button_data[2] == 0:
148-
button_data[0].config(image = self.tile_flag)
149-
button_data[2] = 2
150-
button_data[0].unbind('<Button-1>')
143+
if button_data["state"] == STATE_DEFAULT:
144+
button_data["widget"].config(image = self.tiles["flag"])
145+
button_data["state"] = STATE_FLAGGED
146+
button_data["widget"].unbind(BTN_CLICK)
151147
# if a mine
152-
if button_data[1] == 1:
148+
if button_data["isMine"] == True:
153149
self.correct_flags += 1
154150
self.flags += 1
155151
self.update_flags()
156152
# if flagged, unflag
157-
elif button_data[2] == 2:
158-
button_data[0].config(image = self.tile_plain)
159-
button_data[2] = 0
160-
button_data[0].bind('<Button-1>', self.lclicked_wrapper(button_data[3]))
153+
elif button_data["state"] == 2:
154+
button_data["widget"].config(image = self.tiles["plain"])
155+
button_data["state"] = 0
156+
button_data["widget"].bind(BTN_CLICK, self.lclicked_wrapper(button_data["coords"]["x"], button_data["coords"]["y"]))
161157
# if a mine
162-
if button_data[1] == 1:
158+
if button_data["isMine"] == True:
163159
self.correct_flags -= 1
164160
self.flags -= 1
165161
self.update_flags()
166162

167-
def check_tile(self, key, queue):
163+
def check_tile(self, x, y, queue):
168164
try:
169-
if self.buttons[key][2] == 0:
170-
if self.buttons[key][5] == 0:
171-
self.buttons[key][0].config(image = self.tile_clicked)
172-
queue.append(key)
165+
if self.buttons[x][y]["state"] == STATE_DEFAULT:
166+
if self.buttons[x][y]["mines"] == 0:
167+
self.buttons[x][y]["widget"].config(image = self.tiles["clicked"])
168+
queue.append(self.buttons[x][y]["id"])
173169
else:
174-
self.buttons[key][0].config(image = self.tile_no[self.buttons[key][5]-1])
175-
self.buttons[key][2] = 1
170+
self.buttons[x][y]["widget"].config(image = self.tiles["numbers"][self.buttons[x][y]["mines"]-1])
171+
self.buttons[x][y]["state"] = STATE_CLICKED
176172
self.clicked += 1
177173
except KeyError:
178174
pass
179175

180-
def clear_empty_tiles(self, main_key):
181-
queue = deque([main_key])
176+
def clear_empty_tiles(self, id):
177+
queue = deque([id])
182178

183179
while len(queue) != 0:
184180
key = queue.popleft()
185-
self.check_tile(key-9, queue) #top right
186-
self.check_tile(key-10, queue) #top middle
187-
self.check_tile(key-11, queue) #top left
188-
self.check_tile(key-1, queue) #left
189-
self.check_tile(key+1, queue) #right
190-
self.check_tile(key+9, queue) #bottom right
191-
self.check_tile(key+10, queue) #bottom middle
192-
self.check_tile(key+11, queue) #bottom left
193-
181+
parts = key.split("_")
182+
source_x = int(parts[0])
183+
source_y = int(parts[1])
184+
185+
self.check_tile(source_x-1, source_y-1, queue) #top right
186+
self.check_tile(source_x-1, source_y, queue) #top middle
187+
self.check_tile(source_x-1, source_y+1, queue) #top left
188+
self.check_tile(source_x, source_y-1, queue) #left
189+
self.check_tile(source_x, source_y+1, queue) #right
190+
self.check_tile(source_x+1, source_y-1, queue) #bottom right
191+
self.check_tile(source_x+1, source_y, queue) #bottom middle
192+
self.check_tile(source_x+1, source_y+1, queue) #bottom left
193+
194+
def reveal(self):
195+
for x in range(0, SIZE_X):
196+
for y in range(0, SIZE_Y):
197+
if self.buttons[x][y]["isMine"] == False and self.buttons[x][y]["state"] == STATE_FLAGGED:
198+
self.buttons[x][y]["widget"].config(image = self.tiles["wrong"])
199+
if self.buttons[x][y]["isMine"] == True and self.buttons[x][y]["state"] != STATE_FLAGGED:
200+
self.buttons[x][y]["widget"].config(image = self.tiles["mine"])
201+
global root
202+
root.update()
203+
194204
def gameover(self):
205+
self.reveal()
195206
tkMessageBox.showinfo("Game Over", "You Lose!")
196207
global root
197208
root.destroy()
198209

199210
def victory(self):
211+
self.reveal()
200212
tkMessageBox.showinfo("Game Over", "You Win!")
201213
global root
202214
root.destroy()

0 commit comments

Comments
 (0)