The principle is introduced

“Using the arrow keys to move squares, two squares with the same number collide and merge into a new square with twice the number. Just merge as many of these squares as you can.” First, initialize the game and play a song you like in the background:

Pygame.init () screen = pygame.display.set_mode(cfg.screensize) Pygame.display.set_caption ('2048 -- ') # pygame.mixer.music.load(cfg.BGMPATH) pygame.mixer.music.play(-1, 30)Copy the code

Then, we will define a 2048 game class, which is mainly responsible for the implementation of 2048 game rules:

Class Game2048(object): def __init__(self, matrix_size=(4, 4), max_score_filepath=None, **kwargs): # matrix_size: (num_rows, Self. max_score_filepath = max_score_filepath self.initialize()Copy the code

Specifically, let’s start with a two-dimensional list to save the current state of the game:

self.game_matrix = [['null' for _ in range(self.matrix_size[1])] for _ in range(self.matrix_size[0])]

Copy the code

Null indicates that there is no number in the current block. Otherwise, the corresponding position is represented by the current number. Obviously, the current state of game 2048 can be represented by a 4*4 list:

To start the game, we need to randomly select two positions in this two-dimensional list to generate numbers (i.e. 2 or 4) :

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 = the random choice (empty_pos) self. Game_matrix [I] [j] = 2. If the random random () > 0.1 else 4 self. RandomGenerateNumber () self.randomGenerateNumber()Copy the code

Then, when the player presses the arrow keys (↑, ↓, ←→), the two-dimensional list is updated according to the player’s instructions. It is divided into two parts: moving all the numeric blocks and doing the necessary merging and scoring; Randomly generates a number at a location where there is no number yet. Specifically, the code is implemented as follows:

Def update(self): game_matrix_before = copy. Deepcopy (self.game_matrix) self.move() if game_matrix_before! = self.game_matrix: self.randomGenerateNumber()Copy the code

The code implementation for moving all the numbers and making the necessary merges is as follows:

Def extract(array): array_new = [] for item in array: if item! = 'null': array_new.append(item) return array_new # 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), Return if self. Move_direction is None: return if self. 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] # 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] # left elif self.matrix_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] = self.game_matrix 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 = NoneCopy the code

We’re tired of thinking about it (it’s 4 by 4 T_T anyway), so we just go through this two-dimensional list to do whatever we want. Finally, let’s write a function that determines if the game is over based on the current state of the game:

@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 TrueCopy the code

It’s very simple. If the two-dimensional list is filled with numbers, and the numbers can no longer be combined, the game is over. Otherwise, the game is not over. After 2048 game class is defined, our game is basically finished. Just update the current state of the game in the main loop according to the user’s actions and display all the necessary elements of the game on the screen:

Clock = pygame.time.clock () is_running = True while is_running: 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() if game_204.isgameover: Game_204.savemaxscore () is_running = False # drawGameMatrix(screen, game_204.game_matrix,) cfg) start_x, start_y = drawScore(screen, game_2048.score, game_2048.max_score, cfg) drawGameIntro(screen, start_x, Pygame.display.update () clock.tick(cfg.fps) return endInterface(screen, CFG)Copy the code

The final result looks something like this:

Although I can write this game, but play this hand game I am still a vegetable chicken, hey hey ~