当前位置:首页 > Python教程 > python技巧

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

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

运行效果如下:

技术分享图片

技术分享图片

游戏结束后的效果如下:

技术分享图片

 

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

              1
            import
             random

              2
            import
             sys

              3
            from collections import Iterable
  4from functools import reduce
  5  6import pygame
  7  8# 屏幕尺寸  9 WIDTH, HEIGHT = (650, 370)
 10# 背景颜色 11 BG_COLOR = #92877d 12# 棋盘需要的数据 13 MARGIN_SIZE = 10  # 间隔大小 14 BLOCK_SIZE = 80  # 棋子位置大小 15 16 17def 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 26def get_score(chess_nums_temp):
 27""" 28    计算当前棋盘的总分数
 29""" 30 31def sum_all(x, y):
 32if isinstance(x, Iterable):
 33return sum(x) + sum(y)
 34return x + sum(y)
 35 36return reduce(sum_all, chess_nums_temp)
 37 38 39def 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 54def 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()
 81while True:
 82        screen.blit(surface, (0, 0))
 83for event in pygame.event.get():
 84if event.type == pygame.QUIT:
 85                pygame.quit()
 86                sys.exit()
 87elif event.type == pygame.MOUSEBUTTONDOWN and event.button:
 88if text_quit_rect.collidepoint(pygame.mouse.get_pos()):
 89                    sys.exit()
 90if text_restart_rect.collidepoint(pygame.mouse.get_pos()):
 91return True
 92        pygame.display.update()
 93         clock.tick(60)
 94 95 96def judge_game_over(field):
 97""" 98    只要有1个方向可以移动,那么游戏就没结束
 99"""100returnnot any([judge_move_left(field), judge_move_right(field), judge_move_up(field), judge_move_down(field)])
101102103def 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)]
108return judge_move_left(chess_nums_temp)
109110111def 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. 判断是否可以向右移动118return judge_move_right(chess_nums_temp)
119120121def judge_move_left(chess_nums_temp):
122# 只要棋盘的任意一行可以向左移动, 就返回True123for row in chess_nums_temp:
124for i in range(3):  # 每一行判断3次125# 如果判断的左边的数为0,右边的数不为0,则说明可以向左移动;126if row[i] == 0 and row[i + 1] != 0:
127return True
128elif row[i] != 0 and row[i + 1] == row[i]:
129# 如果判断的左边的数不为0,且左右2个数相等,则说明可以向左移动;130return True
131return False
132133134def judge_move_right(chess_nums_temp):
135# 对棋盘的每一行元素进行反转,此时就可以用向左的函数进行判断了136return judge_move_left([row[::-1] for row in chess_nums_temp])
137138139def move_left(chess_nums_temp):
140for 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)
143144# 2.依次循环判断两个数是否相等,如果相等 第一个*2 第二个数为0。例如[2, 2, 2, 0]-->[4, 0, 2, 0]145for index in range(3):
146if row[index] == row[index + 1]:
147                 row[index] *= 2
148                 row[index + 1] = 0
149150# 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
154return chess_nums_temp
155156157def 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# 最后再次翻转,实现之前的样子163return [row[::-1] for row in chess_nums_temp]
164165166def 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# 再次"行与列转置"从而实现复原172return [list(row) for row in zip(*chess_nums_temp)]
173174175def 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# 再次"行与列转置"从而实现复原181return [list(row) for row in zip(*chess_nums_temp)]
182183184def move(chess_nums_temp, direction):
185"""186    根据方向移动数字
187"""188# 存储判断各个方向是否可移动对应的函数189     judge_move_func_dict = {
190left: judge_move_left,
191right: judge_move_right,
192up: judge_move_up,
193down: judge_move_down
194    }
195# 存储各个方向移动的函数196     move_func_dict = {
197left: move_left,
198right: move_right,
199up: move_up,
200down: move_down
201    }
202203# 调用对应的函数,判断是否可以朝这个方向移动204     ret = judge_move_func_dict[direction](chess_nums_temp)
205print("%s方向是否可以移动:" % direction, ret)
206if ret:
207         chess_nums_temp = move_func_dict[direction](chess_nums_temp)
208        create_random_num(chess_nums_temp)
209210# 返回列表,如果更新了就是新的,如果没有更新就是之前的那个211return chess_nums_temp
212213214def 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    }
227return color_dict[num]
228229230def create_random_num(nums_temp):
231"""232    在棋盘中随机生成一个数字
233"""234# 存储所有空位置235     positions = list()
236for row, line in enumerate(nums_temp):
237for col, num in enumerate(line):
238if num == 0:
239                positions.append((row, col))
240241# 随机从空位置列表中抽取一个,然后拆包242     row, col = random.choice(positions)
243     nums_temp[row][col] = random.choice([2, 4, 2])  # 随机从2个2,1个4中抽取,这样抽到2的概率是4的2倍244245246def 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# 遍历数字254for i, line in enumerate(chess_nums_temp):
255for j, num in enumerate(line):
256if 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)
269270271def draw_chess_board(screen):
272"""273    显示棋盘
274"""275for i in range(4):
276for 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))
280281282def run(screen):
283# 定义列表,用来记录当前棋盘上的所有数字,如果某位置没有数字,则为0284     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()
292while True:
293# 事件检测(鼠标点击、键盘按下等)294for event in pygame.event.get():
295if event.type == pygame.QUIT:
296                pygame.quit()
297                sys.exit()
298elif event.type == pygame.KEYDOWN:
299if 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]
301print("按下了方向键:", direction)
302                     chess_nums = move(chess_nums, direction)
303if judge_game_over(chess_nums):
304print("游戏结束....")
305return306# 每按下方向键,就重新计算307                     score = get_score(chess_nums)
308309# 显示背景色310        screen.fill(pygame.Color(BG_COLOR))
311312# 显示棋盘313        draw_chess_board(screen)
314315# 显示棋盘上的数字316        draw_nums(screen, chess_nums)
317318# 显示分数319        draw_score(screen, score)
320321# 显示操作提示322        draw_tips(screen)
323324# 刷新显示(此时窗口才会真正的显示)325        pygame.display.update()
326327# FPS(每秒钟显示画面的次数)328         clock.tick(60)  # 通过一定的延时,实现1秒钟能够循环60次329330331def main():
332# 游戏初始化333    pygame.init()
334     screen = pygame.display.set_mode((WIDTH, HEIGHT))
335while True:
336# 运行一次游戏337        run(screen)
338# 显示游戏结束,是否重来339        show_game_over(screen)
340341342if__name__ == __main__:
343     main()

 

原文:https://www.cnblogs.com/dong4716138/p/14462954.html


【说明】本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!