zoukankan      html  css  js  c++  java
  • [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)
  • 相关阅读:
    MongoDB Schema Design
    WinDBG中的poi是做什么用的?
    如何在Visual Studio中运行和调试汇编代码?
    [翻译图书] 未完工 Moving Applications to the Cloud on the Microsoft Windows Azure Platform 4
    在Word中生成随机的样本文本
    Quiz Win32内存表示与数值大小
    rep stos dword ptr es:[edi] 是做什么的?
    Windows Azure中虚拟机无法启动, 报错RoleStateUnknown的解决方案
    COM基础介绍
    64位的dump里如何寻找第一个到第四个参数?
  • 原文地址:https://www.cnblogs.com/danjawwi/p/6133910.html
Copyright © 2011-2022 走看看