Skip to content

Commit 94912d7

Browse files
author
maicss
committed
Update 俄罗斯方块游戏.md
1 parent e523b41 commit 94912d7

File tree

1 file changed

+50
-25
lines changed

1 file changed

+50
-25
lines changed

俄罗斯方块游戏.md

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -514,45 +514,53 @@ if __name__ == '__main__':
514514
sys.exit(app.exec_())
515515
```
516516
The game is simplified a bit so that it is easier to understand. The game starts immediately after it is launched. We can pause the game by pressing the p key. The Space key will drop the tetris piece instantly to the bottom. The game goes at constant speed, no acceleration is implemented. The score is the number of lines that we have removed.
517-
517+
```
518518
self.tboard = Board(self)
519519
self.setCentralWidget(self.tboard)
520+
```
520521
An instance of the Board class is created and set to be the central widget of the application.
521-
522+
```
522523
self.statusbar = self.statusBar()
523524
self.tboard.msg2Statusbar[str].connect(self.statusbar.showMessage)
525+
```
524526
We create a statusbar where we will display messages. We will display three possible messages: the number of lines already removed, the paused message, or the game over message. The msg2Statusbar is a custom signal that is implemented in the Board class. The showMessage() is a built-in method that displays a message on a statusbar.
525-
527+
```
526528
self.tboard.start()
529+
```
527530
This line initiates the game.
528-
531+
```
529532
class Board(QFrame):
530533
531534
msg2Statusbar = pyqtSignal(str)
532535
...
536+
```
533537
A custom signal is created. The msg2Statusbar is a signal that is emitted when we want to write a message or the score to the statusbar.
534-
538+
```
535539
BoardWidth = 10
536540
BoardHeight = 22
537541
Speed = 300
542+
```
538543
These are Board's class variables. The BoardWidth and the BoardHeight define the size of the board in blocks. The Speed defines the speed of the game. Each 300 ms a new game cycle will start.
539-
544+
```
540545
...
541546
self.curX = 0
542547
self.curY = 0
543548
self.numLinesRemoved = 0
544549
self.board = []
545550
...
551+
```
546552
In the initBoard() method we initialize some important variables. The self.board variable is a list of numbers from 0 to 7. It represents the position of various shapes and remains of the shapes on the board.
547-
553+
```
548554
def shapeAt(self, x, y):
549555
return self.board[(y * Board.BoardWidth) + x]
556+
```
550557
The shapeAt() method determines the type of a shape at a given block.
551-
558+
```
552559
def squareWidth(self):
553560
return self.contentsRect().width() // Board.BoardWidth
561+
```
554562
The board can be dynamically resized. As a consequence, the size of a block may change. The squareWidth() calculates the width of the single square in pixels and returns it. The Board.BoardWidth is the size of the board in blocks.
555-
563+
```
556564
for i in range(Board.BoardHeight):
557565
for j in range(Board.BoardWidth):
558566
shape = self.shapeAt(j, Board.BoardHeight - i - 1)
@@ -561,8 +569,9 @@ for i in range(Board.BoardHeight):
561569
self.drawSquare(painter,
562570
rect.left() + j * self.squareWidth(),
563571
boardTop + i * self.squareHeight(), shape)
572+
```
564573
The painting of the game is divided into two steps. In the first step, we draw all the shapes, or remains of the shapes that have been dropped to the bottom of the board. All the squares are remembered in the self.board list variable. The variable is accessed using the shapeAt() method.
565-
574+
```
566575
if self.curPiece.shape() != Tetrominoe.NoShape:
567576
568577
for i in range(4):
@@ -572,24 +581,29 @@ if self.curPiece.shape() != Tetrominoe.NoShape:
572581
self.drawSquare(painter, rect.left() + x * self.squareWidth(),
573582
boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
574583
self.curPiece.shape())
584+
```
575585
The next step is the drawing of the actual piece that is falling down.
576-
586+
```
577587
elif key == Qt.Key_Right:
578588
self.tryMove(self.curPiece, self.curX + 1, self.curY)
589+
```
579590
In the keyPressEvent() method we check for pressed keys. If we press the right arrow key, we try to move the piece to the right. We say try because the piece might not be able to move.
580-
591+
```
581592
elif key == Qt.Key_Up:
582593
self.tryMove(self.curPiece.rotateLeft(), self.curX, self.curY)
594+
```
583595
The Up arrow key will rotate the falling piece to the left.
584-
596+
```
585597
elif key == Qt.Key_Space:
586598
self.dropDown()
599+
```
587600
The Space key will drop the falling piece instantly to the bottom.
588-
601+
```
589602
elif key == Qt.Key_D:
590603
self.oneLineDown()
604+
```
591605
Pressing the d key, the piece will go one block down. It can be used to accellerate the falling of a piece a bit.
592-
606+
```
593607
def tryMove(self, newPiece, newX, newY):
594608
595609
for i in range(4):
@@ -608,8 +622,9 @@ def tryMove(self, newPiece, newX, newY):
608622
self.curY = newY
609623
self.update()
610624
return True
625+
```
611626
In the tryMove() method we try to move our shapes. If the shape is at the edge of the board or is adjacent to some other piece, we return False. Otherwise we place the current falling piece to a new position.
612-
627+
```
613628
def timerEvent(self, event):
614629
615630
if event.timerId() == self.timer.timerId():
@@ -622,14 +637,16 @@ def timerEvent(self, event):
622637
623638
else:
624639
super(Board, self).timerEvent(event)
640+
```
625641
In the timer event, we either create a new piece after the previous one was dropped to the bottom or we move a falling piece one line down.
626-
642+
```
627643
def clearBoard(self):
628644
629645
for i in range(Board.BoardHeight * Board.BoardWidth):
630646
self.board.append(Tetrominoe.NoShape)
647+
```
631648
The clearBoard() method clears the board by setting Tetrominoe.NoShape at each block of the board.
632-
649+
```
633650
def removeFullLines(self):
634651
635652
numFullLines = 0
@@ -656,8 +673,9 @@ def removeFullLines(self):
656673
657674
numFullLines = numFullLines + len(rowsToRemove)
658675
...
676+
```
659677
If the piece hits the bottom, we call the removeFullLines() method. We find out all full lines and remove them. We do it by moving all lines above the current full line to be removed one line down. Notice that we reverse the order of the lines to be removed. Otherwise, it would not work correctly. In our case we use a naive gravity. This means that the pieces may be floating above empty gaps.
660-
678+
```
661679
def newPiece(self):
662680
663681
self.curPiece = Shape()
@@ -671,8 +689,9 @@ def newPiece(self):
671689
self.timer.stop()
672690
self.isStarted = False
673691
self.msg2Statusbar.emit("Game over")
692+
```
674693
The newPiece() method creates randomly a new tetris piece. If the piece cannot go into its initial position, the game is over.
675-
694+
```
676695
class Tetrominoe(object):
677696
678697
NoShape = 0
@@ -683,10 +702,11 @@ class Tetrominoe(object):
683702
SquareShape = 5
684703
LShape = 6
685704
MirroredLShape = 7
705+
```
686706
The Tetrominoe class holds names of all possible shapes. We have also a NoShape for an empty space.
687707

688708
The Shape class saves information about a tetris piece.
689-
709+
```
690710
class Shape(object):
691711
692712
coordsTable = (
@@ -695,15 +715,19 @@ class Shape(object):
695715
...
696716
)
697717
...
718+
```
698719
The coordsTable tuple holds all possible coordinate values of our tetris pieces. This is a template from which all pieces take their coordinate values.
699-
720+
```
700721
self.coords = [[0,0] for i in range(4)]
722+
```
701723
Upon creation we create an empty coordinates list. The list will save the coordinates of the tetris piece.
702724

703-
Coordinates
704-
Figure: Coordinates
705-
The above image will help understand the coordinate values a bit more. For example, the tuples (0, -1), (0, 0), (-1, 0), (-1, -1) represent a Z-shape. The diagram illustrates the shape.
725+
坐标系示意图:
706726

727+
![coordinates](./images/11-coordinates.png)
728+
729+
The above image will help understand the coordinate values a bit more. For example, the tuples (0, -1), (0, 0), (-1, 0), (-1, -1) represent a Z-shape. The diagram illustrates the shape.
730+
```
707731
def rotateLeft(self):
708732
709733
if self.pieceShape == Tetrominoe.SquareShape:
@@ -718,6 +742,7 @@ def rotateLeft(self):
718742
result.setY(i, -self.x(i))
719743
720744
return result
745+
```
721746
The rotateLeft() method rotates a piece to the left. The square does not have to be rotated. That is why we simply return the reference to the current object. A new piece is created and its coordinates are set to the ones of the rotated piece.
722747

723748
![Tetris](./images/11-tetris.png)

0 commit comments

Comments
 (0)