Building Tetris in Pygame – Part II

# 003 Data Structures

Before we delve any deeper, it’s a good idea to stop and think about the data structures we’ll need to manage our game.

Ignoring UI cruft for now, the core of Tetris is a 10×20 well, and seven tetriminoes. For the sake of simplicity, it’s probably easiest to hold this in 2D arrays of numbers, like so:

well = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    # 19 more rows...

S_piece = [
    [1, 0],
    [1, 1],
    [0, 1],
    [0, 1, 1],
    [1, 1, 0],

Within this grid, a 0 is empty space, while a number 1-7 is a block. By giving each tetrimino a different number for its blocks, we will be able to easily track which type of tetrimino a given block came from, and will be able to color it appropriately.

In addition, there is a fair amount of other data we must keep track of:

  • Current score
  • Current level
  • The current piece, and its coordinates
  • The next piece

The simplest solution I see is a GameState object containing the well, score, level, and references to our current and next pieces. The tetriminoes themselves will have a module of their own.

And with that, we have an idea of where to begin.

# 004 On Testing…

If you’re following along and are not familiar with Test-Driven Development(TDD), take the time now to go read up on it, both its support and criticism.

TDD is a topic that inspires holy wars on the same scale as OS preferences and static/dynamic typing. I don’t have a particularly strong opinion either way. However, TDD does help you catch stupid mistakes earlier, and encourages tidy code, so I’m going to use it for this project.

We’ll be using Python’s builtin unittest module for writing our test cases. To actually run the test suite, there are plenty of options available — nosetests, py.test, etc. I use a custom framework (details, for those interested, in another post), and will be building my tests accordingly. Those of you following along are, of course, free to use whatever you want.

To start, we’ll write a sanity check test to get our framework up and running:

# tests/

import unittest

class TestSanity(unittest.TestCase):

    def test_fail(self):

    def test_pass(self):

Now, to actually run these tests with our framework, we need to add them to

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from lib.test_runner import ModuleTestRunner

from tests.test_sanity import TestSanity

suite = ModuleTestRunner()

suite.addTestList("Sanity", [TestSanity("test_fail"),

if __name__ == '__main__':

Finally, we run the tests in the terminal:

$ chmod +x
$ ./
Running tests:
SANITY:     F.


In test_fail (tests.test_sanity.TestSanity)
Traceback (most recent call last):
  File ".../tetris/tests/", line 8, in test_fail
AssertionError: False is not true


2 tests run in 0.000280 seconds, 1 failures, 0 skipped, 0 unexpected errors.

(Note: chmod +x makes executable, so we don’t have to prefix ‘python’ every time we want to run the suite.)

One failure, one pass, and our test suite is properly set up. Remove/comment out the import and references to TestSanity in (If you’re using nosetests or another test-discovery framework, you will probably want to delete or rename

Git commit:

$ git add .
$ git status
$ git commit -m "Setup test framework"

This post is getting long, so we’ll stop here for today. Next time we’ll get a basic window on screen, and start implementing the data structures mentioned earlier.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s