-
Notifications
You must be signed in to change notification settings - Fork 20
/
004.py
310 lines (249 loc) · 8.38 KB
/
004.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
import tkinter as tk
from tkinter import messagebox
import random
cell_size = 30
C = 12
R = 20
height = R * cell_size
width = C * cell_size
FPS = 200 # 刷新页面的毫秒间隔
# 定义各种形状
SHAPES = {
"O": [(-1, -1), (0, -1), (-1, 0), (0, 0)],
"S": [(-1, 0), (0, 0), (0, -1), (1, -1)],
"T": [(-1, 0), (0, 0), (0, -1), (1, 0)],
"I": [(0, 1), (0, 0), (0, -1), (0, -2)],
"L": [(-1, 0), (0, 0), (-1, -1), (-1, -2)],
"J": [(-1, 0), (0, 0), (0, -1), (0, -2)],
"Z": [(-1, -1), (0, -1), (0, 0), (1, 0)],
}
# 定义各种形状的颜色
SHAPESCOLOR = {
"O": "blue",
"S": "red",
"T": "yellow",
"I": "green",
"L": "purple",
"J": "orange",
"Z": "Cyan",
}
def draw_cell_by_cr(canvas, c, r, color="#CCCCCC"):
"""
:param canvas: 画板,用于绘制一个方块的Canvas对象
:param c: 方块所在列
:param r: 方块所在行
:param color: 方块颜色,默认为#CCCCCC,轻灰色
:return:
"""
x0 = c * cell_size
y0 = r * cell_size
x1 = c * cell_size + cell_size
y1 = r * cell_size + cell_size
canvas.create_rectangle(x0, y0, x1, y1,
fill=color, outline="white", width=2)
# 绘制面板
def draw_board(canvas, block_list):
for ri in range(R):
for ci in range(C):
cell_type = block_list[ri][ci]
if cell_type:
draw_cell_by_cr(canvas, ci, ri, SHAPESCOLOR[cell_type])
else:
draw_cell_by_cr(canvas, ci, ri)
def draw_cells(canvas, c, r, cell_list, color="#CCCCCC"):
"""
绘制指定形状指定颜色的俄罗斯方块
:param canvas: 画板
:param r: 该形状设定的原点所在的行
:param c: 该形状设定的原点所在的列
:param cell_list: 该形状各个方格相对自身所处位置
:param color: 该形状颜色
:return:
"""
for cell in cell_list:
cell_c, cell_r = cell
ci = cell_c + c
ri = cell_r + r
# 判断该位置方格在画板内部(画板外部的方格不再绘制)
if 0 <= ci < C and 0 <= ri < R:
draw_cell_by_cr(canvas, ci, ri, color)
win = tk.Tk()
canvas = tk.Canvas(win, width=width, height=height, )
canvas.pack()
block_list = []
for i in range(R):
i_row = ['' for j in range(C)]
block_list.append(i_row)
draw_board(canvas, block_list)
def draw_block_move(canvas, block, direction=[0, 0]):
"""
绘制向指定方向移动后的俄罗斯方块
:param canvas: 画板
:param block: 俄罗斯方块对象
:param direction: 俄罗斯方块移动方向
:return:
"""
shape_type = block['kind']
c, r = block['cr']
cell_list = block['cell_list']
# 移动前,先清除原有位置绘制的俄罗斯方块,也就是用背景色绘制原有的俄罗斯方块
draw_cells(canvas, c, r, cell_list)
dc, dr = direction
new_c, new_r = c+dc, r+dr
block['cr'] = [new_c, new_r]
# 在新位置绘制新的俄罗斯方块就好
draw_cells(canvas, new_c, new_r, cell_list, SHAPESCOLOR[shape_type])
def generate_new_block():
# 随机生成新的俄罗斯方块
kind = random.choice(list(SHAPES.keys()))
# 对应横纵坐标,以左上角为原点,水平向右为x轴正方向,
# 竖直向下为y轴正方向,x对应横坐标,y对应纵坐标
cr = [C // 2, 0]
new_block = {
'kind': kind, # 对应俄罗斯方块的类型
'cell_list': SHAPES[kind],
'cr': cr
}
return new_block
def check_move(block, direction=[0, 0]):
"""
判断俄罗斯方块是否可以朝制定方向移动
:param block: 俄罗斯方块对象
:param direction: 俄罗斯方块移动方向
:return: boolean 是否可以朝制定方向移动
"""
cc, cr = block['cr']
cell_list = block['cell_list']
for cell in cell_list:
cell_c, cell_r = cell
c = cell_c + cc + direction[0]
r = cell_r + cr + direction[1]
# 判断该位置是否超出左右边界,以及下边界
# 一般不判断上边界,因为俄罗斯方块生成的时候,可能有一部分在上边界之上还没有出来
if c < 0 or c >= C or r >= R:
return False
# 必须要判断r不小于0才行,具体原因你可以不加这个判断,试试会出现什么效果
if r >= 0 and block_list[r][c]:
return False
return True
def check_row_complete(row):
for cell in row:
if cell=='':
return False
return True
score = 0
win.title("SCORES: %s" % score) # 标题中展示分数
def check_and_clear():
has_complete_row = False
for ri in range(len(block_list)):
if check_row_complete(block_list[ri]):
has_complete_row = True
# 当前行可消除
if ri > 0:
for cur_ri in range(ri, 0, -1):
block_list[cur_ri] = block_list[cur_ri-1][:]
block_list[0] = ['' for j in range(C)]
else:
block_list[ri] = ['' for j in range(C)]
global score
score += 10
if has_complete_row:
draw_board(canvas, block_list)
win.title("SCORES: %s" % score)
def save_block_to_list(block):
shape_type = block['kind']
cc, cr = block['cr']
cell_list = block['cell_list']
for cell in cell_list:
cell_c, cell_r = cell
c = cell_c + cc
r = cell_r + cr
# block_list 在对应位置记下其类型draw_cells
block_list[r][c] = shape_type
def horizontal_move_block(event):
"""
左右水平移动俄罗斯方块
"""
direction = [0, 0]
if event.keysym == 'Left':
direction = [-1, 0]
elif event.keysym == 'Right':
direction = [1, 0]
else:
return
global current_block
if current_block is not None and check_move(current_block, direction):
draw_block_move(canvas, current_block, direction)
def rotate_block(event):
global current_block
if current_block is None:
return
cell_list = current_block['cell_list']
rotate_list = []
for cell in cell_list:
cell_c, cell_r = cell
rotate_cell = [cell_r, -cell_c]
rotate_list.append(rotate_cell)
block_after_rotate = {
'kind': current_block['kind'], # 对应俄罗斯方块的类型
'cell_list': rotate_list,
'cr': current_block['cr']
}
if check_move(block_after_rotate):
cc, cr= current_block['cr']
draw_cells(canvas, cc, cr, current_block['cell_list'])
draw_cells(canvas, cc, cr, rotate_list,SHAPESCOLOR[current_block['kind']])
current_block = block_after_rotate
def land(event):
global current_block
if current_block is None:
return
cell_list = current_block['cell_list']
cc, cr = current_block['cr']
min_height = R
for cell in cell_list:
cell_c, cell_r = cell
c, r = cell_c + cc, cell_r + cr
if r>=0 and block_list[r][c]:
return
h = 0
for ri in range(r+1, R):
if block_list[ri][c]:
break
else:
h += 1
if h < min_height:
min_height = h
down = [0, min_height]
if check_move(current_block, down):
draw_block_move(canvas, current_block, down)
def game_loop():
win.update()
global current_block
if current_block is None:
new_block = generate_new_block()
# 新生成的俄罗斯方块需要先在生成位置绘制出来
draw_block_move(canvas, new_block)
current_block = new_block
if not check_move(current_block, [0, 0]):
messagebox.showinfo("Game Over!", "Your Score is %s" % score)
win.destroy()
return
else:
if check_move(current_block, [0, 1]):
draw_block_move(canvas, current_block, [0, 1])
else:
# 无法移动,记入 block_list 中
save_block_to_list(current_block)
current_block = None
check_and_clear()
win.after(FPS, game_loop)
canvas.focus_set() # 聚焦到canvas画板对象上
canvas.bind("<KeyPress-Left>", horizontal_move_block)
canvas.bind("<KeyPress-Right>", horizontal_move_block)
canvas.bind("<KeyPress-Up>", rotate_block)
canvas.bind("<KeyPress-Down>", land)
current_block = None
win.update()
win.after(FPS, game_loop) # 在FPS 毫秒后调用 game_loop方法
win.mainloop()