我初学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)