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()

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM