Playing with AI - Tetris Game

Photo by Terry Lee on Unsplash

Playing with AI - Tetris Game

Creating Tetris with Pygame

Introduction

This blog will go over how I went about creating the Tetris game in Python and what I learnt. I have made games in Python in the past and remember Pygame being a useful library so I will base my game around this. Everything else will come from a mixture of ChatGPT and general research. Let's begin.

Process

Project Overview

After booting up Pycharm and installing Pygame into the venv, I couldn't decide where to start. To get my brain organised along with the project, I asked ChatGPT to create a directory for a Tetris game and this is what it came up:

TetrisGame/
├── assets/
│   ├── fonts/
│   │   └── (store fonts here)
│   ├── images/
│   │   ├── blocks/
│   │   │   └── (store block images here)
│   │   ├── background.png
│   │   └── (other game images)
├── src/
│   ├── __init__.py
│   ├── main.py
│   ├── tetris.py
│   ├── block.py
│   ├── board.py
│   ├── utils.py
├── README.md
├── requirements.txt

I think that looks like a good enough start, so this is what I went for. The files are pretty straight-forward, where main.py will be the entry point of the program, tetris.py will contain most of the game rules, block.py will define the Block class, board.py will define the Board, and utils.py will contain any functions that are used across the program.

My First Game

Now where? Well, why not Pygame's documentation? Their front game offers an example of having a ball on the screen you can move with the WASD keys. So that's what I did:

# Example file showing a circle moving on screen
import pygame

# pygame setup
pygame.init()
screen = pygame.display.set_mode((1280, 720))
clock = pygame.time.Clock()
running = True
dt = 0

player_pos = pygame.Vector2(screen.get_width() / 2, screen.get_height() / 2)

while running:
    # poll for events
    # pygame.QUIT event means the user clicked X to close your window
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # fill the screen with a color to wipe away anything from last frame
    screen.fill("purple")

    pygame.draw.circle(screen, "red", player_pos, 40)

    keys = pygame.key.get_pressed()
    if keys[pygame.K_w]:
        player_pos.y -= 300 * dt
    if keys[pygame.K_s]:
        player_pos.y += 300 * dt
    if keys[pygame.K_a]:
        player_pos.x -= 300 * dt
    if keys[pygame.K_d]:
        player_pos.x += 300 * dt

    # flip() the display to put your work on screen
    pygame.display.flip()

    # limits FPS to 60
    # dt is delta time in seconds since last frame, used for framerate-
    # independent physics.
    dt = clock.tick(60) / 1000

pygame.quit()

Very exciting stuff. There are several things to learn from the example though. Firstly, we need to initialize the Pygame modules with the init() call. Then, we need to set up the screen and the clock. Finally, we need a game loop that will check each frame for the logic of the game - in this case, it draws the circle and checks for keyboard inputs to update the position of the player.

Tetris Demo

After some playing around with the code and reading more of the documentation, I was able to get a demo going of a T Tetromino falling automatically, moving left and right with arrow keys, and rotating with the up arrow.

# Example file showing a circle moving on screen
import pygame


class BlockSprite(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()  # Call the constructor of the parent class
        self.image = pygame.image.load('assets/images/blocks/t_block.webp')  # Set the image of the sprite
        self.rect = self.image.get_rect()  # Get the rectangular shape of the sprite

    def get_mask(self):
        return pygame.mask.from_surface(self.image)

    def rotate_right(self):
        # Rotate the sprite by 90 degrees
        self.image = pygame.transform.rotate(self.image, -90)
        self.rect = self.image.get_rect(center=self.rect.center)


# pygame setup
pygame.init()
screen = pygame.display.set_mode((1280, 720))
clock = pygame.time.Clock()
running = True
dt = 0

player_pos = pygame.Vector2(screen.get_width() / 2, screen.get_height() / 2)

block_sprite = BlockSprite()
all_sprites = pygame.sprite.Group()
all_sprites.add(block_sprite)

# Set the initial movement speed
move_speed = 5

while running:
    # poll for events
    # pygame.QUIT event means the user clicked X to close your window
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:  # Rotate the sprite when 'R' is pressed
                block_sprite.rotate_right()

    # fill the screen with a color to wipe away anything from last frame
    screen.fill("black")

    # Check for key presses to move the sprite
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        block_sprite.rect.x -= move_speed
    if keys[pygame.K_RIGHT]:
        block_sprite.rect.x += move_speed

    block_sprite.rect.y += move_speed

    block_mask = block_sprite.get_mask()
    if block_sprite.rect.y > screen.get_height() - block_mask.get_size()[1]:
        block_sprite.rect.y = 0  # Wrap to the top

    # Update and draw all sprites in the group
    all_sprites.update()
    all_sprites.draw(screen)

    # flip() the display to put your work on screen
    pygame.display.flip()

    # limits FPS to 60
    # dt is delta time in seconds since last frame, used for framerate-
    # independent physics.
    dt = clock.tick(60) / 1000

pygame.quit()

A little more exciting, but not much.

Conclusion

Unfortunately, that's all I had time for. It was good getting the foundations going and having a play around and I can already see what the next steps will be. Taking the code out of main.py and starting to compartmentalise the code and move it into its correct files is a must. I also need to figure out how to make the movement more akin to Tetris and not the frame-by-frame movement seen in the GIF.

What I did take out of this was how quick Python projects are to get up and running. I did all of this in around half an hour and had the basis of a game ready to go (a bit of a stretch but you get what I mean). I imagine this is going to contrast greatly with the process of setting up the AI.