• python3+pygame实现的2048,非常完整的代码


    前几天写了一个2048程序,是基于python3+pygame实现的,对于初学python的同学来说应该是很好的练手项目,现在将源码分享给大家,添加了清晰的注释,相信大家能看的很明白

    运行效果如下:

    2048游戏

    2048游戏

    游戏结束后的效果如下:

    2048游戏

    完整代码如下,如果需要下载素材(图片、字体等可以到 https://www.itprojects.cn/web/material/details.html?id=10进行下载)

      1 import random
      2 import sys
      3 from collections import Iterable
      4 from functools import reduce
      5 
      6 import pygame
      7 
      8 # 屏幕尺寸
      9 WIDTH, HEIGHT = (650, 370)
     10 # 背景颜色
     11 BG_COLOR = '#92877d'
     12 # 棋盘需要的数据
     13 MARGIN_SIZE = 10  # 间隔大小
     14 BLOCK_SIZE = 80  # 棋子位置大小
     15 
     16 
     17 def draw_tips(screen):
     18     """
     19     显示提示信息
     20     """
     21     # 显示"分数:"
     22     tips_img = pygame.image.load("resources/images/tips.png")
     23     screen.blit(tips_img, (375, 200))
     24 
     25 
     26 def get_score(chess_nums_temp):
     27     """
     28     计算当前棋盘的总分数
     29     """
     30 
     31     def sum_all(x, y):
     32         if isinstance(x, Iterable):
     33             return sum(x) + sum(y)
     34         return x + sum(y)
     35 
     36     return reduce(sum_all, chess_nums_temp)
     37 
     38 
     39 def draw_score(screen, score):
     40     """
     41     显示分数
     42     """
     43     # 显示数字
     44     font_size_big = 60
     45     font_color = (0, 255, 255)
     46     font_big = pygame.font.Font("resources/font/Gabriola.ttf", font_size_big)
     47     score = font_big.render(str(score), True, font_color)
     48     screen.blit(score, (470, 25))
     49     # 显示"分数:"
     50     score_img = pygame.image.load("resources/images/score.png")
     51     screen.blit(score_img, (370, 30))
     52 
     53 
     54 def show_game_over(screen):
     55     font_size_big = 60
     56     font_size_small = 30
     57     font_color = (255, 255, 255)
     58     font_big = pygame.font.Font("resources/font/Gabriola.ttf", font_size_big)
     59     font_small = pygame.font.Font("resources/font/Gabriola.ttf", font_size_small)
     60     surface = screen.convert_alpha()
     61     surface.fill((127, 255, 212, 2))
     62     text = font_big.render('Game Over!', True, font_color)
     63     text_rect = text.get_rect()
     64     text_rect.centerx, text_rect.centery = WIDTH / 2, HEIGHT / 2 - 50
     65     surface.blit(text, text_rect)
     66     button_width, button_height = 100, 40
     67     button_start_x_left = WIDTH / 2 - button_width - 20
     68     button_start_x_right = WIDTH / 2 + 20
     69     button_start_y = HEIGHT / 2 - button_height / 2 + 20
     70     pygame.draw.rect(surface, (0, 255, 255), (button_start_x_left, button_start_y, button_width, button_height))
     71     text_restart = font_small.render('Restart', True, font_color)
     72     text_restart_rect = text_restart.get_rect()
     73     text_restart_rect.centerx, text_restart_rect.centery = button_start_x_left + button_width / 2, button_start_y + button_height / 2
     74     surface.blit(text_restart, text_restart_rect)
     75     pygame.draw.rect(surface, (0, 255, 255), (button_start_x_right, button_start_y, button_width, button_height))
     76     text_quit = font_small.render('Quit', True, font_color)
     77     text_quit_rect = text_quit.get_rect()
     78     text_quit_rect.centerx, text_quit_rect.centery = button_start_x_right + button_width / 2, button_start_y + button_height / 2
     79     surface.blit(text_quit, text_quit_rect)
     80     clock = pygame.time.Clock()
     81     while True:
     82         screen.blit(surface, (0, 0))
     83         for event in pygame.event.get():
     84             if event.type == pygame.QUIT:
     85                 pygame.quit()
     86                 sys.exit()
     87             elif event.type == pygame.MOUSEBUTTONDOWN and event.button:
     88                 if text_quit_rect.collidepoint(pygame.mouse.get_pos()):
     89                     sys.exit()
     90                 if text_restart_rect.collidepoint(pygame.mouse.get_pos()):
     91                     return True
     92         pygame.display.update()
     93         clock.tick(60)
     94 
     95 
     96 def judge_game_over(field):
     97     """
     98     只要有1个方向可以移动,那么游戏就没结束
     99     """
    100     return not any([judge_move_left(field), judge_move_right(field), judge_move_up(field), judge_move_down(field)])
    101 
    102 
    103 def judge_move_up(chess_nums_temp):
    104     # 对棋盘的数字进行「行与列转置」,即原来在第2行第3列变为第3行第2列
    105     # zip: 实现
    106     # *chess_nums_temp对列表进行拆包
    107     chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
    108     return judge_move_left(chess_nums_temp)
    109 
    110 
    111 def judge_move_down(chess_nums_temp):
    112     """
    113     逻辑:判断能否向下移动, 也就是对于元素进行转置, 判断转置后的棋盘能否向右移动
    114     """
    115     # 1.「行与列转置」
    116     chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
    117     # 2. 判断是否可以向右移动
    118     return judge_move_right(chess_nums_temp)
    119 
    120 
    121 def judge_move_left(chess_nums_temp):
    122     # 只要棋盘的任意一行可以向左移动, 就返回True
    123     for row in chess_nums_temp:
    124         for i in range(3):  # 每一行判断3次
    125             # 如果判断的左边的数为0,右边的数不为0,则说明可以向左移动;
    126             if row[i] == 0 and row[i + 1] != 0:
    127                 return True
    128             elif row[i] != 0 and row[i + 1] == row[i]:
    129                 # 如果判断的左边的数不为0,且左右2个数相等,则说明可以向左移动;
    130                 return True
    131     return False
    132 
    133 
    134 def judge_move_right(chess_nums_temp):
    135     # 对棋盘的每一行元素进行反转,此时就可以用向左的函数进行判断了
    136     return judge_move_left([row[::-1] for row in chess_nums_temp])
    137 
    138 
    139 def move_left(chess_nums_temp):
    140     for i, row in enumerate(chess_nums_temp):
    141         # 1.把这一行的非0 数字向前放,把0向后放。例如之前是[0, 2, 2, 2]-->[2, 2, 2, 0]
    142         row = sorted(row, key=lambda x: 1 if x == 0 else 0)
    143 
    144         # 2.依次循环判断两个数是否相等,如果相等 第一个*2 第二个数为0。例如[2, 2, 2, 0]-->[4, 0, 2, 0]
    145         for index in range(3):
    146             if row[index] == row[index + 1]:
    147                 row[index] *= 2
    148                 row[index + 1] = 0
    149 
    150         # 3.将合并之后的空隙移除,即非0靠左,0靠右。例如[4, 0, 2, 0]-->[4, 2, 0, 0]
    151         row = sorted(row, key=lambda x: 1 if x == 0 else 0)
    152         # 4. 更新数字列表,因为这一行已经是操作之后的了
    153         chess_nums_temp[i] = row
    154     return chess_nums_temp
    155 
    156 
    157 def move_right(chess_nums_temp):
    158     # 先翻翻转
    159     chess_nums_temp = [row[::-1] for row in chess_nums_temp]
    160     # 然后在调用像左移动的功能
    161     move_left(chess_nums_temp)
    162     # 最后再次翻转,实现之前的样子
    163     return [row[::-1] for row in chess_nums_temp]
    164 
    165 
    166 def move_up(chess_nums_temp):
    167     # "行与列转置"
    168     chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
    169     # 向左移动
    170     chess_nums_temp = move_left(chess_nums_temp)
    171     # 再次"行与列转置"从而实现复原
    172     return [list(row) for row in zip(*chess_nums_temp)]
    173 
    174 
    175 def move_down(chess_nums_temp):
    176     # "行与列转置"
    177     chess_nums_temp = [list(row) for row in zip(*chess_nums_temp)]
    178     # 向右移动
    179     chess_nums_temp = move_right(chess_nums_temp)
    180     # 再次"行与列转置"从而实现复原
    181     return [list(row) for row in zip(*chess_nums_temp)]
    182 
    183 
    184 def move(chess_nums_temp, direction):
    185     """
    186     根据方向移动数字
    187     """
    188     # 存储判断各个方向是否可移动对应的函数
    189     judge_move_func_dict = {
    190         'left': judge_move_left,
    191         'right': judge_move_right,
    192         'up': judge_move_up,
    193         'down': judge_move_down
    194     }
    195     # 存储各个方向移动的函数
    196     move_func_dict = {
    197         'left': move_left,
    198         'right': move_right,
    199         'up': move_up,
    200         'down': move_down
    201     }
    202 
    203     # 调用对应的函数,判断是否可以朝这个方向移动
    204     ret = judge_move_func_dict[direction](chess_nums_temp)
    205     print("%s方向是否可以移动:" % direction, ret)
    206     if ret:
    207         chess_nums_temp = move_func_dict[direction](chess_nums_temp)
    208         create_random_num(chess_nums_temp)
    209 
    210     # 返回列表,如果更新了就是新的,如果没有更新就是之前的那个
    211     return chess_nums_temp
    212 
    213 
    214 def get_num_color(num):
    215     """
    216     根据当前要显示的数字,提取出背景色以及字体颜色
    217     对应的数字:[方格背景颜色, 方格里的字体颜色]
    218     """
    219     color_dict = {
    220         2: ['#eee4da', '#776e65'], 4: ['#ede0c8', '#776e65'], 8: ['#f2b179', '#f9f6f2'],
    221         16: ['#f59563', '#f9f6f2'], 32: ['#f67c5f', '#f9f6f2'], 64: ['#f65e3b', '#f9f6f2'],
    222         128: ['#edcf72', '#f9f6f2'], 256: ['#edcc61', '#f9f6f2'], 512: ['#edc850', '#f9f6f2'],
    223         1024: ['#edc53f', '#f9f6f2'], 2048: ['#edc22e', '#f9f6f2'], 4096: ['#eee4da', '#776e65'],
    224         8192: ['#edc22e', '#f9f6f2'], 16384: ['#f2b179', '#776e65'], 32768: ['#f59563', '#776e65'],
    225         65536: ['#f67c5f', '#f9f6f2'], 0: ['#9e948a', None]
    226     }
    227     return color_dict[num]
    228 
    229 
    230 def create_random_num(nums_temp):
    231     """
    232     在棋盘中随机生成一个数字
    233     """
    234     # 存储所有空位置
    235     positions = list()
    236     for row, line in enumerate(nums_temp):
    237         for col, num in enumerate(line):
    238             if num == 0:
    239                 positions.append((row, col))
    240 
    241     # 随机从空位置列表中抽取一个,然后拆包
    242     row, col = random.choice(positions)
    243     nums_temp[row][col] = random.choice([2, 4, 2])  # 随机从2个2,1个4中抽取,这样抽到2的概率是4的2倍
    244 
    245 
    246 def draw_nums(screen, chess_nums_temp):
    247     """
    248     显示棋盘上的数字
    249     """
    250     # 准备字体等
    251     font_size = BLOCK_SIZE - 10
    252     font = pygame.font.Font("./resources/font/Gabriola.ttf", font_size)
    253     # 遍历数字
    254     for i, line in enumerate(chess_nums_temp):
    255         for j, num in enumerate(line):
    256             if num != 0:
    257                 # 计算显示位置(x坐标、y坐标)
    258                 x = MARGIN_SIZE * (j + 1) + BLOCK_SIZE * j
    259                 y = MARGIN_SIZE * (i + 1) + BLOCK_SIZE * i
    260                 # 获取颜色
    261                 font_color = pygame.Color(get_num_color(num)[1])
    262                 # 显示数字
    263                 text = font.render(str(num), True, font_color)
    264                 text_rect = text.get_rect()
    265                 text_rect.centerx, text_rect.centery = x + BLOCK_SIZE / 2, y + BLOCK_SIZE / 2
    266                 # 用对应的数字背景色,重新绘制这个方块
    267                 pygame.draw.rect(screen, pygame.Color(get_num_color(num)[0]), (x, y, BLOCK_SIZE, BLOCK_SIZE))
    268                 screen.blit(text, text_rect)
    269 
    270 
    271 def draw_chess_board(screen):
    272     """
    273     显示棋盘
    274     """
    275     for i in range(4):
    276         for j in range(4):
    277             x = MARGIN_SIZE * (j + 1) + BLOCK_SIZE * j
    278             y = MARGIN_SIZE * (i + 1) + BLOCK_SIZE * i
    279             pygame.draw.rect(screen, pygame.Color('#f9f6f2'), (x, y, BLOCK_SIZE, BLOCK_SIZE))
    280 
    281 
    282 def run(screen):
    283     # 定义列表,用来记录当前棋盘上的所有数字,如果某位置没有数字,则为0
    284     chess_nums = [[0 for _ in range(4)] for _ in range(4)]
    285     # 随机生成一个数字
    286     create_random_num(chess_nums)
    287     create_random_num(chess_nums)
    288     # 记录当前的分数
    289     score = get_score(chess_nums)
    290     # 创建计时器(防止while循环过快,占用太多CPU的问题)
    291     clock = pygame.time.Clock()
    292     while True:
    293         # 事件检测(鼠标点击、键盘按下等)
    294         for event in pygame.event.get():
    295             if event.type == pygame.QUIT:
    296                 pygame.quit()
    297                 sys.exit()
    298             elif event.type == pygame.KEYDOWN:
    299                 if event.key in [pygame.K_UP, pygame.K_DOWN, pygame.K_LEFT, pygame.K_RIGHT]:
    300                     direction = {pygame.K_UP: 'up', pygame.K_DOWN: 'down', pygame.K_LEFT: 'left', pygame.K_RIGHT: 'right'}[event.key]
    301                     print("按下了方向键:", direction)
    302                     chess_nums = move(chess_nums, direction)
    303                     if judge_game_over(chess_nums):
    304                         print("游戏结束....")
    305                         return
    306                     # 每按下方向键,就重新计算
    307                     score = get_score(chess_nums)
    308 
    309         # 显示背景色
    310         screen.fill(pygame.Color(BG_COLOR))
    311 
    312         # 显示棋盘
    313         draw_chess_board(screen)
    314 
    315         # 显示棋盘上的数字
    316         draw_nums(screen, chess_nums)
    317 
    318         # 显示分数
    319         draw_score(screen, score)
    320 
    321         # 显示操作提示
    322         draw_tips(screen)
    323 
    324         # 刷新显示(此时窗口才会真正的显示)
    325         pygame.display.update()
    326 
    327         # FPS(每秒钟显示画面的次数)
    328         clock.tick(60)  # 通过一定的延时,实现1秒钟能够循环60次
    329 
    330 
    331 def main():
    332     # 游戏初始化
    333     pygame.init()
    334     screen = pygame.display.set_mode((WIDTH, HEIGHT))
    335     while True:
    336         # 运行一次游戏
    337         run(screen)
    338         # 显示游戏结束,是否重来
    339         show_game_over(screen)
    340 
    341 
    342 if __name__ == '__main__':
    343     main()
  • 相关阅读:
    520了,用32做个简单的小程序
    我要偷偷的学Python,然后惊呆所有人(第十三天)
    Python关于import的实验(10) 利用__init__.py文件将变量和包下的模块作为包的属性
    爬虫实战 如何爬取B站视频评论?
    【爬虫】对新笔趣阁小说进行爬取,保存和下载
    年轻就该多尝试,教你20小时Get一项新技能
    基于visual c++之windows核心编程代码分析(66)实现Windows服务的远程控制
    基于visual c++之windows核心编程代码分析(59)实现网络简单代理编程
    Windows Azure Traffic Manager的新的管理API
    现实世界的Windows Azure:采访Aidmatrix的交付副总裁Michael Ross
  • 原文地址:https://www.cnblogs.com/dong4716138/p/14462954.html
Copyright © 2020-2023  润新知