Create a 2048 Game with Python Tkinter

PythonPythonBeginner
Practice Now

Introduction

In this project, you'll learn how to create a simple 2048 game using Python and the Tkinter library for the graphical user interface. 2048 is a popular sliding puzzle game where you combine tiles to reach the tile with a value of 2048. While this project won't create the most modern and beautiful user interface, it will provide a solid foundation that you can build upon to enhance the aesthetics.

👀 Preview

2048 game

ðŸŽŊ Tasks

In this project, you will learn:

  • How to import necessary libraries for the game
  • How to create the Game2048 class to handle the game's logic and user interface
  • How to draw the game grid using Tkinter
  • How to spawn the initial tiles on the grid
  • How to update the user interface to reflect the current state of the game grid
  • How to define tile colors based on their values
  • How to handle key presses to move the tiles
  • How to define methods to move tiles in different directions
  • How to check if the game is over

🏆 Achievements

After completing this project, you will be able to:

  • Use the Tkinter library to create a graphical user interface
  • Handle key presses and trigger corresponding actions
  • Update the user interface based on the game state
  • Implement game logic for tile movements and merging
  • Check if a game is over

Create the project files

First, create a new file named 2048_game.py and open it in your preferred code editor.

cd ~/project
touch 2048_game.py
âœĻ Check Solution and Practice

Import necessary libraries

For your game to work, you need to import the essential libraries. In the 2048_game.py file, include Python's random library and Tkinter for building the graphical user interface.

import random
import tkinter as tk

These libraries will be used to create the game's functionality and user interface.

âœĻ Check Solution and Practice

Create the Game2048 class

Define the Game2048 class in your 2048_game.py file. This class will handle the game's logic and user interface.

class Game2048:
    def __init__(self, root):
        self.root = root
        self.root.title("2048 Game")
        self.root.geometry("400x400")

        ## Initialize the game grid and score
        self.grid = [[0 for _ in range(4)] for _ in range(4)]
        self.score = 0

        ## Create an empty list to store tile labels
        self.tiles = []

Here, you're creating a class constructor that initializes the game's root window, sets its title and size, initializes the game grid, and creates a list to hold the game tiles.

âœĻ Check Solution and Practice

Draw the game grid

In the Game2048 class, add a method to draw the game grid using Tkinter. This method creates the initial user interface grid.

    def draw_grid(self):
        for i in range(4):
            row = []
            for j in range(4):
                cell = tk.Label(self.root, text="", font=("Helvetica", 24), width=5, height=2, borderwidth=4, relief="ridge")
                cell.grid(row=i, column=j, padx=5, pady=5)
                row.append(cell)
            self.tiles.append(row)

This method creates a 4x4 grid of labels that will display the game tiles.

âœĻ Check Solution and Practice

Spawn the initial tiles

Now, you need to implement a method that spawns the initial tiles when the game starts. This method will place one or two tiles with the value 2 or 4 on the grid.

    def spawn_tile(self):
        empty_cells = [(i, j) for i in range(4) for j in range(4) if self.grid[i][j] == 0]
        if empty_cells:
            i, j = random.choice(empty_cells)
            self.grid[i][j] = 2 if random.random() < 0.9 else 4
            self.update_tiles()

This method locates empty cells on the grid and randomly places a new tile in one of them.

âœĻ Check Solution and Practice

Update the user interface

Create a method to update the user interface to reflect the current state of the game grid.

    def update_tiles(self):
        for i in range(4):
            for j in range(4):
                tile_value = self.grid[i][j]
                self.tiles[i][j]["text"] = str(tile_value) if tile_value > 0 else ""
                self.tiles[i][j]["background"] = self.get_tile_color(tile_value)

This method iterates through the grid, updating the labels in the graphical user interface to match the grid's state.

âœĻ Check Solution and Practice

Define tile colors

Create a method to assign background colors to the tiles based on their values.

    def get_tile_color(self, value):
        colors = {
            0: "#CDC1B4",
            2: "#EEE4DA",
            4: "#EDE0C8",
            8: "#F2B179",
            16: "#F59563",
            32: "#F67C5F",
            64: "#F65E3B",
            128: "#EDCF72",
            256: "#EDCC61",
            512: "#EDC850",
            1024: "#EDC53F",
            2048: "#EDC22E"
        }
        return colors.get(value, "#FF0000")

This method returns a color code based on the value of the tile.

âœĻ Check Solution and Practice

Handle key presses

Implement a method to handle key presses. In this step, you'll capture arrow key presses and respond accordingly.

    def key_pressed(self, event):
        if self.is_game_over():
            return

        if event.keysym == "Up":
            self.move("up")
        elif event.keysym == "Down":
            self.move("down")
        elif event.keysym == "Left":
            self.move("left")
        elif event.keysym == "Right":
            self.move("right")

        self.spawn_tile()
        self.update_tiles()

This method listens for arrow key presses and triggers the corresponding movement function.

âœĻ Check Solution and Practice

Move tiles

Implement methods to move tiles in the desired direction: up, down, left, or right.

    def move(self, direction):
        if direction == "up":
            self.grid = list(map(list, zip(*self.grid)))
            self.move_left()
            self.grid = list(map(list, zip(*self.grid)))
        elif direction == "down":
            self.grid = list(map(list, zip(*self.grid)))
            self.move_right()
            self.grid = list(map(list, zip(*self.grid)))
        elif direction == "left":
            self.move_left()
        elif direction == "right":
            self.move_right()

This method prepares the grid for movement, invokes the appropriate movement function, and then resets the grid.

âœĻ Check Solution and Practice

Move tiles to the left

Define a method to move tiles to the left within a row.

    def move_left(self):
        for i in range(4):
            row = self.grid[i]
            row = [value for value in row if value != 0]
            while len(row) < 4:
                row.append(0)

            for j in range(3):
                if row[j] == row[j + 1] and row[j] != 0:
                    row[j] *= 2
                    row[j + 1] = 0
                    self.score += row[j]

            row = [value for value in row if value != 0]
            while len(row) < 4:
                row.append(0)
            self.grid[i] = row

This method processes tile movements to the left, merging adjacent tiles when possible and filling empty cells.

âœĻ Check Solution and Practice

Move tiles to the right

Define a method to move tiles to the right within a row.

    def move_right(self):
        for i in range(4):
            row = self.grid[i]
            row = [value for value in row if value != 0]
            while len(row) < 4:
                row.insert(0, 0)

            for j in range(3, 0, -1):
                if row[j] == row[j - 1] and row[j] != 0:
                    row[j] *= 2
                    row[j - 1] = 0
                    self.score += row[j]

            row = [value for value in row if value != 0]
            while len(row) < 4:
                row.insert(0, 0)
            self.grid[i] = row

This method is responsible for moving tiles to the right, merging them when appropriate, and filling empty spaces.

âœĻ Check Solution and Practice

Check for game over

Create a method to check if the game is over. The game is over when no more moves are possible.

    def is_game_over(self):
        for i in range(4):
            for j in range(4):
                if self.grid[i][j] == 0:
                    return False

        for i in range(4):
            for j in range(3):
                if self.grid[i][j] == self.grid[i][j + 1]:
                    return False

        for j in range(4):
            for i in range(3):
                if self.grid[i][j] == self.grid[i + 1][j]:
                    return False

        return True

This method checks if any more moves are possible by examining whether the grid contains empty cells or adjacent cells with the same value. If no more moves are possible, the game is over.

âœĻ Check Solution and Practice

Run the project

Finally, swicth to Desktop and run the project using the following command:

python 2048_game.py
2048 game
âœĻ Check Solution and Practice

Summary

In this project, you've learned how to create a simple 2048 game in Python using the Tkinter library. You started by setting up the project, creating the main Python file, and defining the Game2048 class. You then implemented key presses, tile movements, and checked for game over conditions. The game interface may not be the most modern and beautiful, but you now have a foundation that you can enhance and customize to create a more polished 2048 game. To run the project, you can follow the steps below.

Other Python Tutorials you may like