Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

Lines changed: 2 additions & 1 deletion
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

Lines changed: 328 additions & 0 deletions
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)

AltStyle によって変換されたページ (->オリジナル) /