Home >Backend Development >Python Tutorial >Teach you step by step to implement the 2048 mini game in Python
I believe that the game 2048 must be familiar to everyone. The following article mainly introduces how to use Python to implement the 2048 mini game. The article introduces it in detail through comments and sample codes. I believe it will help everyone understand and Learning has certain reference value. Friends in need should take a look.
Preface
2048 Game Rules: Simply move the direction keys to superimpose the numbers, and get the score after each superposition of these numbers. The game is won when the number 2048 appears. At the same time, every time you move the direction keys, a number 2 or 4 will be randomly generated in the blank area of the 4*4 square matrix. If the square is filled with numbers, then it is GameOver.
Main logic diagram
##Logic diagram illustration: Black is Logic layer, blue is external method, red is intra-class method, you will know later~
main() function, and intersect externally defined functions and classes in it.
Interpretation of the main logic code (see the end of the article for the complete code)
The main logic main is as follows, and the following is the explanation of the main function Interpretation of some methods: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]()Interpretation one by one (the code box will be marked as coming from the outside, if not marked, it is coming from the inside): Define the main function
def main(stdscr):
def init(): #重置游戏棋盘 game_field.reset()reset comes from an externally defined class, one of
game_field=GameField Method 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() #其中highscore为程序初始化过程中定义的一个变量。记录你win游戏的最高分数记录。
return 'Game'Return to a game in progress in the state.
game_field=GameFieldThe status is defined later:
state_actions = { 'Init': init, 'Win': lambda: not_game('Win'), 'Gameover': lambda: not_game('Gameover'), 'Game': game }
def not_game(state): #画出 GameOver 或者 Win 的界面 game_field.draw(stdscr)draw is a method in the imported class
game_field=GameField:
#来自外部类 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的一些高级概念,我就不做多的介绍了。Here I won’t explain too much about the word functions of the draw method, they are very simple concepts.
#读取用户输入得到action,判断是重启游戏还是结束游戏 action = get_user_action(stdscr)Read user behavior, the function comes from the initial definition of the code
#来自外部定义的函数 def get_user_action(keyboard): char = "N" while char not in actions_dict: char = keyboard.getch() return actions_dict[char]At the end, which is the third step of the main function execution, the instance
state = state_actions[state]() is defined:
#主函数底部: state = 'Init' #状态机开始循环 while state != 'Exit': state = state_actions[state]()
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' #game()函数的定义类似于上面已经讲过的not_game(),只是game()有了内部循环 #即如果不是Restart/Exit或者对move之后的状态进行判断,如果不是结束游戏,就一直在game()内部循环。##game()
The definition of the function is similar to what has been said abovenot_game()
, but game()
has an internal loop, that is, if it is not Restart/Exit or judging the state after move, if it is not the end of the game, it will always be game()
Inner loop.
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()
What this means is:
state=state_actions[state]Can be seen as: state=init()
or state=not_game('Win')
or another not_game('Gameover')/game()
Without further ado, let’s go ahead A picture of my success. In addition, you can determine your final winning conditions by setting win=32 in the last few lines!
#-*- 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)More