1 # coding : utf-8
2
3 #: pip install pygame
4 import random
5 import sys
6 import pygame
7
8 #: 颜色定义
9 COLOR_WHITE = (255, 255, 255)
10 COLOR_BLACK = (0, 0, 0)
11
12 class Block:
13 """小块"""
14 width = 24
15 height = 24
16
17 @staticmethod
18 def draw(s, left, top, color, bg_color):
19 pygame.draw.rect(s, bg_color, pygame.Rect(left, top, Block.width, Block.height))
20 pygame.draw.rect(s, color, pygame.Rect(left, top, Block.width - 1, Block.height - 1))
21
22
23 class Building:
24 """积木"""
25
26 def __init__(self):
27 """
28 方块的7种基本形状
29 每次初始化随机选择一个形状
30 @:return True / False
31 """
32 self.form = random.choice(
33 [
34 [
35 [0, 0, 0, 0, 0],
36 [0, 0, 1, 0, 0],
37 [0, 1, 1, 1, 0],
38 [0, 0, 0, 0, 0],
39 [0, 0, 0, 0, 0]
40 ],
41 [
42 [0, 0, 0, 0, 0],
43 [0, 0, 0, 0, 0],
44 [1, 1, 1, 1, 0],
45 [0, 0, 0, 0, 0],
46 [0, 0, 0, 0, 0]
47 ],
48 [
49 [0, 0, 0, 0, 0],
50 [0, 1, 1, 0, 0],
51 [0, 0, 1, 1, 0],
52 [0, 0, 0, 0, 0],
53 [0, 0, 0, 0, 0]
54 ],
55 [
56 [0, 0, 0, 0, 0],
57 [0, 0, 1, 1, 0],
58 [0, 1, 1, 0, 0],
59 [0, 0, 0, 0, 0],
60 [0, 0, 0, 0, 0]
61 ],
62 [
63 [0, 0, 0, 0, 0],
64 [0, 1, 1, 0, 0],
65 [0, 0, 1, 0, 0],
66 [0, 0, 1, 0, 0],
67 [0, 0, 0, 0, 0]
68 ],
69 [
70 [0, 0, 0, 0, 0],
71 [0, 0, 1, 1, 0],
72 [0, 0, 1, 0, 0],
73 [0, 0, 1, 0, 0],
74 [0, 0, 0, 0, 0]
75 ],
76 [
77 [0, 0, 0, 0, 0],
78 [0, 1, 1, 0, 0],
79 [0, 1, 1, 0, 0],
80 [0, 0, 0, 0, 0],
81 [0, 0, 0, 0, 0]
82 ]
83 ])
84
85 def __getitem__(self, pos):
86 return self.form[pos]
87
88 def __setitem__(self, key, value):
89 self.form[key] = value
90
91
92 class Layout:
93 """棋盘"""
94
95 def __init__(self):
96 self.block_x_count = 16;
97 self.block_y_count = 22;
98 self.layout = [[0 if 1 < i < self.block_x_count - 2 and j < self.block_y_count - 2 else 1
99 for i in range(self.block_x_count)] for j in range(self.block_y_count)]
100
101 @property
102 def size(self):
103 """返回棋盘屏幕大小(width,height)"""
104 return (self.block_x_count * Block.width, self.block_y_count * Block.height)
105
106 def create_new_building(self):
107 """
108 创建新的积木,初始化位置为第5,0格, 速度为4
109 :return: 返回是否无空间创建了
110 """
111 self.building = Building()
112 self.building_left, self.building_top = 5, 0 #
113 self.drop_speed = 3
114 print(self.test_building_touch_wall())
115 return self.test_building_touch_wall()
116
117 @property
118 def speed(self):
119 return self.drop_speed
120
121 def test_building_touch_wall(self, x_offset=0, y_offset=0):
122 """
123 积木是否已经触底/墙壁
124 具体操作:
125 判断积木最后一排的1,是否在当前棋牌对应的位置是也是1
126 @:param x_offset: x的偏移量 移动时可以传入1/-1来判断
127 @:param y_offset: y的偏移量 正常下落时可以传入1来判断
128 """
129 for i in range(4, -1, -1):
130 for j in range(5):
131 if self.building[i][j]:
132 if self.layout[i + self.building_top + y_offset][j + self.building_left + x_offset]:
133 return True
134 return False
135
136 def move_left_right(self, x):
137 """
138 左右移动
139 @:param x: 移动量 x_offset
140 """
141 #: 移动时不能撞墙
142 if not self.test_building_touch_wall(x_offset=x):
143 self.building_left += x
144
145 def down_build(self):
146 """ 盒子的自动下移 """
147 self.building_top += 1
148
149 def direct_down(self):
150 """ 手动快速降落 """
151 self.drop_speed = 50
152
153 def convert_building(self):
154 """
155 * 扭转盒子的总方位 (右转)
156 具体操作:
157 把第一竖排的倒序给第一横排的
158 把第二竖排的倒序给第二横排的
159 后面同理.
160 """
161 new_box = [[0 for i in range(5)] for j in range(5)]
162 for i in range(5):
163 for j in range(4, -1, -1):
164 new_box[i][j] = self.building[4 - j][i]
165 self.building = new_box
166
167 def clear_full_lines(self):
168 """消除满行的所有行"""
169 new_layout = [[0 if 1 < i < self.block_x_count - 2 and j < self.block_y_count - 2 else 1
170 for i in range(self.block_x_count)] for j in range(self.block_y_count)]
171
172 row_len = self.block_x_count - 4
173 new_row = self.block_y_count - 2 - 1
174 for cur_row in range(self.block_y_count - 2 - 1, 0, -1):
175 if sum(self.layout[cur_row][2:self.block_x_count - 2]) < row_len:
176 new_layout[new_row] = self.layout[cur_row]
177 new_row -= 1
178 self.layout = new_layout
179
180 def put_building_to_layout(self):
181 """将积木放到棋盘里"""
182 for i in range(4, -1, -1):
183 for j in range(5):
184 if self.building[i][j]:
185 self.layout[i + self.building_top][j + self.building_left] = 1
186 #: 这里会调用消除函数
187 self.clear_full_lines()
188
189 def draw_building(self, s):
190 """
191 显示积木
192 @:param s : pygame = screen
193 """
194 cur_left, cur_top = self.building_left * Block.width, self.building_top * Block.height
195 for i in range(5):
196 for j in range(5):
197 # 只画积木实体,不管盒子本身
198 if self.building[j][i]:
199 Block.draw(s, cur_left + i * Block.width, cur_top + j * Block.height, COLOR_BLACK, COLOR_WHITE)
200
201 def draw(self, s):
202 """
203 显示棋盘
204 @:param s : pygame = screen
205 """
206 for i in range(self.block_x_count):
207 for j in range(self.block_y_count):
208 if self.layout[j][i] == 0:
209 Block.draw(s, i * Block.width, j * Block.height, COLOR_WHITE, COLOR_BLACK)
210 else:
211 Block.draw(s, i * Block.width, j * Block.height, COLOR_BLACK, COLOR_WHITE)
212
213
214 # -------------------------------------------------------------------
215 # Main
216 # -------------------------------------------------------------------
217 def main():
218 #: 初始化
219 while True:
220 layout = Layout()
221 layout.create_new_building()
222 pygame.init()
223 pygame.display.set_caption('俄罗斯方块')
224 screen = pygame.display.set_mode((layout.size), 0, 32)
225 is_over = False
226 #: 单局游戏循环开始 [结束后直接重新开始]
227 while not is_over:
228 #: 处理游戏消息
229 for e in pygame.event.get():
230 if e.type == pygame.QUIT:
231 sys.exit()
232 #: 处理按键
233 if e.type == pygame.KEYDOWN:
234 if e.key == pygame.K_UP:
235 layout.convert_building()
236 if e.key == pygame.K_DOWN:
237 layout.direct_down()
238 if e.key == pygame.K_LEFT:
239 layout.move_left_right(-1)
240 if e.key == pygame.K_RIGHT:
241 layout.move_left_right(1)
242 #: 是否碰触底部地面了,是 -> 融合背景 否 -> 继续下落
243 if layout.test_building_touch_wall(y_offset=1):
244 layout.put_building_to_layout()
245 is_over = layout.create_new_building()
246 else:
247 layout.down_build()
248 #: 绘制
249 layout.draw(screen)
250 layout.draw_building(screen)
251 pygame.display.update()
252 #: 速度
253 pygame.time.Clock().tick(layout.speed)
254
255
256 if __name__ == '__main__':
257 main()