200 lines of python code for 2048 games, python2048
Create a game file 2048.py
First, import the required package:
import cursesfrom random import randrange, choicefrom collections import defaultdict
Main Logic
User behavior
All valid inputs can be converted to "Up, down, left, right, game reset, and exit", expressed in actions
actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit']
Valid input keys are the most common W (top), A (left), S (bottom), D (right), R (reset), Q (exit ), take into account the Enable of the upper-case key to obtain the valid key value list:
letter_codes = [ord(ch) for ch in 'WASDRQwasdrq']
Associate input with behavior:
actionsdict = dict(zip(lettercodes, actions * 2))
State Machine
When dealing with the game's main logic, we will use a very common technology: the state machine, or, more accurately, the finite state machine (FSM)
You will find that 2048 games can easily be divided into several States for conversion.
State stores the current state. The state_actions dictionary variable serves as the state conversion rule. Its key is the state, and value is the function that returns the next state:
Init: init()Game: game()Win: lambda: not_game('Win')Gameover: lambda: not_game('Gameover')
Exit: Exit the loop
Status opportunities keep repeating until the Exit termination state ends.
The following is the extracted main logic code, which will be supplemented later:
Def main (stdscr): def init (): # reset the Game board return 'game' def not_game (state): # Draw the GameOver or Win interface # Read the user input to get the action, determine whether to Restart the game or stop the game responses = defaultdict (lambda: state) # The default status is the current state. If no action is performed, the responses ['restart'] will be cyclically displayed on the current interface. responses ['delete'] = 'init', 'delete' # convert different behaviors to different States return responses [action] def game (): # Draw the current checkerboard status # Read the user input to get the action if action = 'restart': return 'init 'if action = 'delete': return 'delete' # If successfully moved a step: if the Game wins: return 'win' if the Game fails: return 'gameover' return 'game' state_actions = {'init ': Init, 'win': lambda: not_game ('win'), 'gameover': lambda: not_game ('gameover'), 'game ': game} state = 'init' # The state machine starts loop while state! = 'Exit ': state = state_actions [state] ()
User input processing
Blocking + loop: The corresponding behavior is returned only when valid user input is obtained:
def get_user_action(keyboard): char = "N" while char not in actions_dict: char = keyboard.getch() return actions_dict[char]
Matrix transpose and matrix reversal
By adding these two operations, we can greatly save the amount of code and reduce repetitive work. We can see it later.
Matrix transpose:
def transpose(field): return [list(row) for row in zip(*field)]
Matrix reversal (not Inverse Matrix ):
def invert(field): return [row[::-1] for row in field]
Create a chessboard
Initialize the parameters of the board. You can specify the height and width of the board and the game victory conditions. The default value is the most classic 4 × 4 ~ 2048.
Class GameField (object): def _ init _ (self, height = 4, width = 4, win = 2048): self. height = height # High self. width = width # width self. win_value = 2048 # Pass score self. score = 0 # current score self. highscore = 0 # highest score self. reset () # board reset
Board Operations
Randomly generate a 2 or 4
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 #### reset the checkboard 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 ()### # Merge a row to the left (note: this operation is defined in move and removed for ease of reading) def move_row_left (row): def tighten (row ): # squeeze scattered non-zero units into a piece of 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 ): # merge Adjacent Elements 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 # First squeeze into one and then merge and then squeeze into one return tighten (merge (tighten (row )))
Step by step
By transposing and reversing the matrix, you can directly move the matrix from the left to get the other three directions.
Def move (self, direction): def move_row_left (row): # merge a row to the Left 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 ['low'] = 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
Determine whether to win or lose
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) #### determine whether def move_is_possible (self, direction): defrow_is_left_movable (row): def change (I ): if row [I] = 0 and row [I + 1]! = 0: # return True if row [I] can be moved. = 0 and row [I + 1] = row [I]: # return True return False return any (change (I) for I in range (len (row) can be merged) -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 direction in check: return check [direction] (self. field) else: return False
Draw game interface
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') # Draw horizontal split line 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)
Complete main logic
After completing the above work, we can complete the main logic!
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] ()
Run
Fill in the last line of code:
curses.wrapper(main)
Full Code address: https://github.com/JLUNeverMore/easy_2048-in-200-lines
Summary
The above section describes how to implement the 200-line python code 2048 game. I hope it will help you. If you have any questions, please leave a message and I will reply to you in a timely manner. Thank you very much for your support for the help House website!