python之2048

  1 #-*- coding:utf-8 -*-
  2 
  3 import curses
  4 from random import randrange, choice # generate and place new tile
  5 from collections import defaultdict
  6 
  7 letter_codes = [ord(ch) for ch in 'WASDRQwasdrq']
  8 actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit']
  9 actions_dict = dict(zip(letter_codes, actions * 2))
 10 
 11 def get_user_action(keyboard):    
 12     char = "N"
 13     while char not in actions_dict:    
 14         char = keyboard.getch()
 15     return actions_dict[char]
 16 
 17 def transpose(field):
 18     return [list(row) for row in zip(*field)]
 19 
 20 def invert(field):
 21     return [row[::-1] for row in field]
 22 
 23 class GameField(object):
 24     def __init__(self, height=4, width=4, win=2048):
 25         self.height = height
 26         self.width = width
 27         self.win_value = 2048
 28         self.score = 0
 29         self.highscore = 0
 30         self.reset()
 31 
 32     def reset(self):
 33         if self.score > self.highscore:
 34             self.highscore = self.score
 35         self.score = 0
 36         self.field = [[0 for i in range(self.width)] for j in range(self.height)]
 37         self.spawn()
 38         self.spawn()
 39 
 40     def move(self, direction):
 41         def move_row_left(row):
 42             def tighten(row): # squeese non-zero elements together
 43                 new_row = [i for i in row if i != 0]
 44                 new_row += [0 for i in range(len(row) - len(new_row))]
 45                 return new_row
 46 
 47             def merge(row):
 48                 pair = False
 49                 new_row = []
 50                 for i in range(len(row)):
 51                     if pair:
 52                         new_row.append(2 * row[i])
 53                         self.score += 2 * row[i]
 54                         pair = False
 55                     else:
 56                         if i + 1 < len(row) and row[i] == row[i + 1]:
 57                             pair = True
 58                             new_row.append(0)
 59                         else:
 60                             new_row.append(row[i])
 61                 assert len(new_row) == len(row)
 62                 return new_row
 63             return tighten(merge(tighten(row)))
 64 
 65         moves = {}
 66         moves['Left']  = lambda field:                              \
 67                 [move_row_left(row) for row in field]
 68         moves['Right'] = lambda field:                              \
 69                 invert(moves['Left'](invert(field)))
 70         moves['Up']    = lambda field:                              \
 71                 transpose(moves['Left'](transpose(field)))
 72         moves['Down']  = lambda field:                              \
 73                 transpose(moves['Right'](transpose(field)))
 74 
 75         if direction in moves:
 76             if self.move_is_possible(direction):
 77                 self.field = moves[direction](self.field)
 78                 self.spawn()
 79                 return True
 80             else:
 81                 return False
 82 
 83     def is_win(self):
 84         return any(any(i >= self.win_value for i in row) for row in self.field)
 85 
 86     def is_gameover(self):
 87         return not any(self.move_is_possible(move) for move in actions)
 88 
 89     def draw(self, screen):
 90         help_string1 = '(W)Up (S)Down (A)Left (D)Right'
 91         help_string2 = '     (R)Restart (Q)Exit'
 92         gameover_string = '           GAME OVER'
 93         win_string = '          YOU WIN!'
 94         def cast(string):
 95             screen.addstr(string + '\n')
 96 
 97         def draw_hor_separator():
 98             line = '+' + ('+------' * self.width + '+')[1:]
 99             separator = defaultdict(lambda: line)
100             if not hasattr(draw_hor_separator, "counter"):
101                 draw_hor_separator.counter = 0
102             cast(separator[draw_hor_separator.counter])
103             draw_hor_separator.counter += 1
104 
105         def draw_row(row):
106             cast(''.join('|{: ^5} '.format(num) if num > 0 else '|      ' for num in row) + '|')
107 
108         screen.clear()
109         cast('SCORE: ' + str(self.score))
110         if 0 != self.highscore:
111             cast('HGHSCORE: ' + str(self.highscore))
112         for row in self.field:
113             draw_hor_separator()
114             draw_row(row)
115         draw_hor_separator()
116         if self.is_win():
117             cast(win_string)
118         else:
119             if self.is_gameover():
120                 cast(gameover_string)
121             else:
122                 cast(help_string1)
123         cast(help_string2)
124 
125     def spawn(self):
126         new_element = 4 if randrange(100) > 89 else 2
127         (i,j) = choice([(i,j) for i in range(self.width) for j in range(self.height) if self.field[i][j] == 0])
128         self.field[i][j] = new_element
129 
130     def move_is_possible(self, direction):
131         def row_is_left_movable(row): 
132             def change(i): # true if there'll be change in i-th tile
133                 if row[i] == 0 and row[i + 1] != 0: # Move
134                     return True
135                 if row[i] != 0 and row[i + 1] == row[i]: # Merge
136                     return True
137                 return False
138             return any(change(i) for i in range(len(row) - 1))
139 
140         check = {}
141         check['Left']  = lambda field:                              \
142                 any(row_is_left_movable(row) for row in field)
143 
144         check['Right'] = lambda field:                              \
145                  check['Left'](invert(field))
146 
147         check['Up']    = lambda field:                              \
148                 check['Left'](transpose(field))
149 
150         check['Down']  = lambda field:                              \
151                 check['Right'](transpose(field))
152 
153         if direction in check:
154             return check[direction](self.field)
155         else:
156             return False
157 
158 def main(stdscr):
159     def init():
160         #重置游戏棋盘
161         game_field.reset()
162         return 'Game'
163 
164     def not_game(state):
165         #画出 GameOver 或者 Win 的界面
166         game_field.draw(stdscr)
167         #读取用户输入得到action,判断是重启游戏还是结束游戏
168         action = get_user_action(stdscr)
169         responses = defaultdict(lambda: state) #默认是当前状态,没有行为就会一直在当前界面循环
170         responses['Restart'], responses['Exit'] = 'Init', 'Exit' #对应不同的行为转换到不同的状态
171         return responses[action]
172 
173     def game():
174         #画出当前棋盘状态
175         game_field.draw(stdscr)
176         #读取用户输入得到action
177         action = get_user_action(stdscr)
178 
179         if action == 'Restart':
180             return 'Init'
181         if action == 'Exit':
182             return 'Exit'
183         if game_field.move(action): # move successful
184             if game_field.is_win():
185                 return 'Win'
186             if game_field.is_gameover():
187                 return 'Gameover'
188         return 'Game'
189 
190 
191     state_actions = {
192             'Init': init,
193             'Win': lambda: not_game('Win'),
194             'Gameover': lambda: not_game('Gameover'),
195             'Game': game
196         }
197 
198     curses.use_default_colors()
199     game_field = GameField(win=32)
200 
201 
202     state = 'Init'
203 
204     #状态机开始循环
205     while state != 'Exit':
206         state = state_actions[state]()
207 
208 curses.wrapper(main)

python之2048_第1张图片

你可能感兴趣的:(python之2048)