Build Your Own 2048 Game Using Python

With GUI

Β·

5 min read

Build Your Own 2048 Game Using Python

Hello, buddies! In this article, we will look at Python code and logic to design a 2048 game you have played very often. If you are not familiar with the game, it is highly recommended to first play the game so that you can understand its basic functioning πŸ˜‰

Prerequisites

  • IDE that supports Python
  • Basic knowledge about tkinter

Let's Code!

Creating the board

Before doing anything, we need to install the required modules.

from tkinter import *
from tkinter import messagebox
import random

All set! Now, we have to create class for the board as follows.

class Board:
    bg_color={
        '2': '#eee4da',
        '4': '#ede0c8',
        '8': '#edc850',
        '16': '#edc53f',
        '32': '#f67c5f',
        '64': '#f65e3b',
        '128': '#edcf72',
        '256': '#edcc61',
        '512': '#f2b179',
        '1024': '#f59563',
        '2048': '#edc22e',
    }
    color={
         '2': '#776e65',
        '4': '#f9f6f2',
        '8': '#f9f6f2',
        '16': '#f9f6f2',
        '32': '#f9f6f2',
        '64': '#f9f6f2',
        '128': '#f9f6f2',
        '256': '#f9f6f2',
        '512': '#776e65',
        '1024': '#f9f6f2',
        '2048': '#f9f6f2',
    }

First of all, let's get to know what are the variables we have used.

  • Bg_color: It is a dictionary that stores the background color for every cell.
  • Color: It is a dictionary that stores foreground color for every cell.
  • window: It is the main Tkinter window.
  • gameArea: It is a Tkinter frame widget.
  • gridCell: It is a 4Γ—4 integer matrix that stores the actual integer value of all the cells.
  • Board: It is a 4Γ—4 grid of Tkinter label widget which displays the value of the cell on Tkinter window. It is also used to configure the background and foreground of the cell according to its gridCell value.
  • Score: It stores the current score of the player.

Next. let's code the functions in Board.

 def __init__(self):
        self.n=4
        self.window=Tk()
        self.window.title('Buddy 2048 Game')
        self.gameArea=Frame(self.window,bg= 'azure3')
        self.board=[]
        self.gridCell=[[0]*4 for i in range(4)]
        self.compress=False
        self.merge=False
        self.moved=False
        self.score=0

        for i in range(4):
            rows=[]
            for j in range(4):
                l=Label(self.gameArea,text='',bg='azure4',
                font=('arial',22,'bold'),width=4,height=2)
                l.grid(row=i,column=j,padx=7,pady=7)

                rows.append(l);
            self.board.append(rows)
        self.gameArea.grid()

    def reverse(self):
        for ind in range(4):
            i=0
            j=3
            while(i<j):
                self.gridCell[ind][i],self.gridCell[ind][j]=self.gridCell[ind][j],self.gridCell[ind][i]
                i+=1
                j-=1

    def transpose(self):
        self.gridCell=[list(t)for t in zip(*self.gridCell)]

    def compressGrid(self):
        self.compress=False
        temp=[[0] *4 for i in range(4)]
        for i in range(4):
            cnt=0
            for j in range(4):
                if self.gridCell[i][j]!=0:
                    temp[i][cnt]=self.gridCell[i][j]
                    if cnt!=j:
                        self.compress=True
                    cnt+=1
        self.gridCell=temp

    def mergeGrid(self):
        self.merge=False
        for i in range(4):
            for j in range(4 - 1):
                if self.gridCell[i][j] == self.gridCell[i][j + 1] and self.gridCell[i][j] != 0:
                    self.gridCell[i][j] *= 2
                    self.gridCell[i][j + 1] = 0
                    self.score += self.gridCell[i][j]
                    self.merge = True

    def random_cell(self):
        cells=[]
        for i in range(4):
            for j in range(4):
                if self.gridCell[i][j] == 0:
                    cells.append((i, j))
        curr=random.choice(cells)
        i=curr[0]
        j=curr[1]
        self.gridCell[i][j]=2

    def can_merge(self):
        for i in range(4):
            for j in range(3):
                if self.gridCell[i][j] == self.gridCell[i][j+1]:
                    return True

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

    def paintGrid(self):
        for i in range(4):
            for j in range(4):
                if self.gridCell[i][j]==0:
                    self.board[i][j].config(text='',bg='azure4')
                else:
                    self.board[i][j].config(text=str(self.gridCell[i][j]),
                    bg=self.bg_color.get(str(self.gridCell[i][j])),
                    fg=self.color.get(str(self.gridCell[i][j])))

No, no understanding this is not hard at all. Let's break these functions.

  • __init__(self): It is the constructor function. It initializes all the variables with appropriate default values like β€˜0’ for gridCell, False for moved, merge and so on.
  • Reverse: It reverse the gridCell matrix.
  • Transpose: It uses zip function and takes transpose of the gridCell matrix.
  • CompressGrid: It moves all not empty cells to the left, so that merging can be done easily.
  • mergeGrid: It adds the gridCell value of two adjacent cells if they have same gridCell values.
  • Random_cell: It first stores all the empty cells in a list and then picks a random cell from the created list and make its gridCell value 2
  • Can_merge: It returns a boolean value denoting we can merge any two cells or not. We can merge two cells if and only if they hold the same gridCell value.
  • paintGrid: It assigns foreground and background color to each cell of the 4Γ—4 grid corresponding to its gridCell value.

That's it about the class board!

Creating the Game

This class doesn’t have many variables, it only has some Boolean variables indicating game status. So, let's write some other functions.

 class Game:
    def __init__(self,gamepanel):
        self.gamepanel=gamepanel
        self.end=False
        self.won=False

    def start(self):
        self.gamepanel.random_cell()
        self.gamepanel.random_cell()
        self.gamepanel.paintGrid()
        self.gamepanel.window.bind('<Key>', self.link_keys)
        self.gamepanel.window.mainloop()

    def link_keys(self,event):
        if self.end or self.won:
            return

        self.gamepanel.compress = False
        self.gamepanel.merge = False
        self.gamepanel.moved = False
       presed_key=event.keysym

Simple!

__init__(self): It is the constructor function. It initializes all the variables with appropriate default values.

start: It calls random_cell twice to assign β€˜2’ to gridCell value of two random cells and then it paints the grid and after that, it calls link_keys to link up, down, left, and right keys.

Link_keys: First of all it checks if the game is already won or lost, and if it is, it executes a return statement without doing anything. Otherwise, it continues its execution.

 if presed_key=='Up':
            self.gamepanel.transpose()
            self.gamepanel.compressGrid()
            self.gamepanel.mergeGrid()
            self.gamepanel.moved = self.gamepanel.compress or self.gamepanel.merge
            self.gamepanel.compressGrid()
            self.gamepanel.transpose()

        elif presed_key=='Down':
            self.gamepanel.transpose()
            self.gamepanel.reverse()
            self.gamepanel.compressGrid()
            self.gamepanel.mergeGrid()
            self.gamepanel.moved = self.gamepanel.compress or self.gamepanel.merge
            self.gamepanel.compressGrid()
            self.gamepanel.reverse()
            self.gamepanel.transpose()

        elif presed_key=='Left':
            self.gamepanel.compressGrid()
            self.gamepanel.mergeGrid()
            self.gamepanel.moved = self.gamepanel.compress or self.gamepanel.merge
            self.gamepanel.compressGrid()

        elif presed_key=='Right':
            self.gamepanel.reverse()
            self.gamepanel.compressGrid()
            self.gamepanel.mergeGrid()
            self.gamepanel.moved = self.gamepanel.compress or self.gamepanel.merge
            self.gamepanel.compressGrid()
            self.gamepanel.reverse()
        else:
            pass

        self.gamepanel.paintGrid()
        print(self.gamepanel.score)

These are the main controls of the game. This game is played by arrow keys. So we have to make controls as it.

Next, we need to make the Game over and complete part. If tiles make the number 2048 it wins(very rarely). Otherwise, it is game over!

 flag=0
        for i in range(4):
            for j in range(4):
                if(self.gamepanel.gridCell[i][j]==2048):
# If cells are equal to 2048, game wins
                    flag=1
                    break

        if(flag==1): #found 2048
            self.won=True
            messagebox.showinfo('2048', message='You Wonnn!!')
display a simple message box saying you won!
            print("won")
            return

        for i in range(4):
            for j in range(4):
                if self.gamepanel.gridCell[i][j]==0:
                    flag=1
                    break

        if not (flag or self.gamepanel.can_merge()):
            self.end=True

            messagebox.showinfo('2048','Game Over!!!')
            print("Over")

        if self.gamepanel.moved:
            self.gamepanel.random_cell()

        self.gamepanel.paintGrid()

Woah, that's it! Awesome, awesome. But you have to call the functions at the end.

gamepanel =Board()
game2048 = Game( gamepanel)
game2048.start()

So our Output will be,

image.png

GUI is simple so you can change the color of the tiles. Thanks for reading and Happy coding!

Full Code

Β