1
+ from __future__ import print_function
2
+ import tkinter as tk
3
+ import tkinter .messagebox as messagebox
4
+ import random
5
+
6
+ class Grid :
7
+ def __init__ (self , n ):
8
+ self .size = n
9
+ self .cells = self .generate_empty_grid ()
10
+ self .compressed = False
11
+ self .merged = False
12
+ self .moved = False
13
+ self .current_score = 0
14
+
15
+ def random_cell (self ):
16
+ cell = random .choice (self .retreive_empty_cells ())
17
+ i = cell [0 ]
18
+ j = cell [1 ]
19
+ self .cells [i ][j ] = 2 if random .random () < 0.9 else 4
20
+
21
+ def retreive_empty_cells (self ):
22
+ empty_cells = []
23
+ for i in range (self .size ):
24
+ for j in range (self .size ):
25
+ if self .cells [i ][j ] == 0 :
26
+ empty_cells .append ((i ,j ))
27
+ return empty_cells
28
+
29
+ def generate_empty_grid (self ):
30
+ return [[0 ] * self .size for i in range (self .size )]
31
+
32
+ def transpose (self ):
33
+ self .cells = [list (t ) for t in zip (* self .cells )]
34
+
35
+ def reverse (self ):
36
+ for i in range (self .size ):
37
+ start = 0
38
+ end = self .size - 1
39
+ while start < end :
40
+ self .cells [i ][start ], self .cells [i ][end ] = self .cells [i ][end ], self .cells [i ][start ]
41
+ start += 1
42
+ end -= 1
43
+
44
+ def clear_flags (self ):
45
+ self .compressed = False
46
+ self .merged = False
47
+ self .moved = False
48
+
49
+ def left_compress (self ):
50
+ self .compressed = False
51
+ new_grid = self .generate_empty_grid ()
52
+ for i in range (self .size ):
53
+ count = 0
54
+ for j in range (self .size ):
55
+ if self .cells [i ][j ] != 0 :
56
+ new_grid [i ][count ] = self .cells [i ][j ]
57
+ if count != j :
58
+ self .compressed = True
59
+ count += 1
60
+ self .cells = new_grid
61
+
62
+ def left_merge (self ):
63
+ self .merged = False
64
+ for i in range (self .size ):
65
+ for j in range (self .size - 1 ):
66
+ if self .cells [i ][j ] == self .cells [i ][j + 1 ] and self .cells [i ][j ] != 0 :
67
+ self .cells [i ][j ]*= 2
68
+ self .cells [i ][j + 1 ] = 0
69
+ self .current_score += self .cells [i ][j ]
70
+ self .merged = True
71
+ print ('left merged' )
72
+
73
+ def found_2048 (self ):
74
+ for i in range (self .size ):
75
+ for j in range (self .size ):
76
+ if self .cells [i ][i ] >= 2048 :
77
+ return True
78
+ return False
79
+
80
+ def has_empty_cells (self ):
81
+ for i in range (self .size ):
82
+ for j in range (self .size ):
83
+ if self .cells [i ][j ] == 0 :
84
+ return True
85
+ return False
86
+
87
+ def can_merge (self ):
88
+ for i in range (self .size ):
89
+ for j in range (self .size - 1 ):
90
+ if self .cells [i ][j ] == self .cells [i ][j + 1 ]:
91
+ return True
92
+ for j in range (self .size ):
93
+ for i in range (self .size - 1 ):
94
+ if self .cells [i ][j ] == self .cells [i + 1 ][j ]:
95
+ return True
96
+ return False
97
+
98
+ def set_cells (self , cells ):
99
+ self .cells = cells
100
+
101
+ def print_grid (self ):
102
+ print ('=' * 40 )
103
+ for i in range (self .size ):
104
+ for j in range (self .size ):
105
+ print ('%d\t ' , self .cells [i ][j ], end = '' )
106
+ print ()
107
+ print ('=' * 40 )
108
+
109
+ class GamePanel :
110
+ '''The GUI view class of the 2048 game show'''
111
+ CELL_PADDING = 10
112
+ BACKGROUND_COLOR = '#92877d'
113
+ EMPTY_CELL_COLOR = '#9e948a'
114
+ CELL_BACKGROUND_COLOR_DICT = {
115
+ '2' : '#eee4da' ,
116
+ '4' : '#ede0c8' ,
117
+ '8' : '#f2b179' ,
118
+ '16' : '#f59563' ,
119
+ '32' : '#f67c5f' ,
120
+ '64' : '#f65a3b' ,
121
+ '128' : '#edcf72' ,
122
+ '256' : '#edcc61' ,
123
+ '512' : '#edc850' ,
124
+ '1024' : '#edc53f' ,
125
+ '2048' : '#edc22e' ,
126
+ 'beyond' : '#3c3a32' }
127
+
128
+ CELL_COLOR_DICT = {
129
+ '2' : '#776e65' ,
130
+ '4' : '#776e65' ,
131
+ '8' :'#f9f6f2' ,
132
+ '16' : '#f9f6f2' ,
133
+ '32' : '#f9f6f2' ,
134
+ '64' : '#f9f6f2' ,
135
+ '128' : '#f9f6f2' ,
136
+ '256' : '#f9f6f2' ,
137
+ '512' : '#f9f6f2' ,
138
+ '1024' : '#f9f6f2' ,
139
+ '2048' : '#f9f6f2' ,
140
+ 'beyond' : '#19f6f2' }
141
+
142
+ FONT = ('COMIC SANS' , 18 , 'bold' )
143
+ UP_KEYS = ('Up' )
144
+ DOWN_KEYS = ('Down' )
145
+ LEFT_KEYS = ('Left' )
146
+ RIGHT_KEYS = ('Right' )
147
+
148
+ def __init__ (self , grid ):
149
+ self .grid = grid
150
+ self .root = tk .Tk ()
151
+ self .root .title ('2048' )
152
+ self .background = tk .Frame (self .root , bg = GamePanel .BACKGROUND_COLOR )
153
+ self .cell_labels = []
154
+ for i in range (self .grid .size ):
155
+ row_labels = []
156
+ for j in range (self .grid .size ):
157
+ label = tk .Label (self .background , text = '' ,
158
+ bg = GamePanel .EMPTY_CELL_COLOR , justify = tk .CENTER ,
159
+ font = GamePanel .FONT , width = 4 , height = 2 )
160
+ label .grid (row = i , column = j , padx = 10 , pady = 10 )
161
+ row_labels .append (label )
162
+ self .cell_labels .append (row_labels )
163
+ self .background .grid ()
164
+
165
+ def paint (self ):
166
+ for i in range (self .grid .size ):
167
+ for j in range (self .grid .size ):
168
+ if self .grid .cells [i ][j ]== 0 :
169
+ self .cell_labels [i ][j ].configure (text = '' , bg = GamePanel .EMPTY_CELL_COLOR )
170
+ else :
171
+ cell_text = str (self .grid .cells [i ][j ])
172
+ if self .grid .cells [i ][j ]> 2048 :
173
+ bg_color = GamePanel .CELL_BACKGROUND_COLOR_DICT .get ("beyond" )
174
+ fg_color = GamePanel .CELL_COLOR_DICT .get ("beyond" )
175
+ else :
176
+ bg_color = GamePanel .CELL_BACKGROUND_COLOR_DICT .get (cell_text )
177
+ fg_color = GamePanel .CELL_COLOR_DICT .get (cell_text )
178
+ self .cell_labels [i ][j ].configure (text = cell_text ,
179
+ bg = bg_color , fg = fg_color )
180
+
181
+ class Game :
182
+ '''The main Game class which is the controller of the whole game.'''
183
+ def __init__ (self , grid , panel ):
184
+ self .grid = grid
185
+ self .panel = panel
186
+ self .start_cells_num = 2
187
+ self .over = False
188
+ self .won = False
189
+ self .keep_playing = False
190
+
191
+ def is_game_terminated (self ):
192
+ return self .over or (self .won and (not self .keep_playing ))
193
+
194
+ def start (self ):
195
+ self .add_start_cells ()
196
+ self .panel .paint ()
197
+ self .panel .root .bind ('<Key>' , self .key_handler )
198
+ self .panel .root .mainloop ()
199
+
200
+ def add_start_cells (self ):
201
+ for i in range (self .start_cells_num ):
202
+ self .grid .random_cell ()
203
+
204
+ def can_move (self ):
205
+ return self .grid .has_empty_cells () or self .grid .can_merge ()
206
+
207
+ def key_handler (self , event ):
208
+ if self .is_game_terminated ():
209
+ return
210
+
211
+ self .grid .clear_flags ()
212
+ key_value = event .keysym
213
+
214
+ print ('{} key pressed' .format (key_value ))
215
+ if key_value in GamePanel .UP_KEYS :
216
+ self .up ()
217
+ #self.grid.print_grid
218
+ elif key_value in GamePanel .LEFT_KEYS :
219
+ self .left ()
220
+ #self.grid print_grid
221
+ elif key_value in GamePanel .DOWN_KEYS :
222
+ self .down ()
223
+ #self.grid.print_grid()
224
+ elif key_value in GamePanel .RIGHT_KEYS :
225
+ self .right ()
226
+ #self.grid.print_grid()
227
+ else :
228
+ pass
229
+
230
+ self .panel .paint ()
231
+ print ('Score: {}' .format (self .grid .current_score ))
232
+ if self .grid .found_2048 ():
233
+ self .you_win ()
234
+ if not self .keep_playing :
235
+ return
236
+
237
+ if self .grid .moved :
238
+ self .grid .random_cell ()
239
+
240
+ self .panel .paint ()
241
+ if not self .can_move ():
242
+ self .over = True
243
+ self .game_over ()
244
+
245
+ def you_win (self ):
246
+ if not self .won :
247
+ self .won = True
248
+ print ('You Win!' )
249
+ if messagebox .askyesno ('2048' , 'You Win!\n '
250
+ 'Are you going to continue the 2048 game?' ):
251
+ self .keep_playing = True
252
+
253
+ def game_over (self ):
254
+ print ('Game Over!' )
255
+ messagebox .showinfo ('2048' , 'Oops!\n '
256
+ 'Game Over!' )
257
+
258
+ def up (self ):
259
+ self .grid .transpose ()
260
+ self .grid .left_compress ()
261
+ self .grid .left_merge ()
262
+ self .grid .moved = self .grid .compressed or self .grid .merged
263
+ self .grid .left_compress ()
264
+ self .grid .transpose ()
265
+
266
+ def left (self ):
267
+ self .grid .left_compress ()
268
+ self .grid .left_merge ()
269
+ self .grid .moved = self .grid .compressed or self .grid .merged
270
+ self .grid .left_compress ()
271
+
272
+ def down (self ):
273
+ self .grid .transpose ()
274
+ self .grid .reverse ()
275
+ self .grid .left_compress ()
276
+ self .grid .left_merge ()
277
+ self .grid .moved = self .grid .compressed or self .grid .merged
278
+ self .grid .left_compress ()
279
+ self .grid .reverse ()
280
+ self .grid .transpose ()
281
+
282
+ def right (self ):
283
+ self .grid .reverse ()
284
+ self .grid .left_compress ()
285
+ self .grid .left_merge ()
286
+ self .grid .moved = self .grid .compressed or self .grid .merged
287
+ self .grid .left_compress ()
288
+ self .grid .reverse ()
289
+
290
+ if __name__ == '__main__' :
291
+ size = 4
292
+ grid = Grid (size )
293
+ panel = GamePanel (grid )
294
+ game2048 = Game (grid , panel )
295
+ game2048 .start ()
0 commit comments