Skip to content

Commit 55cd1fa

Browse files
committed
m
1 parent 898160e commit 55cd1fa

File tree

3 files changed

+330
-1
lines changed

3 files changed

+330
-1
lines changed

yeke/README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77
+ [py-plane](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-plane) :用 Python 实现微信版飞机大战
88
+ [py-rain](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-rain) :用 Python 实现黑客帝国中的数字雨落既视感
99
+ [py-facepalm](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-facepalm) :用 Python 画一个捂脸表情
10+
+ [py-tetris](https://github.com/JustDoPython/python-examples/tree/master/yeke/py-tetris) :用 Python 写个俄罗斯方块
1011

1112
---
1213

1314
从小白到工程师的学习之路
1415

1516
关注公众号:python 技术,回复"python"一起学习交流
1617

17-
![](http://favorites.ren/assets/images/python.jpg)
18+
![](http://favorites.ren/assets/images/python.jpg)

yeke/py-tetris/__init__.py

Whitespace-only changes.

yeke/py-tetris/tetris.py

+328
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
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

Comments
 (0)