Как написать игру на python 2048

In this article we will look python code and logic to design a 2048 game you have played very often in your smartphone. If you are not familiar with the game, it is highly recommended to first play the game so that you can understand the basic functioning of it.

How to play 2048 :

1. There is a 4*4 grid which can be filled with any number. Initially two random cells are filled with 2 in it. Rest cells are empty.

2. we have to press any one of four keys to move up, down, left, or right. When we press any key, the elements of the cell move in that direction such that if any two identical numbers are contained in that particular row (in case of moving left or right) or column (in case of moving up and down) they get add up and extreme cell in that direction fill itself with that number and rest cells goes empty again.

3. After this grid compression any random empty cell gets itself filled with 2.

4. Following the above process we have to double the elements by adding up and make 2048 in any of the cell. If we are able to do that we wins.

5. But if during the game there is no empty cell left to be filled with a new 2, then the game goes over.

In above process you can see the snapshots from graphical user interface of 2048 game. But all the logic lies in the main code. So to solely understand the logic behind it we can assume the above grid to be a 4*4 matrix ( a list with four rows and four columns). You can see below the way to take input and output without GUI for the above game.

Example : 

Commands are as follows : 
'W' or 'w' : Move Up
'S' or 's' : Move Down
'A' or 'a' : Move Left
'D' or 'd' : Move Right
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 2, 0]
Press the command : a
GAME NOT OVER
[0, 0, 0, 2]
[0, 0, 0, 0]
[0, 0, 0, 0]
[2, 0, 0, 0]
Press the command : s
GAME NOT OVER
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 2, 0]
[2, 0, 0, 2]
Press the command : d
GAME NOT OVER
[0, 0, 0, 0]
[0, 0, 0, 0]
[2, 0, 0, 2]
[0, 0, 0, 4]
Press the command : a
GAME NOT OVER
[0, 2, 0, 0]
[0, 0, 0, 0]
[4, 0, 0, 0]
[4, 0, 0, 0]
Press the command : s
GAME NOT OVER
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[8, 2, 0, 2]
.
.
.
And the series of input output will go on till we lose or win!

Programming Approach : 

  1. We will design each logic function such as we are performing a left swipe then we will use it for right swipe by reversing matrix and performing left swipe.
  2. Moving up can be done by taking transpose then moving left.
  3. Moving down can be done by taking transpose the moving right.
  4. All the logic in the program are explained in detail in the comments. Highly recommended to go through all the comments.

We have two python files below, one is 2048.py which contains main driver code and the other is logic.py which contains all functions used. logic.py should be imported in 2048.py to use these functions. just place both the files in the same folder then run 2048.py will work perfectly.

logic.py  

Python3

import random

def start_game():

    mat =[]

    for i in range(4):

        mat.append([0] * 4)

    print("Commands are as follows : ")

    print("'W' or 'w' : Move Up")

    print("'S' or 's' : Move Down")

    print("'A' or 'a' : Move Left")

    print("'D' or 'd' : Move Right")

    add_new_2(mat)

    return mat

def add_new_2(mat):

    r = random.randint(0, 3)

    c = random.randint(0, 3)

    while(mat[r] != 0):

        r = random.randint(0, 3)

        c = random.randint(0, 3)

    mat[r] = 2

def get_current_state(mat):

    for i in range(4):

        for j in range(4):

            if(mat[i][j]== 2048):

                return 'WON'

    for i in range(4):

        for j in range(4):

            if(mat[i][j]== 0):

                return 'GAME NOT OVER'

    for i in range(3):

        for j in range(3):

            if(mat[i][j]== mat[i + 1][j] or mat[i][j]== mat[i][j + 1]):

                return 'GAME NOT OVER'

    for j in range(3):

        if(mat[3][j]== mat[3][j + 1]):

            return 'GAME NOT OVER'

    for i in range(3):

        if(mat[i][3]== mat[i + 1][3]):

            return 'GAME NOT OVER'

    return 'LOST'

def compress(mat):

    changed = False

    new_mat = []

    for i in range(4):

        new_mat.append([0] * 4)

    for i in range(4):

        pos = 0

        for j in range(4):

            if(mat[i][j] != 0):

                new_mat[i][pos] = mat[i][j]

                if(j != pos):

                    changed = True

                pos += 1

    return new_mat, changed

def merge(mat):

    changed = False

    for i in range(4):

        for j in range(3):

            if(mat[i][j] == mat[i][j + 1] and mat[i][j] != 0):

                mat[i][j] = mat[i][j] * 2

                mat[i][j + 1] = 0

                changed = True

    return mat, changed

def reverse(mat):

    new_mat =[]

    for i in range(4):

        new_mat.append([])

        for j in range(4):

            new_mat[i].append(mat[i][3 - j])

    return new_mat

def transpose(mat):

    new_mat = []

    for i in range(4):

        new_mat.append([])

        for j in range(4):

            new_mat[i].append(mat[j][i])

    return new_mat

def move_left(grid):

    new_grid, changed1 = compress(grid)

    new_grid, changed2 = merge(new_grid)

    changed = changed1 or changed2

    new_grid, temp = compress(new_grid)

    return new_grid, changed

def move_right(grid):

    new_grid = reverse(grid)

    new_grid, changed = move_left(new_grid)

    new_grid = reverse(new_grid)

    return new_grid, changed

def move_up(grid):

    new_grid = transpose(grid)

    new_grid, changed = move_left(new_grid)

    new_grid = transpose(new_grid)

    return new_grid, changed

def move_down(grid):

    new_grid = transpose(grid)

    new_grid, changed = move_right(new_grid)

    new_grid = transpose(new_grid)

    return new_grid, changed

Code Explanation:

  1. The code starts by importing the random package.
  2. This package provides methods for generating random numbers.
  3. Next, the start_game() function is declared.
  4. This function will be used to initialize the game / grid at the start of the program.
  5. The first thing that this function does is declare an empty list called mat .
  6. Then, it appends four lists each with four elements as 0 .
  7. These lists represent the cells on the game / grid.
  8. The first list (mat[0] ) represents cell 0 , and so on.
  9. Next, the for loop iterates through 4 values (i in range(4)) .
  10. For each value, it generates a new list containing 4 elements ( [0] * 4 ).
  11. These lists represent each of the 4 possible positions on the game / grid.
  12. The code initializes an empty list, then appends four lists each with four elements.
  13. The first list has 0 elements, the second list has 1 element, the third list has 2 elements, and so on.
  14. The code starts by declaring two variables, r and c. These will hold the row and column numbers at which the new 2 will be inserted into the grid.
  15. Next, the code calls a function named add_new_2().
  16. This function takes one argument, mat.
  17. mat is a Python list object (a data structure that stores multiple items).
  18. The add_new_2() function begins by choosing two random numbers, r and c. It then uses these numbers to specify the row and column number at which the new 2 should be inserted into the grid.
  19. Finally, it returns mat as its result.
  20. When you run this code on your computer, you’ll see something like this: ‘W’ or ‘w’ : Move Up S’ or ‘s’ : Move Down A’ or ‘a’ : Move Left D’ or ‘d’ : Move Right
  21. The code first randomly selects a row and column index.
  22. Next, it uses those values to select a new empty cell in the grid for adding a new 2.
  23. Finally, the add_new_2 function is called with the newly selected cell as its argument.
  24. The code starts by declaring two variables.
  25. The first, mat, is an array of four integers.
  26. The second, r, is a random number between 0 and 3.
  27. The code then loops through each integer in the mat array.
  28. It checks to see if the value stored at that location in the mat array matches 2048 (which is the winning condition in this game).
  29. If it does, the code returns ‘WON’.
  30. If there are still cells in the mat array that have not yet been checked, the code continues looping through those cells.
  31. For each cell that has not yet been checked, it checks to see if its value matches 2048.
  32. If it does not, then the code declares victory for the player and ends the program execution.
  33. If all of the cells in mat have already been checked or if one of those cells contains 2048 (the winning condition), then no victory can be declared and control passes back to get_current_state() so that another round of checking can begin.
  34. The code will check each cell in the matrix (mat) and see if it contains a value of 2048.
  35. If any cell does, then the code will return ‘WON’.
  36. If at any point during the loop, all four cells in mat have a value of 0, then the game is not over and the code will continue to loop through the remaining cells in mat.
  37. The code starts by checking to see if the game has already ended.
  38. If it has not, then the code checks to see if any cells have been merged.
  39. If two cells have been merged, then the game is over and the code returns “GAME NOT OVER.”
  40. Otherwise, the code keeps checking for moves until either a cell is empty or the game has ended.
  41. In case of a tie, we declare that we have lost the game.
  42. The code will check to see if the cells at the given coordinates are equal.
  43. If they are, it will return “GAME NOT OVER.”
  44. If they are not, then it will return “LOST.”
  45. The code compresses the grid by copying each cell’s value to a new list.
  46. The code starts by creating an empty list, and then it loops through all of the cells in the matrix.
  47. For each cell, it calculates the sum of all of its values in the new list.
  48. Then it assigns this sum to the i variable.
  49. The next line creates a bool variable called changed.
  50. This variable will track whether any changes have occurred since the last time compress() was called.
  51. If there have been no changes, then changed is set to False .
  52. Otherwise, changed becomes True .
  53. Next, the code compacts the grid by copying each cell’s value into a new list.
  54. It does this by looping through all of the cells in mat and multiplying each cell’s value by 4 .
  55. Finally, it adds these lists together to create new_mat .
  56. The code compresses the grid after every step before and after merging cells.
  57. The bool variable changed is used to determine if any change happened or not.
  58. If no change occurred, then the code simply creates an empty grid.
  59. The code starts by declaring two variables, changed and new_mat.
  60. The changed variable will keep track of whether the cells in the matrix have been modified.
  61. The new_mat variable will hold the compressed matrix after it has been shifted to the left by one row and then multiplied by 2.
  62. Next, the code loops through each column in turn.
  63. For each cell in that column, if its value is equal to the next cell’s value and they are not empty, then they are double-checked to make sure that they are still equal.
  64. If they are, then their values are set to be 2 times their original value and the next cell in that column is emptied so that it can hold a new value for future calculations.
  65. The merge() function is called next.
  66. This function takes as input a matrix of 4×4 cells and merges all of the cells in it together based on their values.
  67. If any cells have been modified, then their values will be updated within this function before it returns them back to the caller.
  68. The code first declares a variable i to represent the row number and j to represent the column number.
  69. It then loops through each cell in the matrix, checking to see if the value of the current cell matches the next cell in the row and also making sure that both cells are not empty.
  70. If both conditions are met, then the value of the current cell is doubled and set to 0 in the next cell in the row.
  71. This process is repeated for every row in the matrix.
  72. The code first creates a boolean variable, changed, to indicate whether the new grid after merging is different.
  73. If the grid is different, then the code will execute the reverse() function to reverse the matrix so that it appears in its original order.
  74. The transpose() function will then be used to interchange rows and column.
  75. Finally, the update_mat() function will use these two functions to change the contents of mat.
  76. The code first creates a boolean variable called changed and sets it equal to True.
  77. Then it calls the reverse() function to reverse the matrix.
  78. Next, transpose() is called to interleave rows and column.
  79. Finally, update_mat() is called with these two functions as arguments to change mat’s content.
  80. The code first defines two variables, changed and mat.
  81. The changed variable will be set to True once the matrix has been merged and therefore represents the new grid.
  82. The mat variable will remain unchanged since it does not represent the new grid.
  83. The next block of code defines a function, reverse, which will reverses the sequence of rows in the mat variable.
  84. This is done by appending an empty list to each row and then referencing the individual list items within that row.
  85. Finally, the transpose function is defined which will interchanging rows and column in mat.
  86. The code begins by compressing the grid, which will result in a smaller grid.
  87. Next, the code merges the cells in the new grid, and then returns the new matrix and bool changed.
  88. Finally, the code compresses the new matrix again.
  89. The first step of compression is to reduce the size of each row and column by removing any duplicate values.
  90. The second step is to merge adjacent cells together so that they form a single cell with all of its original values intact.
  91. Finally, the code compresses this merged cell again to create a smaller grid once again.
  92. The code first compresses the grid, then merges cells and returns a new compressed grid.
  93. Next, it compresses the new grid again and compares the two results.
  94. Finally, it returns the new matrix and bool changed.
  95. The code in this section is used to update the grid on the screen.
  96. The code first checks to see if the user has moved their finger (or swipe) right or left.
  97. If the user has moved their finger (or swipe) right, then the code updates the grid by reversing it.
  98. Next, if the user moves their finger (or swipe) up, then instead of reversing the matrix, the code just takes its transpose value and updates the grid accordingly.
  99. The code firstly reverses the grid matrix.
  100. This is necessary in order to move right or up.
  101. Next, it updates the grid matrix based on the inputted direction.
  102. Finally, it returns the updated grid and changed values.
  103. The code starts by creating two new variables, new_grid and changed.
  104. The code then moves the grid left using the move_left function.
  105. Next, the code takes transpose of the new grid to create a new matrix.
  106. Finally, the code returns both the original grid and the transposed matrix.
  107. The move_down function works in a similar way.
  108. First, it creates two new variables, new_grid and changed.
  109. Then it moves down using the move_down function.
  110. Again, transpose is used to create a new matrix.
  111. Finally, both original grids and transposed matrices are returned.
  112. The code first creates a new 2×2 grid.
  113. Next, it moves the leftmost column of the new grid one row down and the rightmost column of the new grid one row up.
  114. Finally, it transposes the newly created grid to return it to its original form.
  115. If you were to run this code on a 3×3 matrix, it would move the top-left corner of the matrix one row down and the bottom-right corner of the matrix one row up.
     

2048.py 

Python3

import logic

if __name__ == '__main__':

    mat = logic.start_game()

while(True):

    x = input("Press the command : ")

    if(x == 'W' or x == 'w'):

        mat, flag = logic.move_up(mat)

        status = logic.get_current_state(mat)

        print(status)

        if(status == 'GAME NOT OVER'):

            logic.add_new_2(mat)

        else:

            break

    elif(x == 'S' or x == 's'):

        mat, flag = logic.move_down(mat)

        status = logic.get_current_state(mat)

        print(status)

        if(status == 'GAME NOT OVER'):

            logic.add_new_2(mat)

        else:

            break

    elif(x == 'A' or x == 'a'):

        mat, flag = logic.move_left(mat)

        status = logic.get_current_state(mat)

        print(status)

        if(status == 'GAME NOT OVER'):

            logic.add_new_2(mat)

        else:

            break

    elif(x == 'D' or x == 'd'):

        mat, flag = logic.move_right(mat)

        status = logic.get_current_state(mat)

        print(status)

        if(status == 'GAME NOT OVER'):

            logic.add_new_2(mat)

        else:

            break

    else:

        print("Invalid Key Pressed")

    print(mat)

Output :

Code Explanation:

  1. The code starts by importing the logic module.
  2. This module contains all the functions that we will use in our program.
  3. Next, we have a function to initialize the matrix.
  4. This is done by calling the start_game() function.
  5. The while loop runs until the user presses any of the keyboard keys (W, S, A, D).
  6. For each key press, we call one of the functions in logic.
  7. These are move_up(), move_down(), and move_left().
  8. We also need to call get_current_state() to get information about the current state of our matrix.
  9. Each function in logic takes two arguments: mat and flag.
  10. mat is the matrix object and flag is either ‘W’ for moving up or ‘S’ for moving down.
  11. If you recall from earlier in this chapter, these are references to variables that store data about our game board.
  12. After calling each function, we print out its results and then check to see if game is over yet using status variable.
  13. If it isn’t over yet, we add a new row to our matrix using add_new_2().
  14. Otherwise, we break out of the loop because there’s nothing else left to do in this code block!
  15. Finally, at
  16. The code starts by importing the logic.py file.
  17. This file contains all the functions used in this project.
  18. Next, we have written the driver code.
  19. In this code, we are checking for the input of a key and depending on that input, we are calling one of the function in logic.py file.
  20. We will be discussing each of these functions in detail later on in this article.
  21. The while loop is used to keep track of user input and execute the corresponding code inside it.
  22. The code inside this loop will be executed until user presses any other key or the game is over.
  23. Inside the if statement, we are checking for different keys and depending on that input, we are calling one of the functions from logic.py

Internship at OpenGenus

Get this book -> Problems on Array: For Interviews and Competitive Programming

2048 game is a fun and addictive single-player game that keeps you hooked from the moment you start playing. In this article, we will explain the strategies of the game and implement the steps in code using Python.

How to play

This is a tile-sliding game. It means, it has is a board-game which has numbers on tiles. We slide the tiles and merge the numbers. We can only slide the tiles to Up, Down, Left, and Right directions. Choosing a direction will mean every element on the board will move to that direction.

Only two same numbers will merge; for example, we can merge 2 and 2, but we cannot merge 2 and 4. The goal is to merge the numbers until we get 2048. Merging numbers to 2048 will mean we have won the game. Otherwise, if there are no empty tiles left and we cannot merge any more numbers, the game is over and we lose.

At the beginning of the game, two 2’s are randomly generated to start the game. As we swipe from left to right or any other direction, with every move a random number 2 or 4 is added to board. This addition of random number happens whether the other tiles are merged or not.

Implementation

We will be explaining the steps as we implement the code. For this game, we have chosen a 4X4 matrix; that would be a square board with 4 rows and 4 columns.

To show the board and get the similar experience of actually playing the game, we are using the standard GUI library Tkinter for the graphical interface. It provides widgets that can be easily implemented as needed. We are also importing the random library to generate random numbers.

«Frame» widget in Tkinter keeps other widgets organized and working together. The colors for the board and the tiles are randomly picked and methods are defined for every function of the game.

Start the Code

We are using a Game class so that all the functions and variables needed to build the game can be used easily. We are using the title «2048» for the game. Frame is used for keeping the functions grouped and well-maintained.

from tkinter import *
import random

class Game(Frame):
    def __init__(self):
        Frame.__init__(self)
        self.grid()
        self.master.title("2048")
        self.main_grid = Frame(self, bg='lightgrey', bd=3, width=400,height=400)
        self.main_grid.grid()

Make the Board

Now we create the 4X4 Gameboard using nested lists.

    def gameboard(self):
        self.tiles = []
        for i in range(4):
            row = []
            for j in range(4):
                tile_frame = Frame(
                    self.main_grid,
                    bg='white',
                    width='50',
                    height='50'
                )
                tile_frame.grid(
                    row=i, column=j, 
                    padx=3, pady=3
                )
                tile_number = Label(
                    self.main_grid, 
                    bg='white'
                )
                tile_number.grid(row=i, column=j)
                tile_data = 
                {
                   "frame": tile_frame, 
                   "number": tile_number
                }
                row.append(tile_data)
            self.tiles.append(row)

A «tiles» list is created for storing the tiles of the grid. Adding the grid to the frame and tile_number to the grid. Tile numbers are created using Label widget. Tile number refers to each and every tile on the board. The tile data refers to the values we are supposed to merge. Tile data is used as a dictionary to store the values of the tiles.

The board will look like this:

Gameboard

Start Game

At the start of the game, the board will contain two random 2’s at two random positions.

  • First, we declare a nested list for the board where all the values are 0.
  • Then we loop through the board and use «random» library methods to find two random tiles that contains 0. This 0 value will mean the tile is empty and a value can be inserted.

start

Operations of The Game

To perform the merge towards up, down, left, or right, we need functions that can be used for merging the values. Instead of writing a huge number of different methods for each of the operations, we can write some basic functions that can be used by them all.

For that we are going to stack all the values to the left of the board for every operation.

  • We create a new matrix
  • Add the elements of the board to the matrix. The board elements will remain on the same row but move to the left-most column(if it’s empty) of that row on the new matrix.
  • After getting all the values to the left side, we assign the new matrix to the board.

MoveToLeft

    def moveToLeft(self):
        new_board = [[0 for col in range(4)] for row in range(4)]
        for i in range(4):
            fill_position = 0
            for j in range(4):
                if self.board[i][j] != 0:
                    new_board[i][fill_position] = self.board[i][j]
                    fill_position += 1
        self.board = new_board

For the merge function, first we check if the value is not 0 and if it is same as the value next to it. Instead of adding the values we simply multiply the value with 2 and we set the tile next to it to 0.

Merge

    def merge(self):
        for i in range(4):
            for j in range(3):
                if self.board[i][j] != 0 and self.board[i][j] == self.board[i][j+1]:
                    self.board[i][j] *= 2
                    self.board[i][j+1] = 0

Then we create a reverse function that will reverse the rows of the board. We declare a new matrix to store the reverse rows and then set the board to the new matrix.

Reverse

    def reverse(self):
        new_board = []
        for i in range(4):
            new_board.append([])
            for j in range(4):
                new_board[i].append(self.board[i][3-j])
        self.board = new_board

The last function is the transpose function. This function changes the values diagonally. It means the values of board[0][1] and board[1][0] will swap.

Transpose

    def transpose(self):
        new_board = [[0 for col in range(4)] for row in range(4)]
        for i in range(4):
            for j in range(4):
                new_board[i][j] = self.board[j][i]
        self.board = new_board

Pick Random Value

After each move, a random number(2 or 4) will be added to a random tile. For this, we are declaring a function that randomly generates row and column number for a random tile that is empty and available for inserting a value.

    def pickNewValue(self):
        row = col = 0
        while self.board[row][col] != 0:
            row = random.randint(0, 3)
            col = random.randint(0, 3)

        if random.randint(1, 5) == 1:
            self.board[row][col] = 4
        else:
            self.board[row][col] = 2

Update The Board

After every move, we need to update the board to see the result of that move. After every move, we are updating the values of the tiles and inserting a random 2 or 4 on a random tile. We are using tile_data dictionary to store tile value.

    def updateGame(self):
        for i in range(4):
            for j in range(4):
                tile_value = self.board[i][j]
                if tile_value == 0:
                    self.tiles[i][j]["frame"].configure(bg="white")
                    self.tiles[i][j]["number"].configure(bg="white", text="")
                else:
                    self.tiles[i][j]["frame"].configure(bg="orange")
                    self.tiles[i][j]["number"].configure(
                        bg="orange",
                        fg="white",
                        font="20",
                   text=str(tile_value)
                    )
            self.update_idletasks()

Moves For The Game

Now, we write the functions of Left, Right, Up, and Down.

Left

To merge the elements to the left, we need to take them all to the left and merge. Then we add a random 2 or 4 and we update the board.

    def left(self, event):
        self.moveToLeft()
        self.merge()
        self.moveToLeft()
        self.pickNewValue()
        self.updateGame()
        self.final_result()

Right

The move to the right will reverse the rows, move the elements to the left, merge, move them again to the left, and reverse the rows back. Then place a random 2 or 4 and update the board.

    def right(self, event):
        self.reverse()
        self.moveToLeft()
        self.merge()
        self.moveToLeft()
        self.reverse()
        self.pickNewValue()
        self.updateGame()
        self.final_result()

Up

This move requires the elements to change diagonally first. Then we move them to the left, merge the values, move to the left again to eliminate the 0 created in the empty space, and transpose the values back. Then we repeat the process of picking a random value and updating the game.

    def up(self, event):
        self.transpose()
        self.moveToLeft()
        self.merge()
        self.moveToLeft()
        self.transpose()
        self.pickNewValue()
        self.updateGame()
        self.final_result()

Down

In this move, we mix all of the steps takes for previous moves. For this, we first need to transpose, then reverse, move values to the left, merge, move them back ti left, reverse back, and transpose again. Then the process of picking random value and updating game will repeat.

   def down(self, event):
        self.transpose()
        self.reverse()
        self.moveToLeft()
        self.merge()
        self.moveToLeft()
        self.reverse()
        self.transpose()
        self.pickNewValue()
        self.updateGame()
        self.final_result()

Check For Moves

If the board fills up with values we need to check if any two values can be merged. In other words, if any move is left. We create a function that checks if we can apply any move up or down, and left or right.

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

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

End of The Game

The game ends in two cases.

  1. We find 2048, which means the game is over and we win
  2. The board is filled up with values and no move left, which means the game is over and we lose.

We write a function to check for both cases. In the winning case the board will show a green sign with «You Win» writing and for the opposite case, the sign will be red with «Game Over» writing. The function for final result is called after updating the board in every move.

    def final_result(self):
        if any(2048 in row for row in self.board):
            game_over_frame = Frame(self.main_grid, borderwidth=2)
            game_over_frame.place(relx=0.5, rely=0.5, anchor='center')
            Label(
                game_over_frame,
                text="You Win",
                bg="green",
                fg="white",
                font="20"
            ).pack()

        elif not any(0 in row for row in self.board) and not self.horizontal_move_exists() and not self.vertical_move_exists():
            game_over_frame = Frame(self.main_grid, borderwidth=2)
            game_over_frame.place(relx=0.5, rely=0.5, anchor='center')
            Label(
                game_over_frame,
                text="Game Over",
                bg="Red",
                fg="white",
                font="20"
            ).pack()

Show Output

To show the gameboard and all the graphics we need to add some more steps. First, we need to call the functions to create the board and start the game in the init method at the beginning of the code. There we also need to bind the moves to keyboard press. The mainloop() is added to keep the GUI running.

class Game(Frame):
    def __init__(self):
        Frame.__init__(self)
        self.grid()
        self.master.title("2048")
        self.main_grid = Frame(self, bg='lightgrey', bd=3, width=400,height=400)
        self.main_grid.grid()
        
        #call the functions to run the program
        self.gameboard()
        self.start_game()

        self.master.bind("<Left>", self.left)
        self.master.bind("<Right>", self.right)
        self.master.bind("<Up>", self.up)
        self.master.bind("<Down>", self.down)

        self.mainloop()

At the end of the code, we need to create an instance of the class Game to run the code.

def main():
    Game()

if __name__ == "__main__":
    main()

Final Results

For winning case:
win

For losing case:
gameover

With this article at OpenGenus, you must have the complete idea of how to develop 2048 game in Python as a project and boost your Software Developer Portfolio.

In this tutorial, we will build the 2048 game in Python. We will go through the different moves during the build of the game.

*I will use 4×4 matrix to explain this game in this context*

Some detail on the 2048 game

In this game initially, we have been provided with two 2’s at random place in the matrix.

When moving up, down, left or right, it will merges with the only similar numbers present in its path.

For example, below is the screenshot image of the game:

2048 game in Python

After this, if we press key ‘W’ or moves up, we will go through the following changes:

  1. 4’s at  (0,0) and (1,0) merges to 8.
  2. 2 at (2,0) moves to (1,0).
  3. 16’s at (1,1) and (2,1) merges to 32 as shown.
  4. no change in 8.
  5. 2 at (2,3) moves to (2,1)

This will result in the following matrix:

matrix

I would recommend you to play this game by yours 3-4 times to understand it thoroughly.

Let us first go through different functions to be used, I will explain them afterwards :

Compress:

def compress(mat):
    new_mat=[[0 for i in range(4)] for i in range(4)]     
    for i in range(4):
        pos=0
        for j in range(4):
            if mat[i][j]!=0:
                new_mat[i][pos]=mat[i][j]             #This compress function is for lest move.
                pos+=1
    return new_mat

Merge:

def merge(mat):
    for i in range(4):
        for j in range(3):
            if mat[i][j]==mat[i][j+1] and mat[i][j]!=0:
                mat[i][j]+=mat[i][j].                     #This merge function is for left move.
                mat[i][j+1]=0
    return mat

Reverse:

def reverse(mat):
    new_mat=[]
    for i in range(4):
        new_mat.append([])
        for j in range(4):
            new_mat[i].append(mat[i][3-j])
    return new_mat

Transpose:

def transp(mat):
    new_mat=[[0 for i in range(4)] for i in range(4)]
    for i in range(4):
        for j in range(4):
            new_mat[i][j]=mat[j][i]
    return new_mat

These four functions are majorly used in the whole game.

In simple words, the compress function finishes the gap between the required numbers, or make the important numbers, on which we want to perform further actions, close.

Merge function, merges the similar numbers present comparing next matrix.

Reverse and transpose functions reverses and transposes matrix, simultaneously.

Also, read: How To Convert Image To Matrix Using Python

Initially merge and compress functions are coded for the left move, we can use the same function for different moves using reverse and transpose functions. Various functions for various moves are as follows:

Left Move:

  1. Compress
  2. Merge
  3. Compress

Right Move:

  1. Reverse
  2. Compress
  3. Merge
  4. Compress
  5. Reverse

In this by reversing and re-reversing make a matrix to deal it like Left move.

Up Move:

  1. Transpose
  2. Compress
  3. Merge
  4. Compress
  5. Transpose

Transpose function makes matrix work like Left move, afterwards transposing it again to work flawlessly.

Down Move:

  1. Transpose
  2. Reverse
  3. Compress
  4. Merge
  5. Compress
  6. Reverse
  7. Transpose

Transpose and reverse make matrix work like the left move.

This is how we code for different moves.

This is how the logic part works for the 2048 game.

Some more changes

After every work is done on this game, or whenever compress or merge function does some work, a new 2 at random place will be added to our game.

Changes in code are as follows:

def merge(mat):
    for i in range(4):
        for j in range(3):
            if mat[i][j]==mat[i][j+1] and mat[i][j]!=0:
                mat[i][j]+=mat[i][j]
                mat[i][j+1]=0
                change=True                    #change is true if this if function processes
    return mat
def compress(mat):
    new_mat=[[0 for i in range(4)] for i in range(4)]
    for i in range(4):
        pos=0
        for j in range(4):
            if mat[i][j]!=0:
                new_mat[i][pos]=mat[i][j]     #row(i) of new_mat and mat are same, if columns are different, this implies work has been done.
                if j!=pos:
                    change = True
                pos+=1
    return new_mat

Complete Logic code for this game is :

import random

def start_game():
    return [[0 for i in range(4)] for i in range(4)]

def reverse(mat):
    new_mat=[]
    for i in range(4):
        new_mat.append([])
        for j in range(4):
            new_mat[i].append(mat[i][3-j])
    return new_mat

def transp(mat):
    new_mat=[[0 for i in range(4)] for i in range(4)]
    for i in range(4):
        for j in range(4):
            new_mat[i][j]=mat[j][i]
    return new_mat
def merge(mat):
    for i in range(4):
        for j in range(3):
            if mat[i][j]==mat[i][j+1] and mat[i][j]!=0:
                mat[i][j]+=mat[i][j]
                mat[i][j+1]=0
    return mat
        
def compress(mat):
    new_mat=[[0 for i in range(4)] for i in range(4)]
    for i in range(4):
        pos=0
        for j in range(4):
            if mat[i][j]!=0:
                new_mat[i][pos]=mat[i][j]
                pos+=1
    return new_mat

def moveLeft(arr):
    st1=compress(arr)
    st2=merge(st1)
    st3=compress(st2)
    return st3
    
def moveRight(arr):
    st0=reverse(arr)
    st1=compress(st0)
    st2=merge(st1)
    st3=compress(st2)
    st4=reverse(st3)
   
    return st4
    
def moveUp(arr):
    st0=transp(arr)
    st1=compress(st0)
    st2=merge(st1)
    st3=compress(st2)
    st4=transp(st3)
        
    return st4
    
def moveDown(arr):
    st0=transp(arr)
    st=reverse(st0)
    st1=compress(st)
    st2=merge(st1)
    st3=compress(st2)
    st3=reverse(st3)
    st4=transp(st3)
    return st4

input=list(map(int,input().split()))
arr=start_game()
arr[1][3]=2                       #initial array setup, choosen random numbers at random places.
arr[2][2]=2
arr[3][0]=4
arr[3][1]=8
arr[2][1]=4

for i in input:
    if i==1:                         #move Up
        arr=moveUp(arr)
    elif i==2:                       #move Down
        arr=moveDown(arr)
    elif i==3:                       #move left
        arr=moveLeft(arr)
    elif i==4:                       #move right
        arr=moveRight(arr)
print(arr)

while giving input: 1 2 3 4

we will get the output :   [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 4], [0, 4, 8, 4]]

2048-with-pygame

An implementation of the game 2048 with pygame library

Check out my simpler version of 2048, also implemented with python, but not the pygame library.

##Rules

There will be 16 empty slots on a 4×4 board.

The player will start with two numbers that are either two or four, on two random slots on the board.

The player uses up, down, left, right arrow key to move the numbers.

When moving the numbers to the right, all numbers will be pushed to the right of the board.

For instance:

The 4 and 2 in the first row will become (in the first row):

And the program adds a 2 or 4 at a random slot.

If two numbers are of the same value, they will be added together.

For instance 4 4 4:

will become 4 8 when pushed right :

and 8 4 when pushed left:

If the player gets the number «2048» on the board, he/she wins.

The player looses if all the slots are filled in and he/she cannot make any move at all.

Beginner Python and found that the Python theory on the Internet is too boring.
Oh ~~ Simply get a small jelly, learn, while writing code.

Remarks:
1. Many places I pay much attention to it, so that everyone will watch.
2. The full code can be used in the last code.

First, the imported library

import sys
import random
import pygame

Second, global variables

1. There are annotations on each statement.
2. The color of the square uses a classic 2048 game color.

pygame.init()  # Initialize Pygame ()
size = 100  # Each game checkered size
queue = 4  # Initial set to 4x4 array
game_lis = []  #    
background_color = (255, 239, 213)  # background color
Dividingline_color = (255, 222, 173)  #      
Dividingline_width = 15  #       Width

score_height = 120  #        
score_width = 140  #       's width
score_color = (205, 193, 180)  #    
font1 = pygame.font.SysFont('SimHei', 50)  #           Show black body 24
font_pos_x = Dividingline_width  #              
font_pos_y = int(font1.size('Score')[1])  #            
score = 0  #     initial value is 0

font3 = pygame.font.Font(None, 50)  #      
black = (0, 0, 0)  #    

screen_height = (((size + Dividingline_width) * queue) + score_height + Dividingline_width * 2)  #      
screen_width = (((size + Dividingline_width) * queue) + Dividingline_width)  # Screen width

colors = {0: (205, 193, 180),  # Various blocks corresponding to colors
          2: (238, 228, 218),
          4: (237, 224, 200),
          8: (242, 177, 121),
          16: (245, 149, 99),
          32: (246, 124, 95),
          64: (246, 94, 59),
          128: (237, 207, 114),
          256: (237, 204, 98),
          512: (237, 200, 80),
          1024: (237, 197, 63),
          2048: (225, 187, 0)}

Third, draw a game background

This part of the code includes a game background color and a grid line.

#Draw background
def _draw_background(screen):
    screen.fill(background_color)
    Dividingline_width_half = int(Dividingline_width / 2)
    Difference = score_height + Dividingline_width + int(Dividingline_width / 2)

    for i in range(queue + 1):  #Draw horizontal line
        pygame.draw.line(screen, Dividingline_color,
                         (0, i * (size + Dividingline_width) + Difference),
                         (screen_height, i * (size + Dividingline_width) + Difference),
                         Dividingline_width)

    for j in range(queue + 1):  #Draw horizontal line
        pygame.draw.line(screen, Dividingline_color,
                         (Dividingline_width_half + j * (size + Dividingline_width), Difference),
                         (Dividingline_width_half + j * (size + Dividingline_width), screen_height),
                         Dividingline_width)

Fourth, draw a score area

1. The score zone is a variable region, the most labor-saving situation is only the score.
2. Barley, I design in the main program is a loop call, and the result of keeping the previous result (the whole score area).
Is there a big name, how to display a dynamic number without overworking.

#Draw score area
def _draw_score(screen, font, pos_x, pos_y):
    global score
    print_text(screen, font, pos_x, 10, f'Score')
    print_text(screen, font, pos_x, pos_y + 6, f'{score}')


#      
def print_text(screen, font, x, y, text):
    imgText = font.render(text, True, score_color)
    screen.blit(imgText, (x, y))

V. Initialization game list

Here is a two-dimensional array to store game numbers, and the initial setting is 0.

# Initialize the gaming list, store 2048 numbers
def _game_list():
    for i in range(queue):  #       0
        lis = []
        for j in range(queue):
            lis.append(0)
        game_lis.append(lis)

6. Show game squares and numbers

1. Display the game block, the square color changes according to the value in the gaming list.
2. Display game numbers.

# Display game area: game squares and numbers
def _show_game(screen):
    for i in range(queue):
        for j in range(queue):
            rect_color = colors[game_lis[i][j]]  # Remove the value corresponding to the Dictionary Colors
            rect_position = [(j + 1) * Dividingline_width + j * size,  #     position
                             Dividingline_width * (i + 2) + size * i + score_height]
            pygame.draw.rect(screen, rect_color, (rect_position, (size, size)), 0)  # Paint color blocks

            if game_lis[i][j] != 0:  # Only when the number is not 0, it will be output.
                textSurfaceObj = font3.render(str(game_lis[i][j]), True, black)  # get_rect () method Returns Rect object
                textRectObj = textSurfaceObj.get_rect()
                rect_position = [(j + 1) * Dividingline_width + (j + 0.5) * size,  #    
                                 Dividingline_width * (i + 2) + size * (i + 0.5) + score_height]
                textRectObj.center = tuple(rect_position)
                screen.blit(textSurfaceObj, textRectObj)

Seven, generate random number

Random number in random position

# Generate random numbers in the random position
def _random():
    random_num = random.randint(1, 2)  # random number
    num = pow(2, random_num)
    random_pos_x = random.randint(0, queue - 1)  # Random X coordinate
    random_pos_y = random.randint(0, queue - 1)  # Random Y coordinate
    if game_lis[random_pos_x][random_pos_y] == 0:
        game_lis[random_pos_x][random_pos_y] = num
    else:
        _random()

Eight, button control -Left ()

Left direction key control.

# Capture keyboard Left operation
def _LEFT():
    global score
    for i in range(queue):
        while 0 in game_lis[i]:
            game_lis[i].remove(0)
        for j in range(len(game_lis[i]) - 1):
            if game_lis[i][j] == game_lis[i][j + 1]:
                game_lis[i][j] = game_lis[i][j] + game_lis[i][j + 1]
                score = score + game_lis[i][j]
                game_lis[i][j + 1] = 0
        while 0 in game_lis[i]:
            game_lis[i].remove(0)
        lis = []
        for j in range(queue - len(game_lis[i])):
            lis.append(0)
        game_lis[i] = game_lis[i] + lis

Nine, button control -Right ()

Right direction key control.

# Capture the keyboard RIGHT operation
def _RIGHT():
    global score
    for i in range(queue):
        while 0 in game_lis[i]:
            game_lis[i].remove(0)
        for j in range(len(game_lis[i]) - 1, 0, -1):
            if game_lis[i][j] == game_lis[i][j - 1]:
                game_lis[i][j] = game_lis[i][j] + game_lis[i][j - 1]
                score = score + game_lis[i][j]
                game_lis[i][j - 1] = 0
        while 0 in game_lis[i]:
            game_lis[i].remove(0)
        lis = []
        for j in range(queue - len(game_lis[i])):
            lis.append(0)
        game_lis[i] = lis + game_lis[i]

Ten, button control -Up ()

The upper point is controlled, and the list transaction is used.

def _UP():
    global score
    lis = [[0, 0, 0, 0],
           [0, 0, 0, 0],
           [0, 0, 0, 0],
           [0, 0, 0, 0]]
    for i in range(queue):  # The whole list rotates 90 degrees clockwise
        for j in range(queue):
            lis[i][j] = game_lis[queue - 1 - j][i]

    for i in range(queue):
        while 0 in lis[i]:
            lis[i].remove(0)
        for j in range(len(lis[i]) - 1, 0, -1):
            if lis[i][j] == lis[i][j - 1]:
                lis[i][j] = lis[i][j] + lis[i][j - 1]
                score = score + lis[i][j]
                lis[i][j - 1] = 0
        while 0 in lis[i]:
            lis[i].remove(0)
        list1 = []
        for j in range(queue - len(lis[i])):
            list1.append(0)
        lis[i] = list1 + lis[i]

    for i in range(queue):  # The whole list rotates 90 degrees counterclockwise
        for j in range(queue):
            game_lis[i][j] = lis[j][queue - 1 - i]

Eleven, button control — Down ()

The lower arrow keys control uses a list transaction.

def _DOWN():
    global score
    lis = [[0, 0, 0, 0],
           [0, 0, 0, 0],
           [0, 0, 0, 0],
           [0, 0, 0, 0]]

    for i in range(queue):  # The whole list rotates 90 degrees clockwise
        for j in range(queue):
            lis[i][j] = game_lis[queue - 1 - j][i]

    for i in range(queue):
        while 0 in lis[i]:
            lis[i].remove(0)
        for j in range(len(lis[i]) - 1):
            if lis[i][j] == lis[i][j + 1]:
                lis[i][j] = lis[i][j] + lis[i][j + 1]
                score = score + lis[i][j]
                lis[i][j + 1] = 0
        while 0 in lis[i]:
            lis[i].remove(0)
        list1 = []
        for j in range(queue - len(lis[i])):
            list1.append(0)
        lis[i] = lis[i] + list1

    for i in range(queue):  # The whole list rotates 90 degrees counterclockwise
        for j in range(queue):
            game_lis[i][j] = lis[j][queue - 1 - i]

Twelve, master function

def main():
    screen = pygame.display.set_mode((screen_width, screen_height))  # Establish a game window
    pygame.display.set_caption('2048')  # Set the window title

    _draw_background(screen)  #Draw background
    _game_list()  # Initialize the game list
    _show_game(screen)  # Display game squares and numbers
    _draw_score(screen, font1, font_pos_x, font_pos_y)  #Draw a partition

    # Start the game
    _random()  # Generate random number
    pygame.display.flip()  # Update game
    _show_game(screen)  # Display game squares and numbers
    _draw_score(screen, font1, font_pos_x, font_pos_y)  #Draw a partition


    while True:
        for event in pygame.event.get():  # get () Get the return value of the event
            if event.type == pygame.QUIT:  #            , then exit
                pygame.quit()  #       g window
                sys.exit()  #       p      

            elif event.type == pygame.KEYDOWN:  # Capture button operation
                if event.key == pygame.K_LEFT:  # Press the left button
                    _LEFT()
                    print("left")
                elif event.key == pygame.K_RIGHT:  # Press the right button
                    _RIGHT()
                    print("right")
                elif event.key == pygame.K_UP:  # Press the button
                    _UP()
                    print("up")
                elif event.key == pygame.K_DOWN:  # Press the button
                    _DOWN()
                    print("down")
                else:
                    print("False")
                pygame.display.flip()  # Update game
                _draw_background(screen)  #Draw background
                _random()  # Generate random number
                _show_game(screen)  # Display game squares and numbers
                _draw_score(screen, font1, font_pos_x, font_pos_y)  #Draw a partition

Result map

Start interface

2. Game interface

Complete code

Direct copying can be used.

import sys
import random
import pygame

pygame.init()  # Initialize Pygame ()
size = 100  # Each game checkered size
queue = 4  # Initial set to 4x4 array
game_lis = []  #    
background_color = (255, 239, 213)  # background color
Dividingline_color = (255, 222, 173)  #      
Dividingline_width = 15  #       Width

score_height = 120  #        
score_width = 140  #       's width
score_color = (205, 193, 180)  #    
font1 = pygame.font.SysFont('SimHei', 50)  #           Show black body 24
font_pos_x = Dividingline_width  #              
font_pos_y = int(font1.size('Score')[1])  #            
score = 0  #     initial value is 0

font3 = pygame.font.Font(None, 50)  #      
black = (0, 0, 0)  #    

screen_height = (((size + Dividingline_width) * queue) + score_height + Dividingline_width * 2)  #      
screen_width = (((size + Dividingline_width) * queue) + Dividingline_width)  # Screen width

colors = {0: (205, 193, 180),  # Various blocks corresponding to colors
          2: (238, 228, 218),
          4: (237, 224, 200),
          8: (242, 177, 121),
          16: (245, 149, 99),
          32: (246, 124, 95),
          64: (246, 94, 59),
          128: (237, 207, 114),
          256: (237, 204, 98),
          512: (237, 200, 80),
          1024: (237, 197, 63),
          2048: (225, 187, 0)}


#Draw background
def _draw_background(screen):
    screen.fill(background_color)
    Dividingline_width_half = int(Dividingline_width / 2)
    Difference = score_height + Dividingline_width + int(Dividingline_width / 2)

    for i in range(queue + 1):  #Draw horizontal line
        pygame.draw.line(screen, Dividingline_color,
                         (0, i * (size + Dividingline_width) + Difference),
                         (screen_height, i * (size + Dividingline_width) + Difference),
                         Dividingline_width)

    for j in range(queue + 1):  #Draw horizontal line
        pygame.draw.line(screen, Dividingline_color,
                         (Dividingline_width_half + j * (size + Dividingline_width), Difference),
                         (Dividingline_width_half + j * (size + Dividingline_width), screen_height),
                         Dividingline_width)


#Draw score area
def _draw_score(screen, font, pos_x, pos_y):
    global score
    print_text(screen, font, pos_x, 10, f'Score')
    print_text(screen, font, pos_x, pos_y + 6, f'{score}')


#      
def print_text(screen, font, x, y, text):
    imgText = font.render(text, True, score_color)
    screen.blit(imgText, (x, y))


# Initialize the gaming list, store 2048 numbers
def _game_list():
    for i in range(queue):  #       0
        lis = []
        for j in range(queue):
            lis.append(0)
        game_lis.append(lis)


# Display game area: game squares and numbers
def _show_game(screen):
    for i in range(queue):
        for j in range(queue):
            rect_color = colors[game_lis[i][j]]  # Remove the value corresponding to the Dictionary Colors
            rect_position = [(j + 1) * Dividingline_width + j * size,  #     position
                             Dividingline_width * (i + 2) + size * i + score_height]
            pygame.draw.rect(screen, rect_color, (rect_position, (size, size)), 0)  # Paint color blocks

            if game_lis[i][j] != 0:  # Only when the number is not 0, it will be output.
                textSurfaceObj = font3.render(str(game_lis[i][j]), True, black)  # get_rect () method Returns Rect object
                textRectObj = textSurfaceObj.get_rect()
                rect_position = [(j + 1) * Dividingline_width + (j + 0.5) * size,  #    
                                 Dividingline_width * (i + 2) + size * (i + 0.5) + score_height]
                textRectObj.center = tuple(rect_position)
                screen.blit(textSurfaceObj, textRectObj)


# Generate random numbers in the random position
def _random():
    random_num = random.randint(1, 2)  # random number
    num = pow(2, random_num)
    random_pos_x = random.randint(0, queue - 1)  # Random X coordinate
    random_pos_y = random.randint(0, queue - 1)  # Random Y coordinate
    if game_lis[random_pos_x][random_pos_y] == 0:
        game_lis[random_pos_x][random_pos_y] = num
    else:
        _random()


# Capture keyboard Left operation
def _LEFT():
    global score
    for i in range(queue):
        while 0 in game_lis[i]:
            game_lis[i].remove(0)
        for j in range(len(game_lis[i]) - 1):
            if game_lis[i][j] == game_lis[i][j + 1]:
                game_lis[i][j] = game_lis[i][j] + game_lis[i][j + 1]
                score = score + game_lis[i][j]
                game_lis[i][j + 1] = 0
        while 0 in game_lis[i]:
            game_lis[i].remove(0)
        lis = []
        for j in range(queue - len(game_lis[i])):
            lis.append(0)
        game_lis[i] = game_lis[i] + lis


# Capture the keyboard RIGHT operation
def _RIGHT():
    global score
    for i in range(queue):
        while 0 in game_lis[i]:
            game_lis[i].remove(0)
        for j in range(len(game_lis[i]) - 1, 0, -1):
            if game_lis[i][j] == game_lis[i][j - 1]:
                game_lis[i][j] = game_lis[i][j] + game_lis[i][j - 1]
                score = score + game_lis[i][j]
                game_lis[i][j - 1] = 0
        while 0 in game_lis[i]:
            game_lis[i].remove(0)
        lis = []
        for j in range(queue - len(game_lis[i])):
            lis.append(0)
        game_lis[i] = lis + game_lis[i]


# Get keyboard UP operations
def _UP():
    global score
    lis = [[0, 0, 0, 0],
           [0, 0, 0, 0],
           [0, 0, 0, 0],
           [0, 0, 0, 0]]
    for i in range(queue):  # The whole list rotates 90 degrees clockwise
        for j in range(queue):
            lis[i][j] = game_lis[queue - 1 - j][i]

    for i in range(queue):
        while 0 in lis[i]:
            lis[i].remove(0)
        for j in range(len(lis[i]) - 1, 0, -1):
            if lis[i][j] == lis[i][j - 1]:
                lis[i][j] = lis[i][j] + lis[i][j - 1]
                score = score + lis[i][j]
                lis[i][j - 1] = 0
        while 0 in lis[i]:
            lis[i].remove(0)
        list1 = []
        for j in range(queue - len(lis[i])):
            list1.append(0)
        lis[i] = list1 + lis[i]

    for i in range(queue):  # The whole list rotates 90 degrees counterclockwise
        for j in range(queue):
            game_lis[i][j] = lis[j][queue - 1 - i]


# Get keyboard DOWN operations
def _DOWN():
    global score
    lis = [[0, 0, 0, 0],
           [0, 0, 0, 0],
           [0, 0, 0, 0],
           [0, 0, 0, 0]]

    for i in range(queue):  # The whole list rotates 90 degrees clockwise
        for j in range(queue):
            lis[i][j] = game_lis[queue - 1 - j][i]

    for i in range(queue):
        while 0 in lis[i]:
            lis[i].remove(0)
        for j in range(len(lis[i]) - 1):
            if lis[i][j] == lis[i][j + 1]:
                lis[i][j] = lis[i][j] + lis[i][j + 1]
                score = score + lis[i][j]
                lis[i][j + 1] = 0
        while 0 in lis[i]:
            lis[i].remove(0)
        list1 = []
        for j in range(queue - len(lis[i])):
            list1.append(0)
        lis[i] = lis[i] + list1

    for i in range(queue):  # The whole list rotates 90 degrees counterclockwise
        for j in range(queue):
            game_lis[i][j] = lis[j][queue - 1 - i]


#    
def main():
    screen = pygame.display.set_mode((screen_width, screen_height))  # Establish a game window
    pygame.display.set_caption('2048')  # Set the window title

    _draw_background(screen)  #Draw background
    _game_list()  # Initialize the game list
    _show_game(screen)  # Display game squares and numbers
    _draw_score(screen, font1, font_pos_x, font_pos_y)  #Draw a partition

    # Start the game
    _random()  # Generate random number
    pygame.display.flip()  # Update game
    _show_game(screen)  # Display game squares and numbers
    _draw_score(screen, font1, font_pos_x, font_pos_y)  #Draw a partition


    while True:
        for event in pygame.event.get():  # get () Get the return value of the event
            if event.type == pygame.QUIT:  #            , then exit
                pygame.quit()  #       g window
                sys.exit()  #       p      

            elif event.type == pygame.KEYDOWN:  # Capture button operation
                if event.key == pygame.K_LEFT:  # Press the left button
                    _LEFT()
                    print("left")
                elif event.key == pygame.K_RIGHT:  # Press the right button
                    _RIGHT()
                    print("right")
                elif event.key == pygame.K_UP:  # Press the button
                    _UP()
                    print("up")
                elif event.key == pygame.K_DOWN:  # Press the button
                    _DOWN()
                    print("down")
                else:
                    print("False")
                pygame.display.flip()  # Update game
                _draw_background(screen)  #Draw background
                _random()  # Generate random number
                _show_game(screen)  # Display game squares and numbers
                _draw_score(screen, font1, font_pos_x, font_pos_y)  #Draw a partition


if __name__ == '__main__':
    main()

Existence problem

1. The arrow keys are not flexible enough, and occasionally need to be reacted on both sides 2. What should I do if the game ends, no pop-up options

Notebook

Outline of post¶

Let’s try to implement the 2048 game in Python, based on my experience playing it as well as the sources. We’ll do this with a class, that will have the following methods :

  • an __init__ method that will initialize a board with two numbers
  • a move method that will apply a move (up, down, right or left) to the board and randomly add a new number to the board
  • a is_game_over method that determines if the game is finished or not

In [1]:

import numpy as np
from numpy import zeros

class board_game_2048():
    def __init__(self):
        self.board = zeros((4, 4), dtype=np.int)
        self.game_over = False
        
    def move(self, direction):
        pass
    
    def is_game_over(self):
        pass

Out[3]:

array([[0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0]])

Randomly filling a cell of the board¶

So far, so good. The first thing I want to do is to fill a cell randomly with a 2 or a 4. The way to do this would be:

In [4]:

from random import randint, random

def fill_cell(board):
    i, j = (board == 0).nonzero()
    if i.size != 0:
        rnd = randint(0, i.size - 1) 
        board[i[rnd], j[rnd]] = 2 * ((random() > .9) + 1)

In [5]:

fill_cell(game.board)
game.board

Out[5]:

array([[0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 2, 0, 0],
       [0, 0, 0, 0]])

Moving a vector to the left¶

Next step would be to address the moving of the tiles. To do this, we’ll start by moving just a single column, seen as a 4 vector and we’ll implement the move to the left.

In [6]:

from numpy import array, zeros

In [7]:

def move_left(col):
    new_col = zeros((4), dtype=col.dtype)
    j = 0
    previous = None
    for i in range(col.size):
        if col[i] != 0: # number different from zero
            if previous == None:
                previous = col[i]
            else:
                if previous == col[i]:
                    new_col[j] = 2 * col[i]
                    j += 1
                    previous = None
                else:
                    new_col[j] = previous
                    j += 1
                    previous = col[i]
    if previous != None:
        new_col[j] = previous
    return new_col

In [8]:

col = array([0, 2, 2, 0])

In [9]:

print col
print move_left(col)

In the next cell, we’re building a couple of test cases to check that we implemented the right algorithm.

In [10]:

for col in [array([0, 2, 2, 0]),
            array([2, 2, 2, 8]),
            array([0, 2, 2, 4]),
            array([2, 2, 2, 2]),
            array([256, 256, 2, 4]),
            array([256, 128, 64, 32]),
            array([2, 0, 2, 0])]: # the last one doesn't work yet!
    print col, '->', move_left(col)
[0 2 2 0] -> [4 0 0 0]
[2 2 2 8] -> [4 2 8 0]
[0 2 2 4] -> [4 4 0 0]
[2 2 2 2] -> [4 4 0 0]
[256 256   2   4] -> [512   2   4   0]
[256 128  64  32] -> [256 128  64  32]
[2 0 2 0] -> [4 0 0 0]

Putting it all together: moving the grid¶

Since this seems to work like the real game, we can now go on to the main algorithm for moving the board along the four directions. This is done using the algorithm from the previous section but taking into account the symmetries of the board: individual moves are just a couple rotations away from the move to the left we have implemented above.

In [11]:

from numpy import rot90

def move(board, direction):
    # 0: left, 1: up, 2: right, 3: down
    rotated_board = rot90(board, direction)
    cols = [rotated_board[i, :] for i in range(4)]
    new_board = array([move_left(col) for col in cols])
    return rot90(new_board, -direction)

Out[14]:

array([[0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 2, 0, 0],
       [0, 0, 0, 0]])

In [15]:

for i in range(4):
    print move(game.board, i), 'n' 
[[0 0 0 0]
 [0 0 0 0]
 [2 0 0 0]
 [0 0 0 0]] 

[[0 2 0 0]
 [0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]] 

[[0 0 0 0]
 [0 0 0 0]
 [0 0 0 2]
 [0 0 0 0]] 

[[0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]
 [0 2 0 0]] 

As this seems to work, we can complete the class design. At every move step, we apply the movement to the existing grid. If the returned grid is the same as before, then it’s not a valid move. If the move was valid, we add a random tile.

In [20]:

def main_loop(board, direction):
    new_board = move(board, direction)
    moved = False
    if (new_board == board).all():
        # move is invalid
        pass
    else:
        moved = True
        fill_cell(new_board)
        
    return (moved, new_board)

Out[24]:

(True,
 array([[0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 2, 0, 2],
       [0, 0, 0, 0]]))

Presentation

During this exercise you will build your own
2048 game!

If you never played, have some fun here
for a moment :)

Introducing Numpy

There is many ways to do this, but here we will consider the game’s board as a matrice
and use numpy to program the game.
If you installed Anaconda Python, you already have this module.

Numpy is a Python module introducing
matrices
and many other linear algebra data types and functions.
It is widely used for scientific computing, area in which Python is
excelling.

If you’re familiar with Matlab or Octave, have a look at
this page which compare
most famous operations form one language to the other.

Here are a few examples:

Note

By convention, we import numpy like this to avoid to have to type numpy
each time we want to use it in our program.

Initiate a 4×4 matrice of zeros considered as integers:

>>> m = np.zeros((4,4), dtype=int)
>>> print(m)
[[0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]]

Changing the value of one or many elements:

>>> m[3,2] = 1
>>> print(m)
[[0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]
 [0 0 1 0]]
>>> m[3,:] = 1
>>> print(m)
[[0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]
 [1 1 1 1]]

Getting the position of specific values:

>>> np.where(m == 1)
(array([3, 3, 3, 3]), array([0, 1, 2, 3]))

Some core elements of Numpy are implemented in the C language making
it super disco fast.

2048 Rules

The board is a 4 by 4 matrice (i.e. a list of 4 lists of length 4).
When initialized, all positions are empty except 2 randomly chosen
positions set to 2.

Then, the player interact with the game by entering a direction (up, down, right, left) and 3 things happens:

  • All elements fall such as gravity whose pulling in that direction.
  • If two positions with the same value are one under the other when «falling», they merge by adding their values into one single position.
  • A new elements pops in a random empty position, of value 2 80% of the time and of value 4 20% of the time.

The player looses if no empty position are available to add new elements.

The player wins if one position reach the value 2048.

You must implement the game’s logic and not merely the examples given as
illustrations of the exercises which don’t cover all possible cases.

Exercise 1 :: Initiate the grid

Provide the function init_grid() that returns a random board
for starting the game.

The correction robot will import your script and execute
the init_grid() function, expecting something like:

>>> import numpy as np
>>> from solution import *
>>> grid = init_grid()
>>> print(grid)
[[0 0 0 0]
 [0 0 0 0]
 [0 0 0 2]
 [0 0 0 2]]
>>> type(grid)
<class 'numpy.ndarray'>

Exercise 2 :: Add new element to the grid

Provide the function add_new(grid) that will add either a 2 (80% of the time)
or a 4 (20% of the time) somewhere empty in any board.
If no empty position is available, add_new should
just return the board passed as argument.

>>> import numpy as np
>>> from solution import *
>>> grid = init_grid()
>>> print(grid)
[[0 0 0 0]
 [2 0 2 0]
 [0 0 0 0]
 [0 0 0 0]]
>>> grid = add_new(grid)
>>> print(grid)
[[0 0 0 0]
 [2 0 2 0]
 [0 0 0 4]
 [0 0 0 0]]
>>> type(grid)
<class 'numpy.ndarray'>

Exercise 3 :: Roll some row

Provide the function rollin_row(row) that will roll
the row to the left and returns it, such as:

>>> from solution import *
>>> row = [2, 0, 0, 2]
>>> row = rollin_row(row)
>>> print(row)
[4, 0, 0, 0]
>>> row = [2, 4, 4, 2]
>>> row = rollin_row(row)
>>> print(row)
[2, 8, 2, 0]

Here it doesn’t matter if row is a numpy object or a classic python list.

Exercise 4 :: Roll the grid

Provide the function rollin(grid, direction) where grid is the grid to be
moved and direction the direction to be taken,
l (left), r (right), u (up) and d (down).

rollin should return the new grid.

>>> print(grid)
[[ 8  0 16  0]
 [ 4  0  0  0]
 [ 4  0  2  0]
 [ 0 32  2  0]]
>>> grid = rollin(grid, 'd')
>>> print(grid)
[[ 0  0  0  0]
 [ 0  0  0  0]
 [ 8  0 16  0]
 [ 8 32  4  0]]
>>> type(grid)
<class 'numpy.ndarray'>

Exercise 5 :: Create an interactive 2048 game !

Note

This part is not mandatory

You have already built important elements of the game. A few things still need to be done, among which:

  • add a new element (2 or 4) at each turn, when possible
  • Make it playable from a terminal according to player’s inputs
  • Handle when the grid is stuck or a wrong command is entered, why not with Exceptions
  • Analyse the grid to know when the user wins or looses.

Provide the function my2048() that let the user play 2048, such as:

>>> my2048()
[[0 0 0 2]
 [0 0 0 0]
 [0 0 0 0]
 [0 0 0 2]]
u
[[0 0 2 4]
 [0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]]
l
[[2 4 0 0]
 [0 0 0 0]
 [2 0 0 0]
 [0 0 0 0]]
l
The grid is stuck.
Move in another direction.
[[2 4 0 0]
 [0 0 0 0]
 [2 0 0 0]
 [0 0 0 0]]
u
[[4 4 0 0]
 [0 2 0 0]
 [0 0 0 0]
 [0 0 0 0]]
e
Unknown command.
Press l, r, u or d + Enter.
[[4 4 0 0]
 [0 2 0 0]
 [0 0 0 0]
 [0 0 0 0]]
r
[[0 0 0 8]
 [0 0 2 2]
 [0 0 0 0]
 [0 0 0 0]]
[[ 2  8  8  4]
 [ 8 32 16 64]
 [16  8  4  2]
 [ 8  4  2  4]]
l
[[ 2 16  4  4]
 [ 8 32 16 64]
 [16  8  4  2]
 [ 8  4  2  4]]
l
[[ 2 16  8  2]
 [ 8 32 16 64]
 [16  8  4  2]
 [ 8  4  2  4]]
Game Over ! :(

Exercise 6 :: Make python play for you !

Note

This part is not mandatory

Provide another version of my2048() named ai2048() that will choose itself which direction to go until the game ends.

Super disco Bonus :: make an AI that wins !

Понравилась статья? Поделить с друзьями:
  • Как написать игру на lazarus
  • Как написать имя на английском поля
  • Как написать имя мухаммад на арабском языке
  • Как написать имя мая на английском
  • Как написать имя красиво на телефоне