Pygame actual combat: upgraded 2048 games - unlock new tricks! I can't stop! [source code attached]

Introduction

"Hey! Don't sleep, wake up! It's time to work ~"     

                                                  by - Gu Muzi!

 

"Just woke up, boss, what can I do for you?"

                                                    by - all members!

Have you had a thorough understanding of the super detailed - simple version of 2048 shared with you in the last issue? Cough... It doesn't matter if you haven't eaten it thoroughly!

Take your time, mumuzi has been waiting for you to rise ~ ha ha, ha ha, Na ~ now this article is an upgraded version of 2048 game promised last time.

What about the previous one? Any difference?

 

Of course, it's very different. Today, I teach you to write this small program with interface. This point with interface alone can surpass the previous one. It's more interesting. Let's have a look!

 

text

Environmental installation:

Python 3, pycharm, Pygame modules, etc.

 pip install -i https://pypi.douban.com/simple/ pygame

Profile:

Music, background, font, etc.

import os


'''FPS'''
FPS = 60
'''background color '''
BG_COLOR = '#D3D3D3'
'''Screen size'''
SCREENSIZE = (650, 370)
'''Save the current highest score file'''
MAX_SCORE_FILEPATH = 'score'
'''Font path'''
FONTPATH = os.path.join(os.getcwd(), 'resources/font/Gabriola.ttf')
'''Background music path'''
BGMPATH = os.path.join(os.getcwd(), 'resources/audio/bgm.mp3')
'''Other necessary constants'''
MARGIN_SIZE = 10
BLOCK_SIZE = 80
GAME_MATRIX_SIZE = (4, 4)

Define 2048 Games:

class Game2048(object):
    def __init__(self, matrix_size=(4, 4), max_score_filepath=None, **kwargs):
        # matrix_size: (num_rows, num_cols)
        self.matrix_size = matrix_size
        # Save path of the highest score of the game
        self.max_score_filepath = max_score_filepath
        # initialization
        self.initialize()
    '''Update game status'''
    def update(self):
        game_matrix_before = copy.deepcopy(self.game_matrix)
        self.move()
        if game_matrix_before != self.game_matrix: self.randomGenerateNumber()
        if self.score > self.max_score: self.max_score = self.score
    '''According to the specified direction, Move all blocks'''
    def move(self):
        # Extract non empty number
        def extract(array):
            array_new = []
            for item in array:
                if item != 'null': array_new.append(item)
            return array_new
        # Merge non empty numbers
        def merge(array):
            score = 0
            if len(array) < 2: return array, score
            for i in range(len(array)-1):
                if array[i] == 'null':
                    break
                if array[i] == array[i+1]:
                    array[i] *= 2
                    array.pop(i+1)
                    array.append('null')
                    score += array[i]
            return extract(array), score
        # return if you don't need to move
        if self.move_direction is None: return
        # Up
        if self.move_direction == 'up':
            for j in range(self.matrix_size[1]):
                col = []
                for i in range(self.matrix_size[0]):
                    col.append(self.game_matrix[i][j])
                col = extract(col)
                col.reverse()
                col, score = merge(col)
                self.score += score
                col.reverse()
                col = col + ['null',] * (self.matrix_size[0] - len(col))
                for i in range(self.matrix_size[0]):
                    self.game_matrix[i][j] = col[i]
        # down
        elif self.move_direction == 'down':
            for j in range(self.matrix_size[1]):
                col = []
                for i in range(self.matrix_size[0]):
                    col.append(self.game_matrix[i][j])
                col = extract(col)
                col, score = merge(col)
                self.score += score
                col = ['null',] * (self.matrix_size[0] - len(col)) + col
                for i in range(self.matrix_size[0]):
                    self.game_matrix[i][j] = col[i]
        # towards the left
        elif self.move_direction == 'left':
            for idx, row in enumerate(copy.deepcopy(self.game_matrix)):
                row = extract(row)
                row.reverse()
                row, score = merge(row)
                self.score += score
                row.reverse()
                row = row + ['null',] * (self.matrix_size[1] - len(row))
                self.game_matrix[idx] = row
        # towards the right
        elif self.move_direction == 'right':
            for idx, row in enumerate(copy.deepcopy(self.game_matrix)):
                row = extract(row)
                row, score = merge(row)
                self.score += score
                row = ['null',] * (self.matrix_size[1] - len(row)) + row
                self.game_matrix[idx] = row
        self.move_direction = None
    '''Randomly generate numbers in new locations'''
    def randomGenerateNumber(self):
        empty_pos = []
        for i in range(self.matrix_size[0]):
            for j in range(self.matrix_size[1]):
                if self.game_matrix[i][j] == 'null': empty_pos.append([i, j])
        i, j = random.choice(empty_pos)
        self.game_matrix[i][j] = 2 if random.random() > 0.1 else 4
    '''initialization'''
    def initialize(self):
        self.game_matrix = [['null' for _ in range(self.matrix_size[1])] for _ in range(self.matrix_size[0])]
        self.score = 0
        self.max_score = self.readMaxScore()
        self.move_direction = None
        self.randomGenerateNumber()
        self.randomGenerateNumber()
    '''Set direction of movement'''
    def setDirection(self, direction):
        assert direction in ['up', 'down', 'left', 'right']
        self.move_direction = direction
    '''Save highest score'''
    def saveMaxScore(self):
        f = open(self.max_score_filepath, 'w', encoding='utf-8')
        f.write(str(self.max_score))
        f.close()
    '''Read the highest score of the game'''
    def readMaxScore(self):
        try:
            f = open(self.max_score_filepath, 'r', encoding='utf-8')
            score = int(f.read().strip())
            f.close()
            return score
        except:
            return 0
    '''Is the game over'''
    @property
    def isgameover(self):
        for i in range(self.matrix_size[0]):
            for j in range(self.matrix_size[1]):
                if self.game_matrix[i][j] == 'null': return False
                if (i == self.matrix_size[0] - 1) and (j == self.matrix_size[1] - 1):
                    continue
                elif (i == self.matrix_size[0] - 1):
                    if (self.game_matrix[i][j] == self.game_matrix[i][j+1]):
                        return False
                elif (j == self.matrix_size[1] - 1):
                    if (self.game_matrix[i][j] == self.game_matrix[i+1][j]):
                        return False
                else:
                    if (self.game_matrix[i][j] == self.game_matrix[i+1][j]) or (self.game_matrix[i][j] == self.game_matrix[i][j+1]):
                        return False
        return True

Then set different colors.

Different numbers 2-4-8 and so on are combined into different numbers, and the color changes accordingly.

Color display:

'''obtain [grid background color, font color in the grid]' 'according to the current score of the grid'
def getColorByNumber(number):
    number2color_dict = {
        2: ['#eee4da', '#776e65'], 4: ['#ede0c8', '#776e65'], 8: ['#f2b179', '#f9f6f2'],
        16: ['#f59563', '#f9f6f2'], 32: ['#f67c5f', '#f9f6f2'], 64: ['#f65e3b', '#f9f6f2'],
        128: ['#edcf72', '#f9f6f2'], 256: ['#edcc61', '#f9f6f2'], 512: ['#edc850', '#f9f6f2'],
        1024: ['#edc53f', '#f9f6f2'], 2048: ['#edc22e', '#f9f6f2'], 4096: ['#eee4da', '#776e65'],
        8192: ['#edc22e', '#f9f6f2'], 16384: ['#f2b179', '#776e65'], 32768: ['#f59563', '#776e65'],
        65536: ['#f67c5f', '#f9f6f2'], 'null': ['#9e948a', None]
    }
    return number2color_dict[number]


'''Draw the current number arrangement of 2048 games to the screen'''
def drawGameMatrix(screen, game_matrix, cfg):
    for i in range(len(game_matrix)):
        for j in range(len(game_matrix[i])):
            number = game_matrix[i][j]
            x = cfg.MARGIN_SIZE * (j + 1) + cfg.BLOCK_SIZE * j
            y = cfg.MARGIN_SIZE * (i + 1) + cfg.BLOCK_SIZE * i
            pygame.draw.rect(screen, pygame.Color(getColorByNumber(number)[0]), (x, y, cfg.BLOCK_SIZE, cfg.BLOCK_SIZE))
            if number != 'null':
                font_color = pygame.Color(getColorByNumber(number)[1])
                font_size = cfg.BLOCK_SIZE - 10 * len(str(number))
                font = pygame.font.Font(cfg.FONTPATH, font_size)
                text = font.render(str(number), True, font_color)
                text_rect = text.get_rect()
                text_rect.centerx, text_rect.centery = x + cfg.BLOCK_SIZE / 2, y + cfg.BLOCK_SIZE / 2
                screen.blit(text, text_rect)

Add additional elements to the game interface.

The following figure: introduction to playing methods and score display.

'''Draw the highest score and current score of the game to the screen'''
def drawScore(screen, score, max_score, cfg):
    font_color = (255, 255, 255)
    font_size = 30
    font = pygame.font.Font(cfg.FONTPATH, font_size)
    text_max_score = font.render('Best: %s' % max_score, True, font_color)
    text_score = font.render('Score: %s' % score, True, font_color)
    start_x = cfg.BLOCK_SIZE * cfg.GAME_MATRIX_SIZE[1] + cfg.MARGIN_SIZE * (cfg.GAME_MATRIX_SIZE[1] + 1)
    screen.blit(text_max_score, (start_x+10, 10))
    screen.blit(text_score, (start_x+10, 20+text_score.get_rect().height))
    start_y = 30 + text_score.get_rect().height + text_max_score.get_rect().height
    return (start_x, start_y)


'''Game introduction'''
def drawGameIntro(screen, start_x, start_y, cfg):
    start_y += 40
    font_color = (0, 0, 0)
    font_size_big = 30
    font_size_small = 20
    font_big = pygame.font.Font(cfg.FONTPATH, font_size_big)
    font_small = pygame.font.Font(cfg.FONTPATH, font_size_small)
    intros = ['Game play:', ' Slide the keyboard up, down, left and right.', 'Combine two identical numbers', 'For example: 2 + 2 = 4, 4 + 4 = 8... Until 1024 + 1024 = 2048!', 'You win!']
    for idx, intro in enumerate(intros):
        font = font_big if idx == 0 else font_small
        text = font.render(intro, True, font_color)
        screen.blit(text, (start_x+10, start_y))
        start_y += text.get_rect().height + 10

Game end interface:

Attached source code:

def endInterface(screen, cfg):
    font_size_big = 60
    font_size_small = 30
    font_color = (255, 255, 255)
    font_big = pygame.font.Font(cfg.FONTPATH, font_size_big)
    font_small = pygame.font.Font(cfg.FONTPATH, font_size_small)
    surface = screen.convert_alpha()
    surface.fill((127, 255, 212, 2))
    text = font_big.render('Game Over!', True, font_color)
    text_rect = text.get_rect()
    text_rect.centerx, text_rect.centery = cfg.SCREENSIZE[0]/2, cfg.SCREENSIZE[1]/2-50
    surface.blit(text, text_rect)
    button_width, button_height = 100, 40
    button_start_x_left = cfg.SCREENSIZE[0] / 2 - button_width - 20
    button_start_x_right = cfg.SCREENSIZE[0] / 2 + 20
    button_start_y = cfg.SCREENSIZE[1] / 2 - button_height / 2 + 20
    pygame.draw.rect(surface, (0, 255, 255), (button_start_x_left, button_start_y, button_width, button_height))
    text_restart = font_small.render('Restart', True, font_color)
    text_restart_rect = text_restart.get_rect()
    text_restart_rect.centerx, text_restart_rect.centery = button_start_x_left + button_width / 2, button_start_y + button_height / 2
    surface.blit(text_restart, text_restart_rect)
    pygame.draw.rect(surface, (0, 255, 255), (button_start_x_right, button_start_y, button_width, button_height))
    text_quit = font_small.render('Quit', True, font_color)
    text_quit_rect = text_quit.get_rect()
    text_quit_rect.centerx, text_quit_rect.centery = button_start_x_right + button_width / 2, button_start_y + button_height / 2
    surface.blit(text_quit, text_quit_rect)
    while True:
        screen.blit(surface, (0, 0))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.MOUSEBUTTONDOWN and event.button:
                if text_quit_rect.collidepoint(pygame.mouse.get_pos()):
                    return False
                if text_restart_rect.collidepoint(pygame.mouse.get_pos()):
                    return True
        pygame.display.update()

Main program:

def main(cfg):
    # Game initialization
    pygame.init()
    screen = pygame.display.set_mode(cfg.SCREENSIZE)
    pygame.display.set_caption('2048 Small game upgrade')
    # Play background music
    pygame.mixer.music.load(cfg.BGMPATH)
    pygame.mixer.music.play(-1)
    # Instantiate 2048 games
    game_2048 = Game2048(matrix_size=cfg.GAME_MATRIX_SIZE, max_score_filepath=cfg.MAX_SCORE_FILEPATH)
    # Game main loop
    clock = pygame.time.Clock()
    is_running = True
    while is_running:
        screen.fill(pygame.Color(cfg.BG_COLOR))
        # --Key detection
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == pygame.KEYDOWN:
                if event.key in [pygame.K_UP, pygame.K_DOWN, pygame.K_LEFT, pygame.K_RIGHT]:
                    game_2048.setDirection({pygame.K_UP: 'up', pygame.K_DOWN: 'down', pygame.K_LEFT: 'left', pygame.K_RIGHT: 'right'}[event.key])
        # --Update game status
        game_2048.update()
        if game_2048.isgameover:
            game_2048.saveMaxScore()
            is_running = False
        # --Draw the necessary game elements to the screen
        drawGameMatrix(screen, game_2048.game_matrix, cfg)
        start_x, start_y = drawScore(screen, game_2048.score, game_2048.max_score, cfg)
        drawGameIntro(screen, start_x, start_y, cfg)
        # --Screen update
        pygame.display.update()
        clock.tick(cfg.FPS)
    return endInterface(screen, cfg)


'''run'''
if __name__ == '__main__':
    while True:
        if not main(cfg):
            break

2048 game effect is shown in the figure below:

I wanted to cut a picture of 2048 for you, but it got stuck after playing for several times. You'll make do with it. BAM ~ I tried my best!

summary

All right! This upgraded 2048 game is over here. Come and try. Can you pass the customs?

Free source code collection office:

For the complete project source code + material source code base, see: # private letter editor 06# or click the blue text to add to get free benefits!

Your support is my biggest motivation!! Remember the third company ~mua   Welcome to read previous articles~

Tags: Python Programmer pygame

Posted on Mon, 25 Oct 2021 00:13:23 -0400 by Harbinger