[python] python实现2048游戏,及代码解析。

  我初学python,有不对之处望大家指教。转载请征得同意。

  我在网络上也找了一些2048游戏代码的讲解,但都不是特别详细。所以我希望能够尽量详细的讲解。同时,有的地方我也不懂,希望大家能帮助补充。我会随时更新以方便后来者。

  当然,需要一定的python基础再看此实例。

  1 #-*- coding:utf-8 -*-
  2 
  3 import curses
  4 from random import randrange, choice
  5 from collections import defaultdict
  6 # 引入3个扩展包
  7 
  8 letter_codes = [ord(ch) for ch in 'WASDRQwasdrq']
  9 actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit']
 10 actions_dict = dict(zip(letter_codes, actions * 2))
 11 # 创建我们将要用的键盘输入字典,这个字典将在后边通过第18行的
 12 # keyboard.getch()
 13 # 而这个方法被封装成一个函数,调用函数以实现该方法。
 14 
 15 def get_user_action(keyboard):
 16     char = "N"
 17     while char not in actions_dict:
 18         char = keyboard.getch()
 19     return actions_dict[char]
 20 # 键盘输入以匹配字典的方法
 21 
 22 def transpose(field):
 23     return [list(row) for row in zip(*field)]
 24 # 矩阵转置,
 25 # 这是一个数学方法,如望详细了解,请先了解矩阵
 26 # 对于初学者,比较推荐暂时忽略。
 27 
 28 def invert(field):
 29     return [row[::-1] for row in field]
 30 # 矩阵逆转,同上
 31 
 32 class GameField(object):
 33     def __init__(self, height=4, width=4, win=2048):
 34         self.height = height
 35         self.width = width
 36         self.win_value = 2048
 37         self.score = 0
 38         self.highscore = 0
 39         self.reset()
 40     # 定义类的__init__方法,为初始化方法
 41 
 42     def reset(self):
 43         if self.score > self.highscore:
 44             self.highscore = self.score
 45         self.score = 0
 46         self.field = [[0 for i in range(self.width)] for j in range(self.height)]
 47         self.spawn()
 48         self.spawn()
 49     # 重置方法,虽然命名为reset,但是初始化也同样使用该方法。
 50     # 如果你觉得命名为set更合适,请改为set。
 51     # 这个方法中使用了spawn()函数,这个函数放在了后边,
 52     # spawn()函数的功能是生成新的数字,reset()需要生成两次。
 53 
 54 
 55     def move(self, direction):
 56     # 最重要的3个函数之一
 57         def move_row_left(row):
 58             def tighten(row):
 59                 new_row = [i for i in row if i != 0]
 60                 new_row += [0 for i in range(len(row) - len(new_row))]
 61                 return new_row
 62 
 63             def merge(row):
 64                 pair = False
 65                 new_row = []
 66                 for i in range(len(row)):
 67                     if pair:
 68                         new_row.append(2 * row[i])
 69                         self.score += 2 * row[i]
 70                         pair = False
 71                     else:
 72                         if i + 1 < len(row) and row[i] == row[i + 1]:
 73                             pair = True
 74                             new_row.append(0)
 75                         else:
 76                             new_row.append(row[i])
 77                 assert len(new_row) == len(row)
 78                 return new_row
 79             return tighten(merge(tighten(row)))
 80         #这里可以有不同的写法,就是tighten一次,merge一次。在merge的时候没必要加0了。
 81         # 欢迎大家把好的方法发给我,谢谢。http://www.cnblogs.com/danjawwi/
 82         # def merge(row):
 83         #     pair = False
 84         #     new_row = []
 85         #     for i in range(len(row)):
 86         #         if pair:
 87         #             new_row.append(2 * row[i])
 88         #             self.score += 2 * row[i]
 89         #             pair = False
 90         #         else:
 91         #             if i + 1 < len(row) and row[i] == row[i + 1]:
 92         #                 pair = True
 93         #             else:
 94         #                 new_row.append(row[i])
 95         #     new_row += [0 for j in range(len(row) - len(new_row))]
 96         #     return new_row
 97         #
 98         # return merge(tighten(row))
 99 
100         moves = {}
101         moves['Left']  = lambda field:                              
102                 [move_row_left(row) for row in field]
103         moves['Right'] = lambda field:                              
104                 invert(moves['Left'](invert(field)))
105         moves['Up']    = lambda field:                              
106                 transpose(moves['Left'](transpose(field)))
107         moves['Down']  = lambda field:                              
108                 transpose(moves['Right'](transpose(field)))
109         # 这里把row的迭代放在了方法外边,在对应字典值这里实现了。也可以放在方法里边
110         
111 
112         if direction in moves:
113             if self.move_is_possible(direction):
114                 self.field = moves[direction](self.field)
115                 self.spawn()
116                 return True
117             else:
118                 return False
119 
120     def is_win(self):
121         return any(any(i >= self.win_value for i in row) for row in self.field)
122     #判断是否赢
123 
124     def is_gameover(self):
125         return not any(self.move_is_possible(move) for move in actions)
126     # 判断是否输
127 
128     def draw(self, screen):
129     # 最重要的3个函数之一
130         help_string1 = '(W)Up (S)Down (A)Left (D)Right'
131         help_string2 = '     (R)Restart (Q)Exit'
132         gameover_string = '           GAME OVER'
133         win_string = '          YOU WIN!'
134         def cast(string):
135             screen.addstr(string + '
')
136 
137         def draw_hor_separator():
138             line = '+' + ('+------' * self.width + '+')[1:]
139             #不明白为什么这里要这样写
140             #直接line = '+------' * self.width + '+' 不行吗?
141             #http://www.cnblogs.com/danjawwi/
142             
143             separator = defaultdict(lambda: line)
144             if not hasattr(draw_hor_separator, "counter"):
145                 draw_hor_separator.counter = 0
146             cast(separator[draw_hor_separator.counter])
147             draw_hor_separator.counter += 1
148             #这里我也不明白,直接根据self.height输出不就行了?
149 
150         def draw_row(row):
151             cast(''.join('|{: ^5} '.format(num) if num > 0 else '|      ' for num in row) + '|')
152             #用到了join 和 format这两种方法。
153         screen.clear()
154         cast('SCORE: ' + str(self.score))
155         if 0 != self.highscore:
156             cast('HGHSCORE: ' + str(self.highscore))
157         for row in self.field:
158             draw_hor_separator()
159             draw_row(row)
160         draw_hor_separator()
161         if self.is_win():
162             cast(win_string)
163         else:
164             if self.is_gameover():
165                 cast(gameover_string)
166             else:
167                 cast(help_string1)
168         cast(help_string2)
169 
170     def spawn(self):
171         new_element = 4 if randrange(100) > 89 else 2
172         (i,j) = choice([(i,j) for i in range(self.width) for j in range(self.height) if self.field[i][j] == 0])
173         self.field[i][j] = new_element
174         #迭代器既可以根据层级来进行迭代,也可以在同层中迭代两次
175 
176     def move_is_possible(self, direction):
177     # 最重要的3个函数之一
178         def row_is_left_movable(row):
179             def change(i): 
180                 if row[i] == 0 and row[i + 1] != 0: 
181                 # 这里是不是在说None != 0 ?
182                     return True
183                 if row[i] != 0 and row[i + 1] == row[i]: 
184                     return True
185                 return False
186             return any(change(i) for i in range(len(row) - 1))
187 
188         check = {}
189         check['Left']  = lambda field:                              
190                 any(row_is_left_movable(row) for row in field)
191 
192         check['Right'] = lambda field:                              
193                  check['Left'](invert(field))
194 
195         check['Up']    = lambda field:                              
196                 check['Left'](transpose(field))
197 
198         check['Down']  = lambda field:                              
199                 check['Right'](transpose(field))
200 
201         if direction in check:
202             return check[direction](self.field)
203         else:
204             return False
205 
206 def main(stdscr):
207     def init():
208         game_field.reset()
209         return 'Game'
210 
211     def not_game(state):
212         game_field.draw(stdscr)
213         action = get_user_action(stdscr)
214         responses = defaultdict(lambda: state)
215         responses['Restart'], responses['Exit'] = 'Init', 'Exit' 
216         return responses[action]
217 
218     def game():
219         game_field.draw(stdscr)
220         action = get_user_action(stdscr)
221 
222         if action == 'Restart':
223             return 'Init'
224         if action == 'Exit':
225             return 'Exit'
226         if game_field.move(action): 
227             if game_field.is_win():
228                 return 'Win'
229             if game_field.is_gameover():
230                 return 'Gameover'
231         return 'Game'
232 
233 
234     state_actions = {
235             'Init': init,
236             'Win': lambda: not_game('Win'),
237             'Gameover': lambda: not_game('Gameover'),
238             'Game': game
239         }
240 
241     curses.use_default_colors()
242     game_field = GameField(win=32)
243 
244     state = 'Init'
245 
246     while state != 'Exit':
247         state = state_actions[state]()
248 
249 curses.wrapper(main)
原文地址:https://www.cnblogs.com/danjawwi/p/6133910.html