JustToThePoint English Website Version
JustToThePoint en español
JustToThePoint in Thai

Tetris

Introduction

Tetris is a puzzle video game where players complete lines by moving and rotating differently shaped pieces. These geometric shapes called “tetrominoes” fall down onto a playing field and the player has to arrange them to form gapless lines. You should sit in meditation for 20 minutes a day, unless you’re too busy, then you should sit for an hour. If Tetris has taught me anything, it’s that errors pile up & accomplishments disappear. Everybody has a plan until they get punched in the mouth, Mike Tyson

The Color Class

Tetriminos or tetriminoes are the pieces used in every Tetris game. They come in seven shapes, all of which can be moved, rotated, and dropped. They have an area of four colored squares or boxes. We will draw a thin highlight on every box, too.

Let’s create a class for the different colors that the pieces can have.

import pygame, configparser, sys, random, time, enum
from pygame.locals import *

This code is based on Making Games with Python & Pygame, Tetromino by Al Sweigart, al@inventwithpython.com, http://inventwithpython.com/pygame, Creative Commons BY-NC-SA 3.0 US and it is released under the same license.

BOARDWIDTH = 14
BOARDHEIGHT = 22
BLANK = '.'

# Color         R    G    B
WHITE       = (255, 255, 255)
GRAY        = (185, 185, 185)
BLACK       = (  0,   0,   0)
NAVYBLUE =    (  0,  0,  128)
BORDERCOLOR = NAVYBLUE
BGCOLOR = BLACK

class Color(enum.Enum):
    BLUE = 'Blue'
    GREEN = 'Green'
    RED = 'Red'
    YELLOW = 'Yellow'
    PINK = 'Pink'

    @classmethod
    def getRandom(cls):
    """ It returns a random color from our list of predefined colors."""
        return random.choice([Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW, Color.PINK])
    @classmethod
    def allColors(cls):
    """ It returns a list with our five predefined colors."""
        return [Color.BLUE, Color.GREEN, Color.RED, Color.YELLOW, Color.PINK]

    def getColor(self):
    """ It returns the corresponding color, a tuple with the RGB values."""
        if self==Color.BLUE:
            return (0, 0, 155)
        elif self==Color.GREEN:
            return (0, 155, 0)
        elif self==Color.RED:
            return (155, 0, 0)
        elif self==Color.YELLOW:
            return (155, 155, 0)
        elif self==Color.PINK:
            return (255, 20, 147)

    def getLightColor(self):
    """ It returns the corresponding lighter version of the color."""
        if self==Color.BLUE:
            return (20, 20, 175)
        elif self==Color.GREEN:
            return (20, 175, 20)
        elif self==Color.RED:
            return (175, 20, 20)
        elif self==Color.YELLOW:
            return (175, 175, 20)
        elif self==Color.PINK:
            return (255, 182, 193)

The Board Class

The board class encapsulates a two-dimensional array or matrix. Its main purpose is to track the rectangular spaces or boxes where all the falling pieces have landed.

class Board:
    def __init__(self):
    """ It creates the blank board array."""
        self.board = []
        for i in range(BOARDWIDTH):
            self.board.append([BLANK] * BOARDHEIGHT)

    def getElement(self, i, j):
    """ It returns the element in the i column and j row."""
        return self.board[i][j] 

    def addToBoard(self, piece):
    """ It adds a piece to our board."""
        for x in range(Piece.PIECESIZE):
            for y in range(Piece.PIECESIZE):
                if not piece.isBlank(x, y): # Each piece is a 5 x 5 board, more specifically a 5 x 5 matrix of blocks; most of them are blanks, and four of them have a particular color (Illustration 1a). Observe that piece.x, piece.y are the top left corner of the piece. 
                    self.board[x + piece.x][y + piece.y] = piece.color

    def isCompleteLine(self, y):
        # It returns True if the row or line is filled with colored boxes with no gaps.
        for x in range(BOARDWIDTH):
            if self.board[x][y] == BLANK:
                return False
        return True

Playing ‘Tetris’ for 15 minutes is like meditation, cPlaying ‘Tetris’ for 15 minutes is like meditation, Ezra Koenig

    def removeCompleteLines(self):
        # It removes any completed lines on the board by moving everything above them down (Observe that the word "down" is relative). It returns the number of completed lines.
        numLinesRemoved = 0
        y = BOARDHEIGHT - 1 # We start "y" at the bottom of the board
        while y >= 0:
            if self.isCompleteLine(y):
            # If the row or line "y" is filled with colored boxes with no gaps, we remove it by pulling all boxes down by one line. 
                for pullDownY in range(y, 0, -1):
                    for x in range(BOARDWIDTH):
                    # We copy the "pullDownY-1" row to the "pullDownY" row.
                        self.board[x][pullDownY] = self.board[x][pullDownY-1]
                # After that, we set all boxes on the top row or line to blank.
                for x in range(BOARDWIDTH):
                    self.board[x][0] = BLANK
                numLinesRemoved += 1
            else:
                y -= 1 # Let's move on to check the next row up.
        return numLinesRemoved

Tetris teaches you that when you try to fit in, you’ll end up disappearing

    def drawBoard(self, game):
    """ It draws the game's board."""
        # First, we draw the border around the board. The board dimensions are (BOARDWIDTH * game.SQUARESIZE) * (BOARDHEIGHT * game.SQUARESIZE) and its top left coordinates are game.XMARGIN, game.TOPMARGIN (Ilustration 1.c).
        pygame.draw.rect(game.screen, BORDERCOLOR, (game.XMARGIN - 3, game.TOPMARGIN - 7, (BOARDWIDTH * game.SQUARESIZE) + 8, (BOARDHEIGHT * game.SQUARESIZE) + 8), 5)

        # Second, we fill the board with BGCOLOR color.
        pygame.draw.rect(game.screen, BGCOLOR, (game.XMARGIN, game.TOPMARGIN, game.SQUARESIZE * BOARDWIDTH, game.SQUARESIZE * BOARDHEIGHT))
        # Finally, we draw all the individual boxes of the board.
        for x in range(BOARDWIDTH):
            for y in range(BOARDHEIGHT):
                self.drawBox(x, y, self.board[x][y], game)

    def drawBox(self, x, y, color, game):
    """ It draws the box defined by its x and y coordinates on the board."""
        pixelx, pixely =  game.convertToPixelCoords(x, y) # It gets the box's pixel coordinates given its board coordinates specified by x and y.
        if color == BLANK: 
            # If the box corresponds to a BLANK box, we draw a semitransparent rectangle. There is no easy way to draw semitransparent rectangles in pygame. This is just a solution.
            shape_surf = pygame.Surface(pygame.Rect(pixelx + 1, pixely + 1, game.SQUARESIZE - 1, game.SQUARESIZE - 1).size, pygame.SRCALPHA)
            # Observe that the rectangle's top-left coordinates are pixelx and pixely and its size are game.SQUARESIZE x game.SQUARESIZE.        
            pygame.draw.rect(shape_surf, (255, 255, 255, 12), shape_surf.get_rect())
            game.screen.blit(shape_surf, Rect(pixelx + 1, pixely + 1, game.SQUARESIZE - 1, game.SQUARESIZE - 1))
            pygame.draw.rect(game.screen, BGCOLOR, (pixelx + 1, pixely + 1, game.SQUARESIZE - 2, game.SQUARESIZE - 2))
        else:
            # Otherwise, we draw a highlighted box by drawing it with its color...
            pygame.draw.rect(game.screen, color.getColor(), (pixelx + 1, pixely + 1, game.SQUARESIZE - 1, game.SQUARESIZE - 1))
            # ... and on top of it, we draw a slightly smaller box with a lighter version of the same color.
            pygame.draw.rect(game.screen, color.getLightColor(), (pixelx + 1, pixely + 1, game.SQUARESIZE - 4, game.SQUARESIZE - 4))

The Class Piece

Each piece has a shape (I, O, T, S, Z, J, and L), rotation, and a color. They can be seen as 5*5 boards or arrays of blocks where most of them are blanks and four of them have a particular color.

class Piece:
    PIECESIZE = 5
    def __init__(self):
    """ It creates a new random piece."""
        self.type= random.randint(0, 6) # The seven tetrominoes are I, O, T, S, Z, J, and L.
        self.rotation = random.randint(0, len(self.getShapes()) - 1) # The number of possible rotations depends on the tetromino's type.
        self.x =  int(BOARDWIDTH / 2) - int(Piece.PIECESIZE / 2) # self.x, self.y are the top left coordinates of the piece. It starts in the middle of the board...
        self.y = -2 # ... and two pixels above the board.
        self.color =  Color.getRandom() # It generates a random color for the new piece.

    def isBlank(self, x, y):
    """ It returns True if the piece has a blank in the (x, y) position. Otherwise, it returns False."""    
        return self.getShape()[x][y] == BLANK

    def getShape(self):
    """ It returns the shape of the piece given its type and rotation."""
        return self.getShapes()[self.rotation]
    
    def getShapes(self):
    """ It returns the array of shapes (corresponding to every possible rotation) of the piece.""" 
        if self.type==0:
            return [['.....',
                     '.....',
                     '..OO.',
                     '.OO..',
                     '.....'],
                    ['.....',
                     '..O..',
                     '..OO.',
                     '...O.',
                     '.....']]
        elif self.type==1:
            return [['.....',
                     '.....',
                     '.OO..',
                     '..OO.',
                     '.....'],
                    ['.....',
                     '..O..',
                     '.OO..',
                     '.O...',
                     '.....']]
        elif self.type==2:
            return [['..O..',
                     '..O..',
                     '..O..',
                     '..O..',
                     '.....'],
                    ['.....',
                     '.....',
                     'OOOO.',
                     '.....',
                     '.....']]
        elif self.type==3:
            return [['.....',
                     '.....',
                     '.OO..',
                     '.OO..',
                     '.....']]
        elif self.type==4:
            return [['.....',
                     '.O...',
                     '.OOO.',
                     '.....',
                     '.....'],
                    ['.....',
                     '..OO.',
                     '..O..',
                     '..O..',
                     '.....'],
                    ['.....',
                     '.....',
                     '.OOO.',
                     '...O.',
                     '.....'],
                    ['.....',
                     '..O..',
                     '..O..',
                     '.OO..',
                     '.....']]
        elif self.type==5:
            return [['.....',
                     '...O.',
                     '.OOO.',
                     '.....',
                     '.....'],
                    ['.....',
                     '..O..',
                     '..O..',
                     '..OO.',
                     '.....'],
                    ['.....',
                     '.....',
                     '.OOO.',
                     '.O...',
                     '.....'],
                    ['.....',
                     '.OO..',
                     '..O..',
                     '..O..',
                     '.....']]
        elif self.type==6:
            return [['.....',
                     '..O..',
                     '.OOO.',
                     '.....',
                     '.....'],
                    ['.....',
                     '..O..',
                     '..OO.',
                     '..O..',
                     '.....'],
                    ['.....',
                     '.....',
                     '.OOO.',
                     '..O..',
                     '.....'],
                    ['.....',
                     '..O..',
                     '.OO..',
                     '..O..',
                     '.....']]

    def isOnBoard(self, x, y):
    """ It returns True if the actual coordinates x, y passed as arguments represent values that exist on the board."""
        return x >= 0 and x < BOARDWIDTH and y < BOARDHEIGHT

    def isValidPosition(self, board, adjX=0, adjY=0):
        # It returns True if the piece (its top left position is self.x, self.y) plus some offset (adjX, adjY) is within the board and not colliding with a fallen piece that has already been saved on the game's board.
        for x in range(Piece.PIECESIZE):
            for y in range(Piece.PIECESIZE):
                isAboveBoard = y + self.y + adjY < 0
                if isAboveBoard or self.isBlank(x, y): # If the box is above the board (it is just starting to fall on the board) or the box is a blank, we could continue because it is still possible to be a valid position. 
                    continue
                if not self.isOnBoard(x + self.x + adjX, y + self.y + adjY): # If the box's coordinates do not exist on the board, it is not a valid position.
                    return False
                if board.getElement(x + self.x + adjX, y + self.y + adjY) != BLANK: # If the box's coordinates are already taken by a fallen piece that has already been saved on the game's board, it is not a valid position either.
                    return False
        return True

Life is like Tetris; if it doesn’t fit, just flip it over, Sabine Hein

    def moveLeft(self, board):
    """ It tries to move the piece to the left. It first checks that it is a valid position (adjX=-1), and then if this is the case, it updates its "x" coordinate and returns True. Otherwise, it returns False."""
        if self.isValidPosition(board, adjX=-1):
            self.x -= 1
            return True
        
        return False

    def moveRight(self, board):
    """ It tries to move the piece to the right. It first checks that it is a valid position (adjX=+1), and then if this is the case, it updates its "x" coordinate and returns True. Otherwise, it returns False."""
        if self.isValidPosition(board, adjX=+1):
            self.x += 1
            return True
        
        return False

    def moveDown(self, board):
    """ It tries to move the piece down. It first checks that it is a valid position (adjY=+1), and then if this is the case, it updates its "y" coordinate and returns True. Otherwise, it returns False."""
        if self.isValidPosition(board, adjY=+1):
            self.y += 1
            return True
        
        return False

    def rotate(self, board):
    """ It tries to rotate the piece. If the end position is not a valid one, it undoes the rotation.""" 
        self.rotation = (self.rotation + 1) % len(self.getShapes())
        if not self.isValidPosition(board):
            self.rotation = (self.rotation - 1) % len(self.getShapes())

    def rotate2(self, board):
    """ It tries to rotate the piece on the other direction. If the end position is not a valid one, it undoes the rotation."""
        self.rotation = (self.rotation - 1) % len(self.getShapes())
        if not self.isValidPosition(board):
            self.rotation = (self.rotation + 1) % len(self.getShapes())

    def drawPiece(self, game):
    """ It draws the piece."""
        shapeToDraw = self.getShape()
        pixelx, pixely = game.convertToPixelCoords(self.x, self.y)
        self.drawBox(game, pixelx, pixely, shapeToDraw)

    def drawBox(self, game, pixelx, pixely, shapeToDraw):
        # Let's draw each of the boxes that make up our piece.
        for x in range(Piece.PIECESIZE):
            for y in range(Piece.PIECESIZE):
                if shapeToDraw[x][y] != BLANK: # We only need to consider the colored boxes of the piece.
                    boxx, boxy = pixelx + (x * game.SQUARESIZE), pixely + (y * game.SQUARESIZE)
                    # We draw a highlighted box by drawing it with its color...
                    pygame.draw.rect(game.screen, self.color.getColor(), (boxx + 1, boxy + 1, game.SQUARESIZE - 1, game.SQUARESIZE - 1))
                    # ... and on top of it, we draw a slightly smaller box with a lighter version of the color.
                    pygame.draw.rect(game.screen, self.color.getLightColor(), (boxx + 1, boxy + 1, game.SQUARESIZE - 4, game.SQUARESIZE - 4))

    def drawNextPiece(self, game):
    """ It draws the next piece to fall down."""
        # Firstly, we draw the "next" text on the window's right.
        game.drawText('Next:', game.font, game.XMARGIN*5/4 + BOARDWIDTH * game.SQUARESIZE, 80)
        # Secondly, we draw the "next" piece on the window's right below the "next" text.
        self.drawBox(game, pixelx=game.XMARGIN*5/4 + BOARDWIDTH * game.SQUARESIZE, pixely=200, shapeToDraw=self.getShape())

The Game class

class Game: # It represents the game itself
    def __init__(self):
        self.running = True # It indicates that the game is still running.
        pygame.init() # It initializes all imported pygame modules.
        self.fpsClock = pygame.time.Clock( ) # A pygame.time.Clock object helps us to make sure our program runs at a certain FPS.
        self.config = configparser.ConfigParser() # We create an instance of the main configuration parser.

We will use the ConfigParser module to manage a user-editable configuration file for our application. It provides a structure similar to what’s found in Microsoft Windows INI files. Our config file tetris.ini is quite simple:

[BASIC] 
# The file consists of sections, each of which contains keys with values. 
WINDOWWIDTH = 1280 
WINDOWHEIGHT = 800 
FPS = 30 
TIMEOUT = 4 
SQUARESIZE = 35 
SIDEWAYSFREQ = 0.15 
DOWNFREQ = 0.15
        self.config.read("tetris.ini") # We read the configuration file.
        self.font = pygame.font.Font("resources/CHERI.TTF", 30) # pygame.font.Font allows us to render fonts in the game.
        self.bigfont = pygame.font.Font("resources/CHERL.TTF", 30)
        self.WINDOWWIDTH = int(self.config['BASIC']['WINDOWWIDTH'])
        self.SQUARESIZE = int(self.config['BASIC']['SQUARESIZE'])
        self.WINDOWHEIGHT = int(self.config['BASIC']['WINDOWHEIGHT'])
        self.screen = pygame.display.set_mode((self.WINDOWWIDTH, self.WINDOWHEIGHT)) # It sets the display mode and creates an instance of the pygame.Surface class.
        pygame.display.set_caption('Tetris') # It sets the current window caption.
        self.fps = int(self.config['BASIC']['FPS']) # This is our frame rate. It is expressed in frames per second or FPS. It is the frequency or rate at which consecutive images or frames are captured or displayed in the game.
        self.XMARGIN = int((self.WINDOWWIDTH - BOARDWIDTH * self.SQUARESIZE) / 2) # XMARGIN + BOARDWIDTH * SQUARESIZE + XMARGIN = WINDOWWIDTH. Illustration 1.c.
        self.TOPMARGIN = self.WINDOWHEIGHT - (BOARDHEIGHT * self.SQUARESIZE) - 5
        self.board = Board() # It is an instance of our Board class.
        self.KEYDOWN =  self.KEYUP = self.PAUSE = self.LEFT = self.RIGHT = self.UP = self.DOWN = self.Q = False
        self.fallFreq = 0.27 # It indicates how many seconds should pass after we let a falling piece falls one block.
        self.score = 0
        self.SIDEWAYSFREQ = float(self.config['BASIC']['SIDEWAYSFREQ'])
        self.DOWNFREQ = float(self.config['BASIC']['DOWNFREQ'])

Every time the player pushes the left, right, or down arrow key down, the falling piece should move one box over to the left, right, or drop one box, respectively. Besides, the player can also hold down these arrow keys to keep moving the falling piece in the same direction.

The MOVESIDEWAYSFREQ and DOWNFREQ constants are set so that every MOVESIDEWAYSFREQ or DOWNFREQ seconds that passes with the left, right, or down arrow key been held down by the player, the piece will move another box over to the left or right or drop one box.

        pygame.mixer.music.load('resources/tetris.mid') # It loads a sound file.
``
The method game_loop **controls the overall flow for the entire game program**. There are three tasks: 1. Check all events. 2. Process these events and take action accordingly. 3. Draw the current state of the game so the player can see what is going on.
``` python
    def gameLoop(self):            
        fallingPiece = Piece() # We create two pieces: the falling and the next piece.
        nextPiece = Piece()
        lastFallTime = time.time() # We initialize the different timers.
        lastMoveDownTime = time.time()
        lastMoveSidewaysTime = time.time()
        lastFallTime = time.time()
        movingDown = False 
        movingLeft = False
        movingRight = False
        numLinesRemoved = 0
        pygame.mixer.music.play(-1, 0.0) # It starts playing our sound file.

        while self.running: # If the player is actually playing...
            self.check_events() # It checks all the events.
            
            if fallingPiece == None:
            # If there is no falling piece in the game, we create a new piece (a new instance of our class Piece) as nextPiece.
                fallingPiece = nextPiece
                nextPiece = Piece()
                lastFallTime = time.time() # It updates the "lastFallTime" timer.
                if not fallingPiece.isValidPosition(self.board):
                # If the fallingPiece is not in a valid position, the board is filled up and the game is over.
                    self.screen.fill(BGCOLOR)
                    pygame.mixer.music.stop()
                    self.showTextScreen("Game Over")
                    sys.exit()
            if self.KEYUP:
                if self.PAUSE:
                # The user wants to pause the game. It updates the timers, fills the screen with the BGCOLOR color and the "Paused" text, stops the music, and waits for the user to press a key. 
                    self.screen.fill(BGCOLOR)
                    pygame.mixer.music.stop()
                    self.showTextScreen('Paused')
                    pygame.mixer.music.play(-1, 0.0)
                    lastFallTime = time.time()
                    lastMoveDownTime = time.time()
                    lastMoveSidewaysTime = time.time()
                elif self.LEFT:
                # If the user releases the arrow or the correspoding WASD key (A, D, and S), we will set the movingLeft, movingRight, or movingDown variables back to False. It indicates that the player no longer wants to move the falling piece in those directions.
                    movingLeft = False
                elif self.RIGHT:
                    movingRight = False
                elif self.DOWN:
                    movingDown = False
            if self.KEYDOWN:
                if self.LEFT and fallingPiece.moveLeft(self.board):
                # If the user presses the left arrow or the 'A' key and this movement is a valid one, we move the falling piece to the left, update the movingLeft and movingRight variables, and the lastMoveSidewayTime timer.
                    movingLeft = True
                    movingRight = False
                    lastMoveSidewaysTime = time.time()
                elif self.RIGHT and fallingPiece.moveRight(self.board):
                # If the user presses the right arrow or the 'D' key and this movement is a valid one, we move the falling piece to the right, update the movingLeft and movingRight variables, and the lastMoveSidewayTime timer.
                    movingRight = True
                    movingLeft = False
                    lastMoveSidewaysTime = time.time()
                elif self.UP:
                # If the user presses the "up" arrow or the 'W' key, we rotate the falling piece.
                    fallingPiece.rotate(self.board)
                elif self.Q: 
                # If the user presses the 'Q' key, we rotate the falling piece in the opposite direction.
                    fallingPiece.rotate2(self.board)
                # If the user presses the "down" arrow or the 'S' key, we move down the falling piece and update the lastMoveDownTime timer.
                elif self.DOWN:
                    movingDown = True
                    if fallingPiece.moveDown(self.board):
                        lastMoveDownTime = time.time()

            if (movingLeft or movingRight) and time.time() - lastMoveSidewaysTime > self.SIDEWAYSFREQ:
            # If the user has pressed down on the left or the right arrow key and he continues to hold down the arrow key, and he does so for longer than self.SIDEWAYSFREQ, we should move the falling piece to the left or right and update the lastMoveSidewaysTime timer (the movement should be a valid one). 
                if (movingLeft and fallingPiece.moveLeft(self.board)) or (movingRight and fallingPiece.moveRight(self.board)):
                    lastMoveSidewaysTime = time.time()
            if movingDown and time.time() - lastMoveDownTime > self.DOWNFREQ and fallingPiece.moveDown(self.board):
           # If the user has pressed down on the down arrow key and he continues to hold down the arrow key, and he does so for longer than self.DOWNFREQ, we should drop or move down the falling piece and update the lastMoveDownTime timer.  
               lastMoveDownTime = time.time()

            if time.time() - lastFallTime > self.fallFreq: # It lets the falling piece fall if it is its time (self.fallFreq) to fall.  
                if not fallingPiece.moveDown(self.board):
                # If it is not possible (it is not a valid position), it means that the falling piece has already landed, so we need to add it to the board.
                    self.board.addToBoard(fallingPiece)
                    numLinesRemoved = self.board.removeCompleteLines()
                    # We need to erase all completed lines on the board and update the score and the fallFreq variables.
                    self.updateScore(numLinesRemoved)
                    fallingPiece = None
                    movingDown = False
                    
                else:
                    lastFallTime = time.time()

            self.drawGame(nextPiece, fallingPiece) # Finally, we draw the current state of the game.
            self.reset_keys() # We reset the keys as we have already processed the key's associated event.

    def drawGame(self, nextPiece, fallingPiece):
    """ It draws the current state of the game."""
        self.screen.fill(BGCOLOR) # It fills the screen with the BGCOLOR color so we can start from scratch.
        self.board.drawBoard(self) # It draws the board.
        self.drawScore() # It draws the score.
        nextPiece.drawNextPiece(self) # It draws the next piece.
        if fallingPiece!=None: # If there is a falling piece, it draws it.
            fallingPiece.drawPiece(self)
            
        pygame.display.update() # It will update the full display surface to the screen.
        self.fpsClock.tick(self.fps) # It just sets up how fast our game should run.
    
    def check_events(self): # Pygame handles all its event messaging through an event queue. This method checks all these events. 
        for event in pygame.event.get(): # It gets events from the queue.
            if event.type == pygame.QUIT:
                self.running = False
                pygame.mixer.music.stop()
                sys.exit() 

            if event.type == KEYUP:
                self.KEYUP = True
                if (event.key == K_p):
                    self.PAUSE = True
                elif (event.key == K_LEFT or event.key == K_a):
                    self.LEFT = True
                elif (event.key == K_RIGHT or event.key == K_d):
                    self.RIGHT = True
                elif (event.key == K_DOWN or event.key == K_s):
                    self.DOWN = True
            elif event.type == KEYDOWN:
                self.KEYDOWN = True
                if event.key == K_LEFT or event.key == K_a:
                    self.LEFT = True
                elif event.key == K_RIGHT or event.key == K_d:
                    self.RIGHT = True
                elif event.key == K_UP or event.key == K_w:
                    self.UP = True
                elif event.key == K_DOWN or event.key == K_s:
                    self.DOWN = True
                elif event.key == K_q:
                    self.Q = True                    

    def reset_keys(self): # It resets all the keys
        self.MOUSEBUTTONUP = False
        self.KEYUP = self.PAUSE = False
        self.KEYDOWN = self.LEFT = self.RIGHT = self.UP = self.DOWN = self.Q = False

    def convertToPixelCoords(self, x, y):
    """ It converts the given x, y board coordinates to pixel coordinates on the screen. Illustration 3, 1.d."""
        return (self.XMARGIN + (x * self.SQUARESIZE)), (self.TOPMARGIN + (y * self.SQUARESIZE))

    def updateScore(self, numLinesRemoved):
    """ It calculates our new score and fallFreq variables based on the number of rows or lines removed from the board."""
        self.score += numLinesRemoved
        self.fallFreq -= 0.01 * numLinesRemoved

    def drawText(self, text, font, x, y):
    """ It draws "text" using the font passed as an argument with a shadow."""
        textSurf = font.render(text, True, GRAY)
        textRect = textSurf.get_rect()
        textRect.topleft = (x, y)
        self.screen.blit(textSurf, textRect)
        textSurf = font.render(text, True, WHITE)
        textRect = textSurf.get_rect()
        textRect.topleft = (x-3, y-3)
        self.screen.blit(textSurf, textRect)

    def drawScore(self):
         """ It draws the score."""
        self.drawText('Score: %s' % self.score, self.font, self.XMARGIN*5/4 + BOARDWIDTH * game.SQUARESIZE, 20)

    def showTextScreen(self, text):
        """ It draws "text" in the middle of the screen with a big font, the "Press a key to play" text with a normal font, and it will wait until the user presses a key."""
        self.drawText(text, self.bigfont, int(self.WINDOWWIDTH / 2) - 3, int(self.WINDOWHEIGHT / 2) - 3)
        self.drawText('Press a key to play.', self.font, int(self.WINDOWWIDTH / 2), int(self.WINDOWHEIGHT / 2) + 100)
        
        self.reset_keys()
        while not self.KEYUP:
            self.check_events()
            pygame.display.update()
            self.fpsClock.tick()

if __name__ == '__main__':
    game = Game() # The main function is quite simple. We create an instance of our Game class.
    game.gameLoop() # We call the game's game_loop function.
Bitcoin donation

JustToThePoint Copyright © 2011 - 2024 Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun. Social Issues, Join us.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.