1
+ from PyQt5 .QtWidgets import QMainWindow , QFrame , QDesktopWidget , QApplication
2
+ from PyQt5 .QtCore import Qt , QBasicTimer , pyqtSignal
3
+ from PyQt5 .QtGui import QPainter , QColor
4
+ import sys , random
5
+
6
+ class Tetris (QMainWindow ):
7
+ def __init__ (self ):
8
+ super ().__init__ ()
9
+ self .initUI ()
10
+
11
+ def initUI (self ):
12
+ self .tboard = MainBoard (self )
13
+ self .setCentralWidget (self .tboard )
14
+ self .statusbar = self .statusBar ()
15
+ self .tboard .msg [str ].connect (self .statusbar .showMessage )
16
+ self .tboard .start ()
17
+ self .resize (400 , 600 )
18
+ self .center ()
19
+ self .setWindowTitle ('俄罗斯方块' )
20
+ self .show ()
21
+
22
+ def center (self ):
23
+ screen = QDesktopWidget ().screenGeometry ()
24
+ size = self .geometry ()
25
+ self .move ((screen .width ()- size .width ())/ 2 ,
26
+ (screen .height ()- size .height ())/ 2 )
27
+
28
+ class MainBoard (QFrame ):
29
+ msg = pyqtSignal (str )
30
+ BoardWidth = 10
31
+ BoardHeight = 20
32
+ Speed = 300
33
+
34
+ def __init__ (self , parent ):
35
+ super ().__init__ (parent )
36
+ self .initBoard ()
37
+
38
+ def initBoard (self ):
39
+ self .timer = QBasicTimer ()
40
+ self .isWaitingAfterLine = False
41
+ self .curX = 0
42
+ self .curY = 0
43
+ self .numLinesRemoved = 0
44
+ self .board = []
45
+ self .setFocusPolicy (Qt .StrongFocus )
46
+ self .isStarted = False
47
+ self .isPaused = False
48
+ self .clearBoard ()
49
+
50
+ def shapeAt (self , x , y ):
51
+ return self .board [(y * MainBoard .BoardWidth ) + x ]
52
+
53
+ def setShapeAt (self , x , y , shape ):
54
+ self .board [(y * MainBoard .BoardWidth ) + x ] = shape
55
+
56
+ def squareWidth (self ):
57
+ return self .contentsRect ().width () // MainBoard .BoardWidth
58
+
59
+ def squareHeight (self ):
60
+ return self .contentsRect ().height () // MainBoard .BoardHeight
61
+
62
+ def start (self ):
63
+ if self .isPaused :
64
+ return
65
+ self .isStarted = True
66
+ self .isWaitingAfterLine = False
67
+ self .numLinesRemoved = 0
68
+ self .clearBoard ()
69
+ self .msg .emit (str (self .numLinesRemoved ))
70
+ self .newPiece ()
71
+ self .timer .start (MainBoard .Speed , self )
72
+
73
+ def pause (self ):
74
+ if not self .isStarted :
75
+ return
76
+ self .isPaused = not self .isPaused
77
+ if self .isPaused :
78
+ self .timer .stop ()
79
+ self .msg .emit ("paused" )
80
+ else :
81
+ self .timer .start (MainBoard .Speed , self )
82
+ self .msg .emit (str (self .numLinesRemoved ))
83
+ self .update ()
84
+
85
+
86
+ def paintEvent (self , event ):
87
+ painter = QPainter (self )
88
+ rect = self .contentsRect ()
89
+ boardTop = rect .bottom () - MainBoard .BoardHeight * self .squareHeight ()
90
+ for i in range (MainBoard .BoardHeight ):
91
+ for j in range (MainBoard .BoardWidth ):
92
+ shape = self .shapeAt (j , MainBoard .BoardHeight - i - 1 )
93
+ if shape != ShapeForm .NoShape :
94
+ self .drawSquare (painter ,
95
+ rect .left () + j * self .squareWidth (),
96
+ boardTop + i * self .squareHeight (), shape )
97
+ if self .curPiece .shape () != ShapeForm .NoShape :
98
+ for i in range (4 ):
99
+ x = self .curX + self .curPiece .x (i )
100
+ y = self .curY - self .curPiece .y (i )
101
+ self .drawSquare (painter , rect .left () + x * self .squareWidth (),
102
+ boardTop + (MainBoard .BoardHeight - y - 1 ) * self .squareHeight (),
103
+ self .curPiece .shape ())
104
+
105
+ def keyPressEvent (self , event ):
106
+ if not self .isStarted or self .curPiece .shape () == ShapeForm .NoShape :
107
+ super (MainBoard , self ).keyPressEvent (event )
108
+ return
109
+ key = event .key ()
110
+ if key == Qt .Key_P :
111
+ self .pause ()
112
+ return
113
+ if self .isPaused :
114
+ return
115
+ elif key == Qt .Key_Left :
116
+ self .tryMove (self .curPiece , self .curX - 1 , self .curY )
117
+ elif key == Qt .Key_Right :
118
+ self .tryMove (self .curPiece , self .curX + 1 , self .curY )
119
+ elif key == Qt .Key_Down :
120
+ self .tryMove (self .curPiece .rotateRight (), self .curX , self .curY )
121
+ elif key == Qt .Key_Up :
122
+ self .tryMove (self .curPiece .rotateLeft (), self .curX , self .curY )
123
+ elif key == Qt .Key_Space :
124
+ self .dropDown ()
125
+ elif key == Qt .Key_D :
126
+ self .oneLineDown ()
127
+ else :
128
+ super (MainBoard , self ).keyPressEvent (event )
129
+
130
+ def timerEvent (self , event ):
131
+ if event .timerId () == self .timer .timerId ():
132
+ if self .isWaitingAfterLine :
133
+ self .isWaitingAfterLine = False
134
+ self .newPiece ()
135
+ else :
136
+ self .oneLineDown ()
137
+ else :
138
+ super (MainBoard , self ).timerEvent (event )
139
+
140
+ def clearBoard (self ):
141
+ for i in range (MainBoard .BoardHeight * MainBoard .BoardWidth ):
142
+ self .board .append (ShapeForm .NoShape )
143
+
144
+ def dropDown (self ):
145
+ newY = self .curY
146
+ while newY > 0 :
147
+ if not self .tryMove (self .curPiece , self .curX , newY - 1 ):
148
+ break
149
+ newY -= 1
150
+ self .pieceDropped ()
151
+
152
+ def oneLineDown (self ):
153
+ if not self .tryMove (self .curPiece , self .curX , self .curY - 1 ):
154
+ self .pieceDropped ()
155
+
156
+ def pieceDropped (self ):
157
+ for i in range (4 ):
158
+ x = self .curX + self .curPiece .x (i )
159
+ y = self .curY - self .curPiece .y (i )
160
+ self .setShapeAt (x , y , self .curPiece .shape ())
161
+ self .removeFullLines ()
162
+ if not self .isWaitingAfterLine :
163
+ self .newPiece ()
164
+
165
+ def removeFullLines (self ):
166
+ numFullLines = 0
167
+ rowsToRemove = []
168
+ for i in range (MainBoard .BoardHeight ):
169
+ n = 0
170
+ for j in range (MainBoard .BoardWidth ):
171
+ if not self .shapeAt (j , i ) == ShapeForm .NoShape :
172
+ n = n + 1
173
+ if n == 10 :
174
+ rowsToRemove .append (i )
175
+ rowsToRemove .reverse ()
176
+ for m in rowsToRemove :
177
+ for k in range (m , MainBoard .BoardHeight ):
178
+ for l in range (MainBoard .BoardWidth ):
179
+ self .setShapeAt (l , k , self .shapeAt (l , k + 1 ))
180
+ numFullLines = numFullLines + len (rowsToRemove )
181
+ if numFullLines > 0 :
182
+ self .numLinesRemoved = self .numLinesRemoved + numFullLines
183
+ self .msg .emit (str (self .numLinesRemoved ))
184
+ self .isWaitingAfterLine = True
185
+ self .curPiece .setShape (ShapeForm .NoShape )
186
+ self .update ()
187
+
188
+ def newPiece (self ):
189
+ self .curPiece = Shape ()
190
+ self .curPiece .setRandomShape ()
191
+ self .curX = MainBoard .BoardWidth // 2 + 1
192
+ self .curY = MainBoard .BoardHeight - 1 + self .curPiece .minY ()
193
+ if not self .tryMove (self .curPiece , self .curX , self .curY ):
194
+ self .curPiece .setShape (ShapeForm .NoShape )
195
+ self .timer .stop ()
196
+ self .isStarted = False
197
+ self .msg .emit ("GAME OVER" )
198
+
199
+ def tryMove (self , newPiece , newX , newY ):
200
+ for i in range (4 ):
201
+ x = newX + newPiece .x (i )
202
+ y = newY - newPiece .y (i )
203
+ if x < 0 or x >= MainBoard .BoardWidth or y < 0 or y >= MainBoard .BoardHeight :
204
+ return False
205
+ if self .shapeAt (x , y ) != ShapeForm .NoShape :
206
+ return False
207
+ self .curPiece = newPiece
208
+ self .curX = newX
209
+ self .curY = newY
210
+ self .update ()
211
+ return True
212
+
213
+ def drawSquare (self , painter , x , y , shape ):
214
+ colorTable = [0x000000 , 0xCC6666 , 0x66CC66 , 0x6666CC ,
215
+ 0xCCCC66 , 0xCC66CC , 0x66CCCC , 0xDAAA00 ]
216
+ color = QColor (colorTable [shape ])
217
+ painter .fillRect (x + 1 , y + 1 , self .squareWidth () - 2 ,
218
+ self .squareHeight () - 2 , color )
219
+ painter .setPen (color .lighter ())
220
+ painter .drawLine (x , y + self .squareHeight () - 1 , x , y )
221
+ painter .drawLine (x , y , x + self .squareWidth () - 1 , y )
222
+ painter .setPen (color .darker ())
223
+ painter .drawLine (x + 1 , y + self .squareHeight () - 1 ,
224
+ x + self .squareWidth () - 1 , y + self .squareHeight () - 1 )
225
+ painter .drawLine (x + self .squareWidth () - 1 ,
226
+ y + self .squareHeight () - 1 , x + self .squareWidth () - 1 , y + 1 )
227
+
228
+ class ShapeForm (object ):
229
+ NoShape = 0
230
+ ZShape = 1
231
+ SShape = 2
232
+ LineShape = 3
233
+ TShape = 4
234
+ SquareShape = 5
235
+ LShape = 6
236
+ MirroredLShape = 7
237
+
238
+ class Shape (object ):
239
+ coordsTable = (
240
+ ((0 , 0 ), (0 , 0 ), (0 , 0 ), (0 , 0 )),
241
+ ((0 , - 1 ), (0 , 0 ), (- 1 , 0 ), (- 1 , 1 )),
242
+ ((0 , - 1 ), (0 , 0 ), (1 , 0 ), (1 , 1 )),
243
+ ((0 , - 1 ), (0 , 0 ), (0 , 1 ), (0 , 2 )),
244
+ ((- 1 , 0 ), (0 , 0 ), (1 , 0 ), (0 , 1 )),
245
+ ((0 , 0 ), (1 , 0 ), (0 , 1 ), (1 , 1 )),
246
+ ((- 1 , - 1 ), (0 , - 1 ), (0 , 0 ), (0 , 1 )),
247
+ ((1 , - 1 ), (0 , - 1 ), (0 , 0 ), (0 , 1 ))
248
+ )
249
+
250
+ def __init__ (self ):
251
+ self .coords = [[0 ,0 ] for i in range (4 )]
252
+ self .pieceShape = ShapeForm .NoShape
253
+ self .setShape (ShapeForm .NoShape )
254
+
255
+ def shape (self ):
256
+ return self .pieceShape
257
+
258
+ def setShape (self , shape ):
259
+ table = Shape .coordsTable [shape ]
260
+ for i in range (4 ):
261
+ for j in range (2 ):
262
+ self .coords [i ][j ] = table [i ][j ]
263
+ self .pieceShape = shape
264
+
265
+ def setRandomShape (self ):
266
+ self .setShape (random .randint (1 , 7 ))
267
+
268
+ def x (self , index ):
269
+ return self .coords [index ][0 ]
270
+
271
+ def y (self , index ):
272
+ return self .coords [index ][1 ]
273
+
274
+ def setX (self , index , x ):
275
+ self .coords [index ][0 ] = x
276
+
277
+ def setY (self , index , y ):
278
+ self .coords [index ][1 ] = y
279
+
280
+ def minX (self ):
281
+ m = self .coords [0 ][0 ]
282
+ for i in range (4 ):
283
+ m = min (m , self .coords [i ][0 ])
284
+ return m
285
+
286
+ def maxX (self ):
287
+ m = self .coords [0 ][0 ]
288
+ for i in range (4 ):
289
+ m = max (m , self .coords [i ][0 ])
290
+ return m
291
+
292
+ def minY (self ):
293
+ m = self .coords [0 ][1 ]
294
+ for i in range (4 ):
295
+ m = min (m , self .coords [i ][1 ])
296
+ return m
297
+
298
+ def maxY (self ):
299
+ m = self .coords [0 ][1 ]
300
+ for i in range (4 ):
301
+ m = max (m , self .coords [i ][1 ])
302
+ return m
303
+
304
+ def rotateLeft (self ):
305
+ if self .pieceShape == ShapeForm .SquareShape :
306
+ return self
307
+ result = Shape ()
308
+ result .pieceShape = self .pieceShape
309
+ for i in range (4 ):
310
+ result .setX (i , self .y (i ))
311
+ result .setY (i , - self .x (i ))
312
+ return result
313
+
314
+ def rotateRight (self ):
315
+ if self .pieceShape == ShapeForm .SquareShape :
316
+ return self
317
+ result = Shape ()
318
+ result .pieceShape = self .pieceShape
319
+ for i in range (4 ):
320
+ result .setX (i , - self .y (i ))
321
+ result .setY (i , self .x (i ))
322
+ return result
323
+
324
+
325
+ if __name__ == '__main__' :
326
+ app = QApplication ([])
327
+ tetris = Tetris ()
328
+ sys .exit (app .exec_ ())
0 commit comments