Python//Homemade 2048 mini game code analysis and harvest

Python//2048 mini game

This is a self-made record of 2048 mini games for personal summary and review.I would be honored if you could help me.  

Article Directory

Finished Product Effect Diagram

With music, the volume is adjustable.

0 Still unresolved

I would appreciate it if the big guys could see this article and help me solve some of the following problems.
1. On my computer, pygame (1.9.6) can only load bmp picture formats, but from the data I look for, pygame should be able to read png jpg and other picture formats.
2. When I package the code as exe file with pyinstaller, an error occurs when I add-w (executed with the Windows subsystem. The command line will not be opened when the program starts). A noun bootloader that I don't understand appears, and the error is as follows:
3. When I package the code into exe file with pyinstaller, I add -i (modify exe default picture) parameter. The picture is very blurred and normal in the packed folder. Whether the EXE icon or the default icon is moved to the desktop (or other directory) is very confusing.
4. I would like to be able to monitor the mouse click position (I hope it is a picture), but I found in pygame that I can only use it (real-time monitoring of mouse position + mouse click events) at the same time to achieve the purpose, but the result is not good (because it involves the whole picture), I am not very able to operate.



1. Write before

As a non-computer professional, I know the code style is bad, the function encapsulation is redundant, but I will work hard to learn. If you think my code is confusing, I'm sorry.

1. For the big Python homework, I took this opportunity to finish the idea that I always wanted to write a game.
2. The code applies to numpy,random,pygame,sys Libraries

import numpy as np
import random
import pygame
import sys

2. Code Ideas and Analysis

Last rough thought map

1. The initialization part is to simply generate two 4*4 matrices. record1 records the last step (we want to add the function to go back to the previous step, but the design is not perfect, there is a small bug, which will be mentioned later). record2 records the current action to zero for no number.useRandom.randint(0,9) to generate a random number to indicate whether to refresh 2 or 4 (scale I set to 9:1).Then fill the number in the random position in the matrix, which is still in randInt mode.
2. How should the matrix change when there is a movement (up, down, left, right)?
Take moving up as an example.First, arrange the numbers (that is, do not consider merging), traverse the matrix in order, when a number appears, it is placed at the top of the column, where the order refers to: moving up means traversing from small to large, and moving down means traversing from large to small. The core idea is to start from the direction of movement, find the numbers, and record the number of times.When the arrangement is complete, merge the same numbers (e.g. 4+4 =8) Merge only once here (simplify properly, play 2048 normally, feel it). The merge process is, traverse the grid, look around for the same number, when found, merge in the direction of movement, and push the subsequent numbers in that direction, then jump out of the cycle (there is redundancy in the judgment of subsequent numbers here, I think it can be simpler, do not modify it here firstAll the other directions are the same.
3. Refresh the matrix, i.e. generate a new number (2 or 4) to fill in the 0 part of the matrix, because here you want to be able to satisfy the requirements of not refreshing by pressing other keys (non-directional keys), first judge if the matrices are the same,




if not np.array_equal(record2, record1):

If the same, do not refresh.The refresh here is roughly the same as the initial refresh, and one of the changes is to determine if the matrix element is zero. There is no traversal here, as follows:

N = random.choice(np.where(record2.reshape(1, 16) == 0)[1])
Next_cell1_location_raw = N // 4
Next_cell1_location_clo = N
while Next_cell1_location_clo > 3:
Next_cell1_location_clo = Next_cell1_location_clo - 4
record2[Next_cell1_location_raw][Next_cell1_location_clo] = Next_cell1      

5. Judge the end of the game. When the matrix has no element of 0, traverse each grid and look around for the same number. This is the same as merge judgment and does not know much about it.(My writing is really complicated/dish)
6. The last step is to use the interface design of pygame, not to mention the specific design, but to use some functions in pygame.

pygame.init()#Initialize each module part
size = width, height = 500, 600
screen = pygame.display.set_mode(size,flags = pygame.RESIZEABLE )#Set interface size, adjustable mode (RESIZEABLE with adjustable screen size, NOFRAME without border, FULLSCREEN with full screen)
icon1 = pygame.image.load('2048.bmp')#Load Pictures
pygame.display.set_caption('2048 Game')#Change Title
screen.fill((238 ,220 ,130))#Screen Background Color
map_font = pygame.font.Font('msyh.ttc', 40)#Generate a font object, here is Microsoft Yahei
font_surf1 = map_font_start.render('2048 Game', True, (105 ,112 ,225))#Write text
font_rect1 = font_surf1.get_rect()#Generate rect (equivalent to a matrix framing text to form a picture for easy movement)
font_rect1.center = (100,20)#Text position setting, center ed here
screen.blit(font_surf1, font_rect1)#Display text, do this for all text, pictures, module design
screen.blit(block_scord, (350, 80))#This is a design for a module
for i in range(4):
     for j in range(4):
         if record2[i][j] == 0:
             font_surf = map_font.render('', True, (0, 0, 0))
         else:
             font_surf = map_font.render(str(record2[i][j]), True, (0, 0, 0))
         font_rect = font_surf.get_rect()
         font_rect.center = (80 + 110 * j, 190 + 110 * i)
         block = pygame.Surface((100, 100))
         block.fill(eval(color[i][j]))
         screen.blit(block, (30 + 110 * j, 140 + 110 * i))
         screen.blit(font_surf, font_rect)
'''
//Here is the number of 16 modules that are generated in a loop to fill the matrix record2 above
'''
while True:
	 for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()#Here is the event judgment, including key press events, mouse events, and all other events are read in this way (read from the event queue).
pygame.mixer.music.load(flag)#Read into Music
pygame.mixer.music.play()#Play music              pygame.mixer.music.set_volume(sound)#Adjust music volume

Below is the condition that the judgment matrix does not refresh, error is to distinguish direction keys, otherwise you will not be able to go back to the previous step (when these keys are pressed)

if not np.array_equal(record2,record1) and error == 0:
    Flash()

6. I also added one step of best results record, which should be saved with txt. This step should not be difficult, but the parameters in open (r W + R + a +) are very meaningful for research, you can look for data to see.

3. Code overview

import numpy as np
import random
import pygame
import sys

#Initialize the game, set up two matrices, record1 records the last step, record2 changes in real time.
#Randomly refresh new grid
def New_Game():
    global record1, record2
    Gameover = False
    record1 = np.array(
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).reshape(4, 4)
    record2 = np.array(
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).reshape(4, 4)

    if random.randint(0, 10) in range(9):
        rand = 1
    else:
        rand = 2
    First_cell = 2 ** rand
    First_cell_location_raw, First_cell_location_clo = random.randint(0, 3), random.randint(0, 3)
    record2[First_cell_location_clo, First_cell_location_raw] = First_cell

#Refresh one grid at a time
def Flash():
    if not np.array_equal(record2, record1):
        try:
            if random.randint(0, 10) in range(9):
                rand = 1
            else:
                rand = 2
            Next_cell1 = 2 ** rand
            N = random.choice(np.where(record2.reshape(1, 16) == 0)[1])
            Next_cell1_location_raw = N // 4
            Next_cell1_location_clo = N
            while Next_cell1_location_clo > 3:
                Next_cell1_location_clo = Next_cell1_location_clo - 4
            record2[Next_cell1_location_raw][Next_cell1_location_clo] = Next_cell1
        except:
            pass

#If 16 squares are full, then if none of the squares extend the same way around, the game is over
def Gameover():
    global gameover
    gameover = 0
    _ = 0
    if len(record2[record2 == 0]) == 0:
        for raw in range(4):
            for clo in range(4):
                if raw == 0:
                    if record2[raw][clo] == record2[raw + 1][clo]:
                        _ = 1
                        break
                elif raw == 3:
                    if record2[raw][clo] == record2[raw - 1][clo]:
                        _ = 1
                        break
                else:
                    if record2[raw][clo] == record2[raw + 1][clo] or record2[raw][clo] == record2[raw - 1][clo]:
                        _ = 1
                        break
                if clo == 0:
                    if record2[raw][clo] == record2[raw][clo + 1]:
                        _ = 1
                        break
                elif clo == 3:
                    if record2[raw][clo] == record2[raw][clo - 1]:
                        _ = 1
                        break
                else:
                    if record2[raw][clo] == record2[raw][clo + 1] or record2[raw][clo] == record2[raw][clo - 1]:
                        _ = 1
                        break
            if _ == 1:
                break
        else:
            gameover = 1

#The following four functions are changes that move in a given direction, including merging of identical grids and moving of different grids
def point_up():
    for clo in range(4):
        count = 0
        for raw in range(4):
            if record2[raw][clo] != 0:
                record2[count][clo] = record2[raw][clo]
                if count != raw:
                    record2[raw][clo] = 0
                count += 1
        for raw in range(3):
            if record2[raw][clo] == record2[raw + 1][clo] and record2[raw][clo] != 0:
                record2[raw][clo] = record2[raw][clo] * 2
                if raw == 2:
                    lenth = 0
                elif raw == 1:
                    lenth = 1
                else:
                    lenth = 2
                for i in range(lenth):
                    record2[raw + i + 1][clo] = record2[raw + i + 2][clo]
                record2[3][clo] = 0
                break


def point_left():
    for raw in range(4):
        count = 0
        for clo in range(4):
            if record2[raw][clo] != 0:
                record2[raw][count] = record2[raw][clo]
                if count != clo:
                    record2[raw][clo] = 0
                count += 1
        for clo in range(3):
            if record2[raw][clo] == record2[raw][clo + 1] and record2[raw][clo] != 0:
                record2[raw][clo] = record2[raw][clo] * 2
                if clo == 2:
                    lenth = 0
                elif clo == 1:
                    lenth = 1
                else:
                    lenth = 2
                for i in range(lenth):
                    record2[raw][clo + i + 1] = record2[raw][clo + i + 2]
                record2[raw][3] = 0
                break


def point_down():
    for clo in range(4):
        count = 0
        for raw in list(range(4))[::-1]:
            if record2[raw][clo] != 0:
                record2[3 - count][clo] = record2[raw][clo]
                if 3 - count != raw:
                    record2[raw][clo] = 0
                count += 1
        for raw in list(range(1, 4))[::-1]:
            if record2[raw][clo] == record2[raw - 1][clo] and record2[raw][clo] != 0:
                record2[raw][clo] = record2[raw][clo] * 2
                if raw == 1:
                    lenth = 0
                elif raw == 2:
                    lenth = 1
                else:
                    lenth = 2
                for i in range(lenth):
                    record2[raw - i - 1][clo] = record2[raw - i - 2][clo]
                record2[0][clo] = 0
                break


def point_right():
    for raw in range(4):
        count = 0
        for clo in list(range(4))[::-1]:
            if record2[raw][clo] != 0:
                record2[raw][3 - count] = record2[raw][clo]
                if 3 - count != clo:
                    record2[raw][clo] = 0
                count += 1
        for clo in list(range(1, 4))[::-1]:
            if record2[raw][clo] == record2[raw][clo - 1] and record2[raw][clo] != 0:
                record2[raw][clo] = record2[raw][clo] * 2
                if clo == 1:
                    lenth = 0
                elif clo == 2:
                    lenth = 1
                else:
                    lenth = 2
                for i in range(lenth):
                    record2[raw][clo - i - 1] = record2[raw][clo - i - 2]
                record2[raw][0] = 0
                break

#Use pygame to design game pictures
def gameframe():
    global map_font ,screen
    pygame.init()
    icon1 = pygame.image.load('2048.bmp')
    size = width, height = 500, 600
    screen = pygame.display.set_mode(size)
    pygame.display.set_caption('2048 Game')
    screen.fill((238 ,220 ,130))
    map_font = pygame.font.Font('msyh.ttc', 40)

    map_font_start = pygame.font.Font('msyh.ttc', 20)

    font_surf1 = map_font_start.render('2048 Game', True, (105 ,112 ,225))
    font_rect1 = font_surf1.get_rect()
    font_rect1.center = (100,20)

    font_surf2 = map_font_start.render('***Wishing you a pleasant game***', True, (150, 0, 221))
    font_rect2 = font_surf2.get_rect()
    font_rect2.center = (100,60)

    font_surf3 = map_font_start.render('White Cloud Dog Making', True, (152, 52, 12))
    font_rect3 = font_surf3.get_rect()
    font_rect3.center = (100,100)

    font_surf4 = map_font_start.render('Highest History:{}'.format(eval(maxscord)), True, (0, 0, 0))
    font_rect4 = font_surf3.get_rect()
    font_rect4.center = (395,50)

    screen.blit(font_surf1, font_rect1)
    screen.blit(font_surf2, font_rect2)
    screen.blit(font_surf3, font_rect3)
    screen.blit(font_surf4, font_rect4)

    # fps = 30
    # flock = pygame.time.Clock()

#Dynamic refresh of 16 grid values
def show_config():
    color = np.array(['(255 ,246 ,143)','(255 ,226 ,133)','(255 ,216 ,163)','(255 ,216 ,143)','(255 ,236 ,123)','(255 ,246 ,123)','(255 ,255 ,123)','(255 ,255 ,143)','(245 ,246 ,133)','(255 ,216 ,173)','(255 ,226 ,173)','(255 ,226 ,143)','(255 ,246 ,173)','(255 ,246 ,103)',"(255 ,206 ,143)",'(245 ,226 ,163)']).reshape(4,4)

    for i in range(4):
        for j in range(4):
            if record2[i][j] == 0:
                font_surf = map_font.render('', True, (0, 0, 0))
            else:
                font_surf = map_font.render(str(record2[i][j]), True, (0, 0, 0))
            font_rect = font_surf.get_rect()
            font_rect.center = (80 + 110 * j, 190 + 110 * i)
            block = pygame.Surface((100, 100))
            block.fill(eval(color[i][j]))
            screen.blit(block, (30 + 110 * j, 140 + 110 * i))
            screen.blit(font_surf, font_rect)

    map_font_scord = pygame.font.Font('msyh.ttc', 20)
    font_surf4 = map_font_scord.render('Total score:{:<}branch'.format(np.sum(record2)), True, (0, 0, 0))
    font_rect4 = font_surf4.get_rect()
    font_rect4.center = (400,95)
    block_scord = pygame.Surface((150, 35))
    block_scord.fill((238 ,220 ,130))
    screen.blit(block_scord, (350, 80))
    screen.blit(font_surf4, font_rect4)

#pygame main loop, keyboard event monitoring
def main():
    global record1 ,record2,maxscord
    with open('Best results.txt', 'r') as f:
        maxscord = f.read()
    gameframe()
    New_Game()
    path1 = 'Guzheng.wav'
    path2 = 'River Fflows In You.wav'
    flag = path2
    sound = 0.5
    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            elif pygame.mixer.music.get_busy()==False:
                if flag == path1:
                    flag = path2
                else:
                    flag = path1
                pygame.mixer.music.load(flag)
                pygame.mixer.music.play()
            elif event.type == pygame.KEYUP:
                error = 0
                if event.key == pygame.K_LEFT:
                    record1 = record2 * 1
                    point_left()
                elif event.key == pygame.K_RIGHT:
                    record1 = record2 * 1
                    point_right()
                elif event.key == pygame.K_UP:
                    record1 = record2 * 1
                    point_up()
                elif event.key == pygame.K_DOWN:
                    record1 = record2 * 1
                    point_down()
                elif event.key == pygame.K_BACKSPACE:
                    record2 = record1
                elif event.key == pygame.K_u:
                    sound = sound + 0.05
                    pygame.mixer.music.set_volume(sound)
                    error = 1
                elif event.key == pygame.K_d:
                    sound = sound - 0.05
                    pygame.mixer.music.set_volume(sound)
                    error = 1
                else:
                    error = 1
                if not np.array_equal(record2,record1) and error == 0:
                    Flash()

        show_config()
        #flock.tick(fps)
        Gameover()
        if gameover == 1:
            block_over = pygame.Surface((400,150))
            block_over.fill((238 ,220 ,130))
            map_font_over = pygame.font.Font('msyh.ttc', 60)
            font_surf = map_font_over.render('GAME OVER', True, (0, 0, 0))
            font_rect = font_surf.get_rect()
            font_rect.center = (250, 350)
            screen.blit(block_over, (50,275))
            screen.blit(font_surf, font_rect)

            with open('Best results.txt', 'w') as f:
                f.write(str(np.sum(record2)))
            if np.sum(record2) > eval(maxscord):
                with open('Best results.txt', 'w') as f:
                        f.write(str(np.sum(record2)))
            pygame.display.update()
        pygame.display.update()


if __name__ == "__main__":
    main()

4. Summary of pits encountered in code writing

1. The array provided by numpy cannot directly determine whether it is equal or not, so the function array_is needed.Equal (array1, array2)
2. The pygame interface refresh implements overlay, not overlay, and there is a sequential order.
3. The pygame music module, if you want to package exe, can only use wav file, otherwise an error will be made.
4. This is a speculation. If the big man understands it, please correct it.When you global ize an array in a function and assign that array to another array, if you write: array1 = array2 then array1 and array2 will be the same, that is, after the array2 changes, when you execute another function, array1 will still be the changed array2, not the previously saved array1 that I want, as I do, array1 = array2 *1. (Feeling a little silly, I don't understand the exact principle, and the faint feeling is the reason for the globe.)


V. Summary of harvest

Through this 2048 production, I not only know the use of pygame (first contact), but also have some experience writing long code (for me), I spent two nights doing feel more satisfied, at this stage my level, really played out, the process is smooth.In addition to writing csdn for the first time, there are also some benefits to sum up.

Tags: Python Windows

Posted on Tue, 19 May 2020 23:26:10 -0400 by Cbrams