Life is short, I use python
This time I bring you a super super super super!
Cool little thing!
It’s okay if you don’t want to move by yourself~
There are more good things~
All here?
Python installation package + information: click here to jump to the business card at the end of the article
Realize the effect
This is really a good thing to catch fish hahahaha
Code
If you haven’t installed the software, install the software first.
Install the pygame module without installing the module
pip install pygame
Import Module
import pygame,sys,time,random from pygame.locals import *
Define color variables
redColour = pygame. Color(255,0,0) blackColour = pygame. Color(0,0,0) whiteColour = pygame. Color(255,255,255) greenColour = pygame. Color(0,255,0) headColour = pygame. Color(0,119,255)
In all subsequent divisions,
In order to prevent deviations in pygame output,
Must take divisor (//) instead of simple division (/)
Program Interface
Line 0, HEIGHT line,
Column 0, WIDTH is listed as the fence,
So the actual size is 13*13
IGHT = 15 WIDTH = 15 FIELD_SIZE = HEIGHT * WIDTH # The snake head is located in the first element of the snake array python learning exchange button qun:903971231 HEAD = 0
Use numbers to represent different objects,
Because each grid on the matrix will be processed into the length of the path to the food during the movement,
Therefore, there needs to be a large enough interval (>HEIGHT*WIDTH) between these three variables to distinguish each other.
Lowercase is generally coordinates, and uppercase represents constants.
FOOD = 0 UNDEFINED = (HEIGHT + 1) * (WIDTH + 1) SNAKE = 2 * UNDEFINED
FOOD = 0 UNDEFINED = (HEIGHT + 1) * (WIDTH + 1) SNAKE = 2 * UNDEFINED
snake is a one-dimensional array,
Directly adding the following values to the corresponding elements means moving in four directions.
LEFT = -1 RIGHT = 1 UP = -WIDTH # One-dimensional array, so the entire width needs to be added to represent up and down movement. DOWN = WIDTH
Error code
ERR = -2333
Use a one-dimensional array to represent two-dimensional things,
board represents the rectangular field of snake movement,
Initialize the snake head at (1,1),
The initial snake length is 1.
board = [0] * FIELD_SIZE #[0,0,0,…] snake = [0] * (FIELD_SIZE + 1) snake[HEAD] = 1*WIDTH + 1 snake_size = 1
A temporary variable corresponding to the above variable,
Used when the snake moves tentatively.
tmpboard = [0] * FIELD_SIZE tmpsnake = [0] * (FIELD_SIZE + 1) tmpsnake[HEAD] = 1*WIDTH + 1 tmpsnake_size = 1
food: The food position is initially at (4, 7),
best_move: direction of movement.
food = 4 * WIDTH + 7 best_move = ERR
array of motion directions,
Game Score (Snake Length)
mov = [LEFT, RIGHT, UP, DOWN] score = 1
Check if a cell is covered by snake body,
If there is no coverage, it is free and returns true.
def is_cell_free(idx, psize, psnake): return not (idx in psnake[:psize])
Check whether a position idx can move in the direction of move
def is_move_possible(idx, move): flag = False if move == LEFT: #Because the actual range is 13*13,[1,13]*[1,13], so when idx is 1, you cannot run to the left, and the remainder is 1 at this time, so >1 flag = True if idx%WIDTH > 1 else False elif move == RIGHT: #The <WIDTH-2 here is the same as above flag = True if idx%WIDTH < (WIDTH-2) else False elif move == UP: #The upward judgment drawing here is easy to understand, because outside the actual range of motion of [1,13]*[1,13], there is another #The big frame is the wall, which is the ranks mentioned before, and the conditions for judging the downward movement are similar flag = True if idx > (2*WIDTH-1) else False elif move == DOWN: flag = True if idx < (FIELD_SIZE-2*WIDTH) else False return flag
reset board After board_BFS, the UNDEFINED value becomes the path length to reach the food. To restore it, reset it.
def board_reset(psnake, psize, pboard): for i in range(FIELD_SIZE): if i == food: pboard[i] = FOOD elif is_cell_free(i, psize, psnake): # The position is empty pboard[i] = UNDEFINED else: # The position is snake body pboard[i] = SNAKE
Breadth-first search traverses the entire board,
Calculate the path length of each non-SNAKE element in the board to reach the food.
def board_BFS(pfood, psnake, pboard): queue = [] queue. append(pfood) inqueue = [0] * FIELD_SIZE found = False # After the while loop ends, except for the body of the snake, # The number in each other square is the Manhattan distance from it to the food while len(queue)!=0: idx = queue.pop(0)# Initially idx is the coordinates of the food if inqueue[idx] == 1: continue inqueue[idx] = 1 for i in range(4):# left and right up and down if is_move_possible(idx, mov[i]): if idx + mov[i] == psnake[HEAD]: found = True if pboard[idx + mov[i]] < SNAKE: # If the point is not the body of the snake if pboard[idx + mov[i]] > pboard[idx] + 1: #Don't care if it is less than, otherwise the existing path data will be overwritten. pboard[idx + mov[i]] = pboard[idx] + 1 if inqueue[idx + mov[i]] == 0: queue.append(idx + mov[i]) return found
From the head of the snake,
According to the element value in the board,
Select the shortest path from the 4 domain points around the snake head.
def choose_shortest_safe_move(psnake, pboard): best_move = ERR min = SNAKE for i in range(4): if is_move_possible(psnake[HEAD], mov[i]) and pboard[psnake[HEAD] + mov[i]]<min: #The minimum judgment here and the maximum judgment of the function below are all assigned first, and then compared with each other in a loop min = pboard[psnake[HEAD] + mov[i]] best_move = mov[i] return best_move
Check if you can chase the tail of the snake,
That is, there is a path between the snake head and the snake tail,
In order to prevent the snakehead from falling into a dead end.
Virtual operation is carried out in tmpboard, tmpsnake.
def is_tail_inside(): global tmpboard, tmpsnake, food, tmpsnake_size tmpboard[tmpsnake[tmpsnake_size-1]] = 0 # Virtually turn the snake tail into food (because it is virtual, so do it in tmpsnake, tmpboard) tmpboard[food] = SNAKE # place where food is placed, as a snake body result = board_BFS(tmpsnake[tmpsnake_size-1], tmpsnake, tmpboard) # Find the path length from each position to the tail of the snake for i in range(4): # Return False if the snake head and tail are next to each other. That is, you can't follow_tail, and you are chasing the tail of the snake. if is_move_possible(tmpsnake[HEAD], mov[i]) and tmpsnake[HEAD] + mov[i]==tmpsnake[tmpsnake_size-1] and tmpsnake_size>3: result = False return result
Let the head of the snake run one step towards the tail,
No matter what the body of the snake blocks, run towards the tail of the snake.
def follow_tail(): global tmpboard, tmpsnake, food, tmpsnake_size tmpsnake_size = snake_size tmpsnake = snake[:] board_reset(tmpsnake, tmpsnake_size, tmpboard) # reset virtual board tmpboard[tmpsnake[tmpsnake_size-1]] = FOOD # Make snake tails food tmpboard[food] = SNAKE # Make the food place become a snake body board_BFS(tmpsnake[tmpsnake_size-1], tmpsnake, tmpboard) # Find the length of the path from each position to the tail of the snake tmpboard[tmpsnake[tmpsnake_size-1]] = SNAKE # restore snake tail return choose_longest_safe_move(tmpsnake, tmpboard) # Return to the running direction (let the snake head move 1 step)
When all options fail,
Just find a feasible direction to go (1 step)
def any_possible_move(): global food , snake, snake_size, board best_move = ERR board_reset(snake, snake_size, board) board_BFS(food, snake, board) min = SNAKE for i in range(4): if is_move_possible(snake[HEAD], mov[i]) and board[snake[HEAD] + mov[i]]<min: min = board[snake[HEAD] + mov[i]] best_move = mov[i] return best_move,
Conversion array function
def shift_array(arr, size): for i in range(size, 0, -1): arr[i] = arr[i-1] def new_food(): #random function to generate new food global food, snake_size cell_free = False while not cell_free: w = random. randint(1, WIDTH-2) h = random.randint(1, HEIGHT-2) food = WIDTH*h + w cell_free = is_cell_free(food, snake_size, snake) pygame.draw.rect(playSurface,redColour,Rect(18*(food//WIDTH), 18*(food%WIDTH),18,18))
The real snake is in this function,
Take 1 step towards pbest_move.
def make_move(pbest_move): global snake, board, snake_size, score shift_array(snake, snake_size) snake[HEAD] += pbest_move p = snake[HEAD] for body in snake:#drawing snake, body, head, tail pygame.draw.rect(playSurface,whiteColour,Rect(18*(body//WIDTH), 18*(body%WIDTH),18,18)) pygame.draw.rect(playSurface,greenColour,Rect(18*(snake[snake_size-1]//WIDTH),18*(snake[snake_size-1]%WIDTH),18,18)) pygame.draw.rect(playSurface,headColour,Rect(18*(p//WIDTH), 18*(p%WIDTH),18,18)) #The following line is to fill in the first white block bug that will appear in the initial situation pygame.draw.rect(playSurface,(255,255,0),Rect(0,0,18,18)) # Refresh the pygame display layer pygame. display. flip() # If the newly added snake head is the food position # Increase the length of the snake by 1, generate new food, and reset the board (because the original path lengths are no longer available) if snake[HEAD] == food: board[snake[HEAD]] = SNAKE # new snake head snake_size += 1 score + = 1 if snake_size < FIELD_SIZE: new_food() else: # If the newly added snake head is not a food position board[snake[HEAD]] = SNAKE # new snake head board[snake[snake_size]] = UNDEFINED # snake tail becomes UNDEFINED, black pygame.draw.rect(playSurface,blackColour,Rect(18*(snake[snake_size]//WIDTH),18*(snake[snake_size]%WIDTH),18,18)) # Refresh the pygame display layer pygame. display. flip()
run virtually once,
Then check if this run is feasible at the call site,
It works only if it works.
After the virtual run eats the food,
Get the position of the virtual snake on the board.
def virtual_shortest_move(): global snake, board, snake_size, tmpsnake, tmpboard, tmpsnake_size, food tmpsnake_size = snake_size tmpsnake = snake[:] # If tmpsnake=snake directly, the two point to the same memory tmpboard = board[:] # The length of the path to the food from each position is already in the board, so there is no need to calculate it board_reset(tmpsnake, tmpsnake_size, tmpboard) food_eated = False while not food_eated: board_BFS(food, tmpsnake, tmpboard) move = choose_shortest_safe_move(tmpsnake, tmpboard) shift_array(tmpsnake, tmpsnake_size) tmpsnake[HEAD] + = move # Add a new position in front of the snake head # If the position of the newly added snake head is exactly the position of the food # Add 1 to the length, reset the board, and the position of the food becomes a part of the snake (SNAKE) if tmpsnake[HEAD] == food: tmpsnake_size += 1 board_reset(tmpsnake, tmpsnake_size, tmpboard) # After the virtual operation, the position of the snake on the board tmpboard[food] = SNAKE food_eated = True else: # If the snake head is not the food position, the newly added position is the snake head, and the last one becomes a space tmpboard[tmpsnake[HEAD]] = SNAKE tmpboard[tmpsnake[tmpsnake_size]] = UNDEFINED
If there is a path between the snake and the food,
Then call this function.
def find_safe_way(): global snake board safe_move = ERR # Virtually run once, because it has ensured that there is a path between the snake and the food, so the execution is valid # Get the position of the virtual snake in the board after running, that is, tmpboard virtual_shortest_move() # The only place where this function is called if is_tail_inside(): # If there is a path between the head of the snake and the tail of the snake after virtual running, choose the shortest path to run (1 step) return choose_shortest_safe_move(snake, board) safe_move = follow_tail() # Otherwise follow_tail 1 step virtually, if it can be done, return true return safe_move
Initialize the pygame module
pygame.init()
Define a variable to control the game speed
fpsClock = pygame.time.Clock()
Create a pygame display layer
playSurface = pygame.display.set_mode((270,270)) pygame.display.set_caption('snake')
Draw the pygame display layer
playSurface.fill(blackColour)
initial food
pygame.draw.rect(playSurface,redColour,Rect(18*(food//WIDTH), 18*(food%WIDTH),18,18)) while True: for event in pygame.event.get(): #Loop monitor keyboard and exit events if event.type == QUIT: #If you click to close print(score)#Print the score after the game is over pygame. quit() sys. exit() elif event.type == KEYDOWN: #If the esc key is pressed if event.key==K_ESCAPE: print(score)#Print the score after the game is over pygame. quit() sys. exit() # Refresh the pygame display layer pygame. display. flip() #Draw the wall, 255, 255, 0 is yellow, and the border is 36 because the pygame rectangle starts with the side and fills the border around it pygame.draw.rect(playSurface,(255,255,0),Rect(0,0,270,270),36) # reset distance board_reset(snake, snake_size, board) # If the snake can eat food, board_BFS returns true # In addition to the snake body (=SNAKE), other element values in the board represent the shortest path length from this point to the food if board_BFS(food, snake, board): best_move = find_safe_way() # The only call to find_safe_way else: best_move = follow_tail() if best_move == ERR: best_move = any_possible_move() # The above thought, only one direction, run one step if best_move != ERR: make_move(best_move) else: print(score)#Print the score after the game is over break # Control game speed fpsClock.tick(20)#20 looks like the speed is just right
Questions & Answers · Source Code Acquisition · Technical Exchange · Group learning please contact