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
76from 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
921class 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