---
title: "Coding Exercise: Build Tetris in Python using Pygame"
description: "Use our simple coding exercises to improve your Python skils! Learn how to build a simple game in Python by using one of it's libraries: pygame"
---

[Byte Academy Blog](https://www.byteacademy.co/blog)

# [Coding Exercise: Build Tetris in Python using Pygame](https://www.byteacademy.co/blog/tetris-pygame-python)

 Written by [Sanoj Mathew](https://www.byteacademy.co/blog/author/sanojm) | Aug 17, 2020 9:12:00 AM

[Apply for Byte Academy's Coding Bootcamp with ISA options](https://application.byteacademy.co/)

pygame is a Free and Open Source[python](https://www.python.org/) programming language library for making multimedia applications like games built on top of the excellent[SDL](http://www.libsdl.org) (Simple DirectMedia Layer) library. Simple DirectMedia Layer is a cross-platform development library designed to provide low-level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D. Like SDL, pygame is highly portable and runs on nearly every platform and operating system. Millions of people have downloaded pygame itself, which is a whole lot of bits flying across the interwebs.

pygame.org (the website) welcomes all Python game, art, music, sound, video and multimedia projects.

 

**History of Pygame**

Pygame was originally written by Pete Shinners to replace PySDL after its development stalled. It has been a[community](https://en.wikipedia.org/wiki/Free_software_community) project since 2000 and is released under the[open source](https://en.wikipedia.org/wiki/Open_source)[free software](https://en.wikipedia.org/wiki/Free_software)[GNU Lesser General Public License](https://en.wikipedia.org/wiki/GNU_Lesser_General_Public_License).

 

**Pygame Installation**

Pygame requires Python. The best way to install pygame is with the [pip](https://pip.pypa.io/en/stable/) tool (which is what python uses to install packages). Note, this comes with python in recent versions. We use the --user flag to tell it to install into the home directory, rather than globally.

Installation on Windows

For Windows, make sure you add Python 3.6.1 in the PATH, from there, you run the below command:

**python3 -m pip install -U pygame --user**

Installation on Ubuntu

For Ubuntu and many Linux distributions, they have their own Pygame package. Run the below command for these operating systems:

**sudo apt-get install python3-pygame**

Installation on Mac

There are issues pertaining in installation of Pygame on Mac. But it is available, if you start a virtual environment in your Mac system using the below command:

python3 -m virtualenv anenv

 Activate the virtual environment:

. ./anenv/bin/activate

The venvdotapp helps the python be a Mac ‘app’, so that the pygame window can get focus

python -m pip install venvdotapp  
venvdotapp  
python -m pip install pygame

 

  

 

**Features of pygame**

- Silliness built in. Pygame is meant to make things fun. New silliness is added every 3.1415 seconds. Silliness is a fun feature added into pygame package, in order to denote the simplicity in coding a game using pygame.
- Does not require OpenGL. With many people having broken OpenGL setups, requiring OpenGL exclusively will cut into your user base significantly. Pygame uses either opengl, directx, windib, X11, linux frame buffer, and many other different backends... including an ASCII art backend! OpenGL is often broken on linux systems, and also on windows systems - which is why professional games use multiple backends.
- Multi-core CPUs can be used easily. With dual-core CPUs common, and 8 core CPUs cheaply available on desktop systems, making use of multi-core CPUs allows you to do more in your game. Selected pygame functions release the dreaded python GIL, which is something you can do from C code.
- Uses optimized C, and Assembly code for core functions. C code is often 10-20 times faster than python code, and assembly code can easily be 100x or more times faster than python code.
- Comes with many Operating systems. Just an apt-get, emerge, pkg\_add, or yast install away.  No need to mess with installing it outside of your operating systems package manager. Comes with binary installers (and uninstallers) for Windows or MacOS X.
- Truly portable. Supports Linux (pygame comes with most mainstream linux distributions), Windows (95,98,me,2000,XP,vista, 64bit windows etc), Windows CE, BeOS, MacOS, Mac OS X, FreeBSD, NetBSD, OpenBSD, BSD/OS, Solaris, IRIX, and QNX.
- It's Simple and easy to use. Kids and adults make games with pygame.
- Does not require a GUI to use all functions. You can use pygame without a monitor - like if you want to use it just to process images, get joystick input, or play sounds.
- A small amount of code. It does not have hundreds of thousands of lines of code for things you won't use anyway. The core is kept simple, and extra things like GUI libraries, and effects are developed separately outside of pygame.

 

**Pros of Pygame**

- Easy Python syntax
- Pygame uses Python as its scripting language. Python is widely considered one of the easiest languages to grasp even for beginners.
- Very easy to understand
- The API is very straightforward.
- Good canvas system
- Pygame has a drawing system that allows the user to create and draw on an unlimited number of canvases.

 

**Cons of Pygame**

- Pygame does not scale well with large project games, but it works well with small games and hobby projects. For small-scale game development, pygame is a good option.
- You might have a hard time finding out the basic functions that you require and their workings, but most of the functions in pygame has been incorporated as small apps across. You can get github programs related to Pygame. All those functions and their descriptions will be given in their code.
- As I have said before, as there is not much description about the functions and their uses, Pygame also lacks the Pygame community and Pygame developers. Those who develop their small-scale applications in Pygame, put up in Github, but the reliability of those developers is still doubtful

- Most of the game that we play, has a certain amount of Physics, AI, networking and inputs involved. In Pygame, we won’t be having these complexities as it is not supported by Pygame.
- Pygame uses an old version of SDL, hence it can be said to be outdated. Some of the features used by SDL2 are also not supported in Pygame. But still remember Old is Gold.

 

**Let us build a Tetris game using Pygame**

 

from random import randrange as rand

import pygame, sys

import getpass

 

# The configuration

cell\_size =    18

cols =     10

rows =     22

maxfps =     30

 

colors = \[

(0,   0, 0  ),

(255, 85,  85),

(100, 200, 115),

(120, 108, 245),

(255, 140, 50 ),

(50,  120, 52 ),

(146, 202, 73 ),

(150, 161, 218 ),

(35,  35, 35)

\]

 

# Define the shapes of the single parts

tetris\_shapes = \[

    \[\[1, 1, 1\],

     \[0, 1, 0\]\],

    

    \[\[0, 2, 2\],

     \[2, 2, 0\]\],

    

    \[\[3, 3, 0\],

     \[0, 3, 3\]\],

    

    \[\[4, 0, 0\],

     \[4, 4, 4\]\],

    

    \[\[0, 0, 5\],

     \[5, 5, 5\]\],

    

    \[\[6, 6, 6, 6\]\],

    

    \[\[7, 7\],

     \[7, 7\]\]

\]

 

def rotate\_clockwise(shape):

    return \[ \[ shape\[y\]\[x\]

     for y in range(len(shape)) \]

     for x in range(len(shape\[0\]) - 1, -1, -1) \]

 

def check\_collision(board, shape, offset):

    off\_x, off\_y = offset

    for cy, row in enumerate(shape):

     for cx, cell in enumerate(row):

     try:

     if cell and board\[ cy + off\_y \]\[ cx + off\_x \]:

     return True

     except IndexError:

     return True

    return False

 

def remove\_row(board, row):

    del board\[row\]

    return \[\[0 for i in range(cols)\]\] + board

    

def join\_matrixes(mat1, mat2, mat2\_off):

    off\_x, off\_y = mat2\_off

    for cy, row in enumerate(mat2):

     for cx, val in enumerate(row):

     mat1\[cy+off\_y-1    \]\[cx+off\_x\] += val

    return mat1

 

def new\_board():

    board = \[ \[ 0 for x in range(cols) \]

     for y in range(rows) \]

    board += \[\[ 1 for x in range(cols)\]\]

    return board

 

class TetrisApp(object):

    def \_\_init\_\_(self):

     pygame.init()

     pygame.key.set\_repeat(250,25)

     self.width = cell\_size\*(cols+6)

     self.height = cell\_size\*rows

     self.rlim = cell\_size\*cols

     self.bground\_grid = \[\[ 8 if x%2==y%2 else 0 for x in range(cols)\] for y in range(rows)\]

   

     self.default\_font =  pygame.font.Font(

     pygame.font.get\_default\_font(), 12)

   

     self.screen = pygame.display.set\_mode((self.width, self.height))

     pygame.event.set\_blocked(pygame.MOUSEMOTION) # We do not need

                                               # mouse movement

                                               # events, so we

                                               # block them.

     self.next\_stone = tetris\_shapes\[rand(len(tetris\_shapes))\]

     self.init\_game()

    

    def new\_stone(self):

     self.stone = self.next\_stone\[:\]

     self.next\_stone = tetris\_shapes\[rand(len(tetris\_shapes))\]

     self.stone\_x = int(cols / 2 - len(self.stone\[0\])/2)

     self.stone\_y = 0

   

     if check\_collision(self.board,

                     self.stone,

                     (self.stone\_x, self.stone\_y)):

     self.gameover = True

    

    def init\_game(self):

     self.board = new\_board()

     self.new\_stone()

     self.level = 1

     self.score = 0

     self.lines = 0

     pygame.time.set\_timer(pygame.USEREVENT+1, 1000)

    

    def disp\_msg(self, msg, topleft):

     x,y = topleft

     for line in msg.splitlines():

     self.screen.blit(

     self.default\_font.render(

     line,

     False,

     (255,255,255),

     (0,0,0)),

     (x,y))

     y+=14

    

    def center\_msg(self, msg):

     for i, line in enumerate(msg.splitlines()):

     msg\_image =  self.default\_font.render(line, False,

     (255,255,255), (0,0,0))

   

     msgim\_center\_x, msgim\_center\_y = msg\_image.get\_size()

     msgim\_center\_x //= 2

     msgim\_center\_y //= 2

   

     self.screen.blit(msg\_image, (

       self.width // 2-msgim\_center\_x,

       self.height // 2-msgim\_center\_y+i\*22))

    

    def draw\_matrix(self, matrix, offset):

     off\_x, off\_y  = offset

     for y, row in enumerate(matrix):

     for x, val in enumerate(row):

     if val:

     pygame.draw.rect(

     self.screen,

     colors\[val\],

     pygame.Rect(

     (off\_x+x) \*

       cell\_size,

     (off\_y+y) \*

       cell\_size,

     cell\_size,

     cell\_size),0)

    

    def add\_cl\_lines(self, n):

     linescores = \[0, 40, 100, 300, 1200\]

     self.lines += n

     self.score += linescores\[n\] \* self.level

     if self.lines >= self.level\*6:

     self.level += 1

     newdelay = 1000-50\*(self.level-1)

     newdelay = 100 if newdelay < 100 else newdelay

     pygame.time.set\_timer(pygame.USEREVENT+1, newdelay)

    

    def move(self, delta\_x):

     if not self.gameover and not self.paused:

     new\_x = self.stone\_x + delta\_x

     if new\_x < 0:

     new\_x = 0

     if new\_x > cols - len(self.stone\[0\]):

     new\_x = cols - len(self.stone\[0\])

     if not check\_collision(self.board,

                         self.stone,

                         (new\_x, self.stone\_y)):

     self.stone\_x = new\_x

    def quit(self):

     self.center\_msg("Exiting...")

     pygame.display.update()

     sys.exit()

    

    def drop(self, manual):

     if not self.gameover and not self.paused:

     self.score += 1 if manual else 0

     self.stone\_y += 1

     if check\_collision(self.board,

                     self.stone,

                     (self.stone\_x, self.stone\_y)):

     self.board = join\_matrixes(

       self.board,

       self.stone,

       (self.stone\_x, self.stone\_y))

     self.new\_stone()

     cleared\_rows = 0

     while True:

     for i, row in enumerate(self.board\[:-1\]):

     if 0 not in row:

     self.board = remove\_row(

       self.board, i)

     cleared\_rows += 1

     break

     else:

     break

     self.add\_cl\_lines(cleared\_rows)

     return True

     return False

    

    def insta\_drop(self):

     if not self.gameover and not self.paused:

     while(not self.drop(True)):

     pass

    

    def rotate\_stone(self):

     if not self.gameover and not self.paused:

     new\_stone = rotate\_clockwise(self.stone)

     if not check\_collision(self.board,

                         new\_stone,

                         (self.stone\_x, self.stone\_y)):

     self.stone = new\_stone

    

    def toggle\_pause(self):

     self.paused = not self.paused

    

    def start\_game(self):

     if self.gameover:

     self.init\_game()

     self.gameover = False

    

    def run(self):

     key\_actions = {

     'ESCAPE':    self.quit,

     'LEFT':     lambda:self.move(-1),

     'RIGHT':    lambda:self.move(+1),

     'DOWN':     lambda:self.drop(True),

     'UP':     self.rotate\_stone,

     'p':     self.toggle\_pause,

     'SPACE':    self.start\_game,

     'RETURN':    self.insta\_drop

     }

   

     self.gameover = False

     self.paused = False

   

     dont\_burn\_my\_cpu = pygame.time.Clock()

     while 1:

     self.screen.fill((0,0,0))

     if self.gameover:

     self.center\_msg("""Game Over!\\nYour score: %d

Press space to continue""" % self.score)

     else:

     if self.paused:

     self.center\_msg("Paused")

     else:

     pygame.draw.line(self.screen,

     (255,255,255),

     (self.rlim+1, 0),

     (self.rlim+1, self.height-1))

     self.disp\_msg("Next:", (

     self.rlim+cell\_size,

     2))

     self.disp\_msg("Score: %d\\n\\nLevel: %d\\

\\nLines: %d" % (self.score, self.level, self.lines),

     (self.rlim+cell\_size, cell\_size\*5))

     self.draw\_matrix(self.bground\_grid, (0,0))

     self.draw\_matrix(self.board, (0,0))

     self.draw\_matrix(self.stone,

     (self.stone\_x, self.stone\_y))

     self.draw\_matrix(self.next\_stone,

     (cols+1,2))

     pygame.display.update()

   

     for event in pygame.event.get():

     if event.type == pygame.USEREVENT+1:

     self.drop(False)

     elif event.type == pygame.QUIT:

     self.quit()

     elif event.type == pygame.KEYDOWN:

     for key in key\_actions:

     if event.key == eval("pygame.K\_"

     +key):

     key\_actions\[key\]()

   

     dont\_burn\_my\_cpu.tick(maxfps)

 

if \_\_name\_\_ == '\_\_main\_\_':

 

    s = getpass.getpass()

    App = TetrisApp()

   App.run()

**Snapshots of Tetris Game**

Running the Python Tetris program from the Ubuntu terminal

 

The Tetris game will open in the new window, which will allow us to use the arrow keys of the keyboard

The game continues… So does the fun.

Happy Gaming!

 

Want to expertise Python? Checkout [Intro to Python](https://www.byteacademy.co/courses/intro-to-python) and [Python bootcamp](https://www.byteacademy.co/courses/python-bootcamp) offered by [Byte Academy](https://www.byteacademy.co/).

[View full post](https://www.byteacademy.co/blog/tetris-pygame-python)

```json
{
  "@context" : "http://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Sanoj Mathew"
  },
  "dateModified" : "2022-05-12T08:43:06.061Z",
  "datePublished" : "2020-08-17T09:12:00Z",
  "headline" : "Coding Exercise: Build Tetris in Python using Pygame",
  "image" : {
    "@type" : "ImageObject",
    "height" : 60,
    "url" : "/hs/hsstatic/content_shared_assets/static-1.4092/img/default-amp-logo.png",
    "width" : 60
  },
  "mainEntityOfPage" : "https://www.byteacademy.co/blog/tetris-pygame-python",
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "height" : 0.0,
      "url" : "https://f.hubspotusercontent30.net/hubfs/5340732/BYTE%20logo%20files/byte_academy_logo.webp",
      "width" : 0.0
    },
    "name" : "Byte Academy Blog"
  }
}
```