前言
2048游戲規則:簡單的移動方向鍵讓數字疊加,并且獲得這些數字每次疊加后的得分,當出現2048這個數字時游戲勝利。同時每次移動方向鍵時,都會在這個4*4的方格矩陣的空白區域隨機產生一個數字2或者4,如果方格被數字填滿了,那么就GameOver了。
主邏輯圖
邏輯圖解:黑色是邏輯層,藍色是外部方法,紅色是類內方法,稍后即可知道~
下面容我逐行解釋主邏輯main()
函數,并且在其中穿叉外部定義的函數與類。
主邏輯代碼解讀(完整代碼見文末)
主邏輯main如下,之后的是對主函數中的一些方法的解讀:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
def main(stdscr): def init(): #重置游戲棋盤 game_field.reset() return 'Game' def not_game(state): #畫出 GameOver 或者 Win 的界面 game_field.draw(stdscr) #讀取用戶輸入得到action,判斷是重啟游戲還是結束游戲 action = get_user_action(stdscr) responses = defaultdict( lambda : state) #默認是當前狀態,沒有行為就會一直在當前界面循環 responses[ 'Restart' ], responses[ 'Exit' ] = 'Init' , 'Exit' #對應不同的行為轉換到不同的狀態 return responses[action] def game(): #畫出當前棋盤狀態 game_field.draw(stdscr) #讀取用戶輸入得到action action = get_user_action(stdscr) if action = = 'Restart' : return 'Init' if action = = 'Exit' : return 'Exit' if game_field.move(action): # move successful if game_field.is_win(): return 'Win' if game_field.is_gameover(): return 'Gameover' return 'Game' state_actions = { 'Init' : init, 'Win' : lambda : not_game( 'Win' ), 'Gameover' : lambda : not_game( 'Gameover' ), 'Game' : game } curses.use_default_colors() game_field = GameField(win = 32 ) state = 'Init' #狀態機開始循環 while state ! = 'Exit' : state = state_actions[state]() |
逐條解讀(代碼框內會標注是來自外部,無標注則是來自內部):定義主函數
1
|
def main(stdscr): |
1
2
3
|
def init(): #重置游戲棋盤 game_field.reset() |
reset出自外部定義的類,game_field=GameField
的一個方法reset:
外部:
1
2
3
4
5
6
7
8
|
def reset( self ): if self .score > self .highscore: self .highscore = self .score self .score = 0 self .field = [[ 0 for i in range ( self .width)] for j in range ( self .height)] self .spawn() self .spawn() #其中highscore為程序初始化過程中定義的一個變量。記錄你win游戲的最高分數記錄。 |
1
|
return 'Game' |
返回一個游戲進行中的狀態。game_field=GameField
狀態在后面有定義:
主函數底部定義:
1
2
3
4
5
6
|
state_actions = { 'Init' : init, 'Win' : lambda : not_game( 'Win' ), 'Gameover' : lambda : not_game( 'Gameover' ), 'Game' : game } |
1
2
3
|
def not_game(state): #畫出 GameOver 或者 Win 的界面 game_field.draw(stdscr) |
draw是導入的類game_field=GameField
中的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
#來自外部類 def draw(self, screen): help_string1 = '(W)Up (S)Down (A)Left (D)Right' help_string2 = ' (R)Restart (Q)Exit' gameover_string = ' GAME OVER' win_string = ' YOU WIN!' #定義各個字符串 def cast(string): screen.addstr(string + '\n' ) def draw_hor_separator(): line = '+' + ( '+------' * self.width + '+' )[ 1 :] separator = defaultdict(lambda: line) if not hasattr(draw_hor_separator, "counter" ): draw_hor_separator.counter = 0 cast(separator[draw_hor_separator.counter]) draw_hor_separator.counter += 1 def draw_row(row): cast( '' .join( '|{: ^5} ' .format(num) if num > 0 else '| ' for num in row) + '|' ) screen.clear() cast( 'SCORE: ' + str(self.score)) if 0 != self.highscore: cast( 'HGHSCORE: ' + str(self.highscore)) for row in self.field: draw_hor_separator() draw_row(row) draw_hor_separator() if self.is_win(): cast(win_string) else : if self.is_gameover(): cast(gameover_string) else : cast(help_string1) cast(help_string2) #這里面的draw方法的字函數我就不做多的解釋了,很簡單的一些概念。 #但是又運用到了很優秀的精簡代碼。 #有的地方建議去查一下python的一些高級概念,我就不做多的介紹了。 |
這里面的draw方法的字函數我就不做多的解釋了,很簡單的一些概念。
但是又運用到了很優秀的精簡代碼。
有的地方建議去查一下python的一些高級概念,我就不做多的介紹了。
1
2
|
#讀取用戶輸入得到action,判斷是重啟游戲還是結束游戲 action = get_user_action(stdscr) |
讀取用戶行為,函數來自于代碼初始的定義
1
2
3
4
5
6
|
#來自外部定義的函數 def get_user_action(keyboard): char = "N" while char not in actions_dict: char = keyboard.getch() return actions_dict[char] |
在結尾處,也即是主函數執行的第三步,定義了state = state_actions[state]()
這一實例:
1
2
3
4
5
6
|
#主函數底部: state = 'Init' #狀態機開始循環 while state ! = 'Exit' : state = state_actions[state]() |
1
2
3
|
responses = defaultdict( lambda : state) #默認是當前狀態,沒有行為就會一直在當前界面循環 responses[ 'Restart' ], responses[ 'Exit' ] = 'Init' , 'Exit' #對應不同的行為轉換到不同的狀態 return responses[action] |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
def game(): #畫出當前棋盤狀態 game_field.draw(stdscr) #讀取用戶輸入得到action action = get_user_action(stdscr) if action = = 'Restart' : return 'Init' if action = = 'Exit' : return 'Exit' if game_field.move(action): # move successful if game_field.is_win(): return 'Win' if game_field.is_gameover(): return 'Gameover' return 'Game' #game()函數的定義類似于上面已經講過的not_game(),只是game()有了內部循環 #即如果不是Restart/Exit或者對move之后的狀態進行判斷,如果不是結束游戲,就一直在game()內部循環。 |
game()
函數的定義類似于上面已經講過的not_game()
,只是game()
有了內部循環,即如果不是Restart/Exit或者對move之后的狀態進行判斷,如果不是結束游戲,就一直在game()
內部循環。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
state_actions = { 'Init' : init, 'Win' : lambda : not_game( 'Win' ), 'Gameover' : lambda : not_game( 'Gameover' ), 'Game' : game } curses.use_default_colors() game_field = GameField(win = 32 ) state = 'Init' #狀態機開始循環 while state ! = 'Exit' : state = state_actions[state]() #此處的意思是:state=state_actions[state] 可以看做是: #state=init()或者state=not_game(‘Win')或者是另外的not_game(‘Gameover')/game() |
此處的意思是:state=state_actions[state]
可以看做是:state=init()
或者state=not_game(‘Win')
或者是另外的not_game(‘Gameover')/game()
廢話不多說,上一個我的成功的圖,另外,可以通過設置最后幾行中的win=32來決定你最終獲勝的條件!
完整代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
#-*- coding:utf-8 -*- import curses from random import randrange, choice # generate and place new tile from collections import defaultdict letter_codes = [ ord (ch) for ch in 'WASDRQwasdrq' ] actions = [ 'Up' , 'Left' , 'Down' , 'Right' , 'Restart' , 'Exit' ] actions_dict = dict ( zip (letter_codes, actions * 2 )) def transpose(field): return [ list (row) for row in zip ( * field)] def invert(field): return [row[:: - 1 ] for row in field] class GameField( object ): def __init__( self , height = 4 , width = 4 , win = 2048 ): self .height = height self .width = width self .win_value = win self .score = 0 self .highscore = 0 self .reset() def reset( self ): if self .score > self .highscore: self .highscore = self .score self .score = 0 self .field = [[ 0 for i in range ( self .width)] for j in range ( self .height)] self .spawn() self .spawn() def move( self , direction): def move_row_left(row): def tighten(row): # squeese non-zero elements together new_row = [i for i in row if i ! = 0 ] new_row + = [ 0 for i in range ( len (row) - len (new_row))] return new_row def merge(row): pair = False new_row = [] for i in range ( len (row)): if pair: new_row.append( 2 * row[i]) self .score + = 2 * row[i] pair = False else : if i + 1 < len (row) and row[i] = = row[i + 1 ]: pair = True new_row.append( 0 ) else : new_row.append(row[i]) assert len (new_row) = = len (row) return new_row return tighten(merge(tighten(row))) moves = {} moves[ 'Left' ] = lambda field: \ [move_row_left(row) for row in field] moves[ 'Right' ] = lambda field: \ invert(moves[ 'Left' ](invert(field))) moves[ 'Up' ] = lambda field: \ transpose(moves[ 'Left' ](transpose(field))) moves[ 'Down' ] = lambda field: \ transpose(moves[ 'Right' ](transpose(field))) if direction in moves: if self .move_is_possible(direction): self .field = moves[direction]( self .field) self .spawn() return True else : return False def is_win( self ): return any ( any (i > = self .win_value for i in row) for row in self .field) def is_gameover( self ): return not any ( self .move_is_possible(move) for move in actions) def draw( self , screen): help_string1 = '(W)Up (S)Down (A)Left (D)Right' help_string2 = ' (R)Restart (Q)Exit' gameover_string = ' GAME OVER' win_string = ' YOU WIN!' def cast(string): screen.addstr(string + '\n' ) def draw_hor_separator(): line = '+' + ( '+------' * self .width + '+' )[ 1 :] separator = defaultdict( lambda : line) if not hasattr (draw_hor_separator, "counter" ): draw_hor_separator.counter = 0 cast(separator[draw_hor_separator.counter]) draw_hor_separator.counter + = 1 def draw_row(row): cast(' '.join(' |{: ^ 5 } '.format(num) if num > 0 else ' | ' for num in row) + ' |') screen.clear() cast( 'SCORE: ' + str ( self .score)) if 0 ! = self .highscore: cast( 'HGHSCORE: ' + str ( self .highscore)) for row in self .field: draw_hor_separator() draw_row(row) draw_hor_separator() if self .is_win(): cast(win_string) else : if self .is_gameover(): cast(gameover_string) else : cast(help_string1) cast(help_string2) def spawn( self ): new_element = 4 if randrange( 100 ) > 89 else 2 (i,j) = choice([(i,j) for i in range ( self .width) for j in range ( self .height) if self .field[i][j] = = 0 ]) self .field[i][j] = new_element def move_is_possible( self , direction): def row_is_left_movable(row): def change(i): # true if there'll be change in i-th tile if row[i] = = 0 and row[i + 1 ] ! = 0 : # Move return True if row[i] ! = 0 and row[i + 1 ] = = row[i]: # Merge return True return False return any (change(i) for i in range ( len (row) - 1 )) check = {} check[ 'Left' ] = lambda field: \ any (row_is_left_movable(row) for row in field) check[ 'Right' ] = lambda field: \ check[ 'Left' ](invert(field)) check[ 'Up' ] = lambda field: \ check[ 'Left' ](transpose(field)) check[ 'Down' ] = lambda field: \ check[ 'Right' ](transpose(field)) if direction in check: return check[direction]( self .field) else : return False def main(stdscr): def init(): #重置游戲棋盤 game_field.reset() return 'Game' def not_game(state): #畫出 GameOver 或者 Win 的界面 game_field.draw(stdscr) #讀取用戶輸入得到action,判斷是重啟游戲還是結束游戲 action = get_user_action(stdscr) responses = defaultdict( lambda : state) #默認是當前狀態,沒有行為就會一直在當前界面循環 responses[ 'Restart' ], responses[ 'Exit' ] = 'Init' , 'Exit' #對應不同的行為轉換到不同的狀態 return responses[action] def game(): #畫出當前棋盤狀態 game_field.draw(stdscr) #讀取用戶輸入得到action action = get_user_action(stdscr) if action = = 'Restart' : return 'Init' if action = = 'Exit' : return 'Exit' if game_field.move(action): # move successful if game_field.is_win(): return 'Win' if game_field.is_gameover(): return 'Gameover' return 'Game' state_actions = { 'Init' : init, 'Win' : lambda : not_game( 'Win' ), 'Gameover' : lambda : not_game( 'Gameover' ), 'Game' : game } curses.use_default_colors() game_field = GameField(win = 32 ) state = 'Init' #狀態機開始循環 while state ! = 'Exit' : state = state_actions[state]() curses.wrapper(main) |
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。
原文鏈接:http://www.jianshu.com/p/7a3a7545d2fb