zoukankan      html  css  js  c++  java
  • 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()
  • 相关阅读:
    Session的使用与Session的生命周期
    Long-Polling, Websockets, SSE(Server-Sent Event), WebRTC 之间的区别与使用
    十九、详述 IntelliJ IDEA 之 添加 jar 包
    十八、IntelliJ IDEA 常用快捷键 之 Windows 版
    十七、IntelliJ IDEA 中的 Maven 项目初体验及搭建 Spring MVC 框架
    十六、详述 IntelliJ IDEA 创建 Maven 项目及设置 java 源目录的方法
    十五、详述 IntelliJ IDEA 插件的安装及使用方法
    十四、详述 IntelliJ IDEA 提交代码前的 Code Analysis 机制
    十三、IntelliJ IDEA 中的版本控制介绍(下)
    十二、IntelliJ IDEA 中的版本控制介绍(中)
  • 原文地址:https://www.cnblogs.com/dong4716138/p/14462954.html
Copyright © 2011-2022 走看看