Step by step teach you how to use Python to implement 2048 games, python2048
Preface
2048 game rules: a simple move arrow key allows a number to overlay and obtain the score after each superposition of these numbers. When the number 2048 appears, the game wins. At the same time, a number 2 or 4 will be randomly generated in the blank area of the 4*4 square matrix each time you move the direction key. If the square is filled with numbers, then GameOver will be generated.
Main logic diagram
Logical diagram: Black is the logical layer, blue is the external method, and red is the class method. You can see it later ~
Next face I explain the main logic line by linemain()
And use the functions and classes defined externally.
Explanation of the main logic code (for the complete code, see the end of this article)
The main logic is as follows, followed by some methods in the main function:
Def main (stdscr): def init (): # reset the Game board game_field.reset () return 'game' def not_game (state): # Draw the GameOver or Win interface game_field.draw (stdscr) # Read the user input to get the action and determine whether to restart the game or stop the game action = get_user_action (stdscr) responses = defaultdict (lambda: state) # The default status is the current status, if there is no behavior, the current interface will loop responses ['restart'], responses ['exit '] = 'init ', 'Exit '# convert the corresponding behavior to different States return responses [action] def game (): # Draw the current checker status game_field.dr Aw (stdscr) # Read user input to get action = get_user_action (stdscr) if action = 'restart': return 'init 'if action = 'eg ': 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} cu Rses. use_default_colors () game_field = GameField (win = 32) state = 'init' # The state machine starts loop while state! = 'Exit ': state = state_actions [state] ()
One by one (the code box is marked as external, but not internal): defines the main function.
def main(stdscr):
Def init (): # reset the game board game_field.reset ()
Reset is a class defined by external users,game_field=GameField
Reset:
External:
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 is a variable defined during program initialization. Record the highest score of your win game.
return 'Game'
Returns the status of a game in progress.game_field=GameField
The status is defined later:
Bottom definition of the main function:
state_actions = { 'Init': init, 'Win': lambda: not_game('Win'), 'Gameover': lambda: not_game('Gameover'), 'Game': game }
Def not_game (state): # Draw the GameOver or Win interface game_field.draw (stdscr)
Draw is the imported class.game_field=GameField
Method in:
# From external class 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! '# Define each string 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 [Operator]) draw_hor_separator.counter + = 1 def draw_row (row): cast (''. join ('| {: ^ 5 }'. format (num) if num> 0 else '| 'fo R 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) # I will not explain more about the word functions of the draw method. It is a very simple concept. # However, it applies excellent streamlined code. # In some cases, it is recommended that you check some advanced concepts of python.
I will not explain more about the word functions of the draw method. It is a very simple concept.
However, it also applies excellent streamlined code.
In some cases, it is recommended that you check some advanced concepts of python.
# Read the user input to get the action to determine whether to restart the game or stop the game 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, that is, the third step of the main function execution.state = state_actions[state]()
This example:
# Main Function bottom: state = 'init' # state machine starts loop while state! = 'Exit ': state = state_actions [state] ()
Responses = defaultdict (lambda: state) # The default value is the current state. If there is no behavior, the responses ['restart'] will be cyclically in the current interface. responses ['delete'] = 'init', 'delete' # convert different behaviors to different States. return responses [action]
Def game (): # Draw the current checker status game_field.draw (stdscr) # Read the user input to get 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' # the definition of the Game () function is similar to the not_game () function mentioned above, but it is only game () internal Loop # if it is not Restart/Exit or the status after move is determined, if it is not the end of the game It has been circulating in game.
game()
The function definition is similar to the one mentioned above.not_game()
, Justgame()
With the internal loop, if it is not Restart/Exit or the status after move is determined, if it is not the end of the gamegame()
Internal 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' # The state machine starts loop while state! = 'Exit ': state = state_actions [state] () # state = state_actions [state] can be viewed as: # state = init () or state = not_game ('win') or another not_game ('gameover')/game ()
The meaning here is:state=state_actions[state]
It can be seen:state=init()
Orstate=not_game(‘Win')
Or anothernot_game(‘Gameover')/game()
Let's not talk about it. In the previous figure of my success, you can set win = 32 in the last few rows to determine your final victory condition!
Complete code
#-*-Coding: UTF-8-*-import cursesfrom random import randrange, choice # generate and place new tilefrom collections import defadicdictletter_codes = [ord (ch) for ch in 'wasdrqwasdrqq'] 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 ro W 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 (tight En (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 ['drop'] = 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 (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 [Operator]) 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. h Eight) 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 ['low'] = lambda field: \ check ['right'] (transpose (field) if direc Tion in check: return check [direction] (self. field) else: return Falsedef main (stdscr): def init (): # reset the Game board game_field.reset () return 'game' def not_game (state ): # Draw the GameOver or Win interface game_field.draw (stdscr) # Read the user input to get the action to determine whether to restart the game or stop the game action = get_user_action (stdscr) responses = defaultdict (lambda: state) # The default status is the current status. If no behavior is found, the system will loop through the current interface. responses ['restart'], responses ['eg'] = 'init ', 'Exit '# corresponding to different rows Return responses [action] def game (): # Draw the current checker status game_field.draw (stdscr) # Read user input to get action = get_user_action (stdscr) if action = 'restart': return 'init 'if action = 'delete': return 'delete' 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_ga Me ('win'), 'gameover': lambda: not_game ('gameover'), 'game': Game} curses. use_default_colors () game_field = GameField (win = 32) state = 'init' # The state machine starts loop while state! = 'Exit ': state = state_actions [state] () curses. wrapper (main)
Summary
The above is all about this article. I hope this article will help you in your study or work. If you have any questions, please leave a message.