A few days ago wrote a 2048 program, is based on Python3 + Pygame implementation, for beginners to learn Python should be a good practice project, now will share the source code to everyone, add a clear note, I believe you can see it is very clear
The running effect is as follows:
The complete code is as follows, if you need to download the material (such as images, fonts, can go to www.itprojects.cn/detail.html… To download)
import random
import sys
from collections import可迭代from functools import reduce
import pygame
# Screen size
WIDTH, HEIGHT = (650.370)
# Background color
BG_COLOR = '#92877d'
# Data needed for the board
MARGIN_SIZE = 10 # Spacing size
BLOCK_SIZE = 80 # Chess position size
def draw_tips(screen) :
""" Display prompt information """
# display "score:"
tips_img = pygame.image.load("resources/images/tips.png")
screen.blit(tips_img, (375.200))
def get_score(chess_nums_temp) :
""" Calculates the total score of the current board. ""
def sum_all(x, y) :
if isinstance(x, Iterable):
return sum(x) + sum(y)
return x + sum(y)
return reduce(sum_all, chess_nums_temp)
def draw_score(screen, score) :
""" Show the score """
# display number
font_size_big = 60
font_color = (0.255.255)
font_big = pygame.font.Font("resources/font/Gabriola.ttf", font_size_big)
score = font_big.render(str(score), True, font_color)
screen.blit(score, (470.25))
# display "score:"
score_img = pygame.image.load("resources/images/score.png")
screen.blit(score_img, (370.30))
def show_game_over(screen) :
font_size_big = 60
font_size_small = 30
font_color = (255.255.255)
font_big = pygame.font.Font("resources/font/Gabriola.ttf", font_size_big)
font_small = pygame.font.Font("resources/font/Gabriola.ttf", 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 = WIDTH / 2, HEIGHT / 2 - 50
surface.blit(text, text_rect)
button_width, button_height = 100.40
button_start_x_left = WIDTH / 2 - button_width - 20
button_start_x_right = WIDTH / 2 + 20
button_start_y = HEIGHT / 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)
clock = pygame.time.Clock()
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()):
sys.exit()
if text_restart_rect.collidepoint(pygame.mouse.get_pos()):
return True
pygame.display.update()
clock.tick(60)
def judge_game_over(field) :
""" As long as you can move in one direction, the game is not over. """
return not any([judge_move_left(field), judge_move_right(field), judge_move_up(field), judge_move_down(field)])
def judge_move_up(chess_nums_temp) :
# transpose the numbers in the checkerboard so that the numbers in row 2 and column 3 become row 3 and column 2
# zip: implementation
# *chess_nums_temp unpacks the list
chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
return judge_move_left(chess_nums_temp)
def judge_move_down(chess_nums_temp) :
Logic: determine if you can move down, that is, transpose the elements and determine if the board can move to the right after transpose.
# 1. Row and column transpose
chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
# 2. Determine if you can move to the right
return judge_move_right(chess_nums_temp)
def judge_move_left(chess_nums_temp) :
# Return True as long as any row of the board can be moved to the left
for row in chess_nums_temp:
for i in range(3) :Judge each line 3 times
# if the number on the left is 0 and the number on the right is not 0, then we can move to the left.
if row[i] == 0 and row[i + 1] != 0:
return True
elifrow[i] ! =0 and row[i + 1] == row[i]:
# if the number on the left is not 0, and the two numbers on the left and right are equal, then we can move to the left.
return True
return False
def judge_move_right(chess_nums_temp) :
# Invert each line of the checkerboard, then you can use the function to the left
return judge_move_left([row[::-1] for row in chess_nums_temp])
def move_left(chess_nums_temp) :
for i, row in enumerate(chess_nums_temp):
# 1. Put the non-zero digits in this line forward and the zeros back. [0, 2, 2, 2]-->[2, 2, 2, 0]
row = sorted(row, key=lambda x: 1 if x == 0 else 0)
# 2. Check if two numbers are equal. If the first number is equal to 2, the second number is 0. For example [2, 2, 2, 0]-->[4, 0, 2, 0]
for index in range(3) :if row[index] == row[index + 1]:
row[index] *= 2
row[index + 1] = 0
# 3. Remove the gap after the merge, i.e. non-0 to the left and 0 to the right. For example [4, 0, 2, 0]-->[4, 2, 0, 0]
row = sorted(row, key=lambda x: 1 if x == 0 else 0)
# 4. Update the list of numbers because this line is already after the operation
chess_nums_temp[i] = row
return chess_nums_temp
def move_right(chess_nums_temp) :
# Scroll first
chess_nums_temp = [row[::-1] for row in chess_nums_temp]
# Then call functions like left move
move_left(chess_nums_temp)
# Finally, flip it again to achieve the previous look
return [row[::-1] for row in chess_nums_temp]
def move_up(chess_nums_temp) :
Row and column transpose
chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
# Move left
chess_nums_temp = move_left(chess_nums_temp)
Undo by transposing row and column again
return [list(row) for row in zip(*chess_nums_temp)]
def move_down(chess_nums_temp) :
Row and column transpose
chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
# Move right
chess_nums_temp = move_right(chess_nums_temp)
Undo by transposing row and column again
return [list(row) for row in zip(*chess_nums_temp)]
def move(chess_nums_temp, direction) :
""" Move the number according to the direction """
# store the function that determines whether each direction can be moved
judge_move_func_dict = {
'left': judge_move_left,
'right': judge_move_right,
'up': judge_move_up,
'down': judge_move_down
}
# Store functions that move in all directions
move_func_dict = {
'left': move_left,
'right': move_right,
'up': move_up,
'down': move_down
}
Call the corresponding function to determine if it can move in this direction
ret = judge_move_func_dict[direction](chess_nums_temp)
print("%s direction can move:" % direction, ret)
if ret:
chess_nums_temp = move_func_dict[direction](chess_nums_temp)
create_random_num(chess_nums_temp)
# return the list, if updated is new, if not updated is the previous one
return chess_nums_temp
def get_num_color(num) :
According to the current number to display, extract the background color and font color corresponding to the number: [grid background color, font color in the grid] ""
color_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'].0: ['#9e948a'.None]}return color_dict[num]
def create_random_num(nums_temp) :
"" generate a random number in the checkerboard. ""
# Store all empty locations
positions = list(a)for row, line in enumerate(nums_temp):
for col, num in enumerate(line):
if num == 0:
positions.append((row, col))
# Select a random location from the list of empty locations and unpack it
row, col = random.choice(positions)
nums_temp[row][col] = random.choice([2.4.2]) # Pick 2 2's and 1 4's at random, so 2's are twice as likely to be picked as 4's
def draw_nums(screen, chess_nums_temp) :
""" Display the numbers on the board """
# Prepare fonts, etc
font_size = BLOCK_SIZE - 10
font = pygame.font.Font("./resources/font/Gabriola.ttf", font_size)
# iterate over numbers
for i, line in enumerate(chess_nums_temp):
for j, num in enumerate(line):
ifnum ! =0:
# Calculate display position (x coordinates, y coordinates)
x = MARGIN_SIZE * (j + 1) + BLOCK_SIZE * j
y = MARGIN_SIZE * (i + 1) + BLOCK_SIZE * i
# get color
font_color = pygame.Color(get_num_color(num)[1])
# display number
text = font.render(str(num), True, font_color)
text_rect = text.get_rect()
text_rect.centerx, text_rect.centery = x + BLOCK_SIZE / 2, y + BLOCK_SIZE / 2
# Redraw this box with the corresponding numeric background color
pygame.draw.rect(screen, pygame.Color(get_num_color(num)[0]), (x, y, BLOCK_SIZE, BLOCK_SIZE))
screen.blit(text, text_rect)
def draw_chess_board(screen) :
""" Display board """
for i in range(4) :for j in range(4):
x = MARGIN_SIZE * (j + 1) + BLOCK_SIZE * j
y = MARGIN_SIZE * (i + 1) + BLOCK_SIZE * i
pygame.draw.rect(screen, pygame.Color('#f9f6f2'), (x, y, BLOCK_SIZE, BLOCK_SIZE))
def run(screen) :
# define a list of all numbers on the current board, 0 if there are no numbers
chess_nums = [[0 for _ in range(4)] for _ in range(4)]
# Generate a random number
create_random_num(chess_nums)
create_random_num(chess_nums)
Record the current score
score = get_score(chess_nums)
# Create timer (prevent while loop too fast, use too much CPU)
clock = pygame.time.Clock()
while True:
# Event detection (mouse click, keyboard press, etc.)
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]:
direction = {pygame.K_UP: 'up', pygame.K_DOWN: 'down', pygame.K_LEFT: 'left', pygame.K_RIGHT: 'right'}[event.key]
print("Pressed the arrow key :", direction)
chess_nums = move(chess_nums, direction)
if judge_game_over(chess_nums):
print("Game over....")
return
# Recalculate every time you press the arrow key
score = get_score(chess_nums)
Display the background color
screen.fill(pygame.Color(BG_COLOR))
# display checkerboard
draw_chess_board(screen)
# Display the numbers on the board
draw_nums(screen, chess_nums)
# display score
draw_score(screen, score)
# display action prompt
draw_tips(screen)
# refresh the display (at which point the window will actually display)
pygame.display.update()
# FPS (number of frames displayed per second)
clock.tick(60) # Through a certain delay, to achieve 1 second can cycle 60 times
def main() :
# Game initialization
pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
while True:
# Run the game once
run(screen)
# show the end of the game, whether to restart
show_game_over(screen)
if __name__ == '__main__':
main()
Copy the code