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