贪吃蛇Python实现(手动+自动模式)

1.背景介绍

2013大火的贪吃蛇图片,网上也有不少的算法实现,在大佬的基础上,将手动与AI模式结合在一起,重构了一下,同时利用另一种Astar寻路算法优化AI模式。

参考文章:https://www.w3cschool.cn/python3/python3-73gj2zjd.html

2.启动页和结束页绘制

首先先设置一下游戏的基本设置,创建settings.py文件,在该文件中创建Settings类

#!/usr/bin/env python
# -*-coding:utf-8 -*-
#设置游戏的基本设置
import pygame
class Settings():
	def __init__(self):
		self.bg_color = (0, 0, 0)#背景颜色
		self.screen_width = 400#屏幕宽度
		self.screen_height = 400#屏幕高度

		self.cell_size = 20#每个格子的大小
		assert self.screen_height % self.cell_size == 0
		assert self.screen_width % self.cell_size == 0
		self.cell_w = int(self.screen_width / self.cell_size)#一行的格子数
		self.cell_h = int(self.screen_height / self.cell_size)#一列的格子数
		self.num = self.cell_w * self.cell_h

		self.game_stats = 0#游戏状态
		self.score = 0#游戏得分

		self.clock_frq = 5#刷新频率
		self.my_clock = pygame.time.Clock()

创建main.py,在其中创建screen和ai_settings对象

from settings import Settings
import pygame

ai_settings = Settings()
#设置屏幕
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
#添加游戏标题
pygame.display.set_caption('贪吃蛇')

创建game_function.py文件,创建show_start_interface()和show_end_interface()函数

import pygame
import sys

#检测启动页的键盘操作
def check_events(ai_settings):
	for event in pygame.event.get():
		#当按关闭或者按ESC键退出游戏
		if event.type == pygame.QUIT:
			pygame.quit()
			sys.exit()
		elif event.type == pygame.KEYDOWN:
			if event.key == pygame.K_ESCAPE:
				pygame.quit()
				sys.exit()
			#按1键选择手动模式
			elif event.key == pygame.K_1:
				ai_settings.game_stats = -1
			#按2键选择自动模式
			elif event.key == pygame.K_2:
				ai_settings.game_stats = -1

#绘制启动页
def show_start_interface(ai_settings, screen):
	title_Font = pygame.font.Font('simkai.ttf', 80)#设置标题字体
	title_image = title_Font.render("贪吃蛇", True,(255,255,255), (0,0,0))#
	title_rect = title_image.get_rect()
	title_rect.center = screen.get_rect().center

	presskey_font = pygame.font.Font('simkai.ttf', 15)#设置说明文字的字体
	presskey_image = presskey_font.render('按1为手动模式,按2为AI模式,按ESC可退出游戏', True, (255,255,255),(0,0,0))
	presskey_rect = presskey_image.get_rect()
	presskey_rect.centerx = title_rect.centerx
	presskey_rect.top = title_rect.bottom

	while True:
		screen.fill(ai_settings.bg_color)#绘制屏幕
		screen.blit(title_image, title_rect)#绘制标题
		screen.blit(presskey_image, presskey_rect)#绘制说明文字
		check_events(ai_settings)#检测键盘
		if ai_settings.game_stats != 0:#说明按了1或2,退出循环
			break
		pygame.display.flip()

#绘制结束页,game over位于屏幕中间
def show_end_interface(ai_settings, screen):
	title_font = pygame.font.Font('simkai.ttf', 80)
	game_image = title_font.render('Game',True, (233,150,122))
	over_image = title_font.render('Over',True, (233,150,122))
	game_rect = game_image.get_rect()
	over_rect = over_image.get_rect()
	screen_rect = screen.get_rect()
	game_rect.midtop = (ai_settings.screen_width / 2, screen_rect.top + 70)
	over_rect.midtop = (ai_settings.screen_width / 2, game_rect.bottom + 50)
	screen.blit(game_image, game_rect)
	screen.blit(over_image, over_rect)
	pygame.display.flip()
	#结束页维持一段时间后,返回启动页,通过修改ai_settings中的game_stats实现
	pygame.time.wait(1500)
	ai_settings.game_stats = 0

修改main.py,然后运行

from settings import Settings
import game_function as gf
import pygame

ai_settings = Settings()
#设置屏幕
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
#添加游戏标题
pygame.display.set_caption('贪吃蛇')

def run_game():
	while True:
		if ai_settings.game_stats == 0:
			gf.show_start_interface(ai_settings,screen)
		elif ai_settings.game_stats == -1:
			gf.show_end_interface(ai_settings,screen)

if __name__ == "__main__":
	pygame.init()
	run_game()

       

启示和结束界面大概就这个样子了,有兴趣美化的可以按照自己的喜好进行。

3.创建蛇

创建snake.py文件,在该文件中创建snake类

import random

class Snake():
	def __init__(self,ai_settings):
		self.reset(ai_settings)
		
	#蛇的初始化
	def reset(self,ai_settings):
		#蛇头的坐标
		self.start_x = random.randint(5, ai_settings.cell_w - 6)
		self.start_y = random.randint(5, ai_settings.cell_h - 6)
		self.head_index = 0
		#蛇的初始运动方向
		self.direction = 'right'
		#蛇的初始坐标字典,初始蛇为蛇头位置及其左边的两个格子
		self.coords = [{'x' : self.start_x, 'y': self.start_y},
		               {'x' : self.start_x - 1, 'y': self.start_y},
		               {'x' : self.start_x - 2, 'y': self.start_y}]	
	#更新蛇,每次蛇移动一步,因此相当于每次移动在蛇的坐标字典中再插入一个新蛇头
	#按常识,每次移动后,不仅仅是蛇头向前移动一个,更重要的是蛇尾也要向前移,但是在该函数中先不处理蛇尾
	#因为如果蛇吃了食物,此时蛇尾其实并没有移动
	def update(self):
		newHead = {}
		#根据移动方向确定蛇头
		if self.direction == 'up':
			newHead = {'x' : self.coords[self.head_index]['x'],
			           'y' : self.coords[self.head_index]['y'] - 1}
		elif self.direction == 'down':
			newHead = {'x' : self.coords[self.head_index]['x'],
			           'y' : self.coords[self.head_index]['y'] + 1}
		elif self.direction == 'left':
			newHead = {'x' : self.coords[self.head_index]['x'] - 1,
			           'y' : self.coords[self.head_index]['y']}
		elif self.direction == 'right':
			newHead = {'x' : self.coords[self.head_index]['x'] + 1,
			           'y' : self.coords[self.head_index]['y']}
		self.coords.insert(0,newHead)

绘制游戏界面

先修改一下check_events(),之前为了顺利绘制结束界面,按1和2时均把gane_stats修改为-1,这里先改回来

#按1键选择手动模式
elif event.key == pygame.K_1:
	ai_settings.game_stats = 1
#按2键选择自动模式
elif event.key == pygame.K_2:
	ai_settings.game_stats = 2

在game_function.py文件中创建update_screen(),绘制带格子的游戏界面

#在游戏界面上绘制格子
def draw_grid(ai_settings, screen):
	#绘制横向线
	for x in range(0, ai_settings.screen_width, ai_settings.cell_size):
		pygame.draw.line(screen,(40,40,40),(x,0),(x,ai_settings.screen_height))
	#绘制竖向线
	for y in range(0, ai_settings.screen_height, ai_settings.cell_size):
		pygame.draw.line(screen,(40,40,40),(0, y),(ai_settings.screen_width,y))

def update_screen(ai_settings, screen):
	screen.fill(ai_settings.bg_color)
	draw_grid(ai_settings, screen)

在main.py中创建蛇对象

snake = Snake(ai_settings)

绘制蛇,在game_function.py文件中创建draw_snake(),同时修改一下update_screen

#绘制蛇
def draw_snake(ai_settings, screen, snake):
	#头部用蓝色
	x = snake.coords[0]['x'] * ai_settings.cell_size
	y = snake.coords[0]['y'] * ai_settings.cell_size
	snake_head_rect = pygame.Rect(x, y, ai_settings.cell_size, ai_settings.cell_size)
	pygame.draw.rect(screen,(0,0,255), snake_head_rect)
	#蛇身内部用浅绿,外框用深绿
	for coord in snake.coords[1: -1]:
		x = coord['x'] * ai_settings.cell_size
		y = coord['y'] * ai_settings.cell_size
		snake_part_rect = pygame.Rect(x, y, ai_settings.cell_size, ai_settings.cell_size)
		pygame.draw.rect(screen,(0,155,0), snake_part_rect)
		snake_part_inner_rect = pygame.Rect(x + 4, y + 4, ai_settings.cell_size - 8, ai_settings.cell_size - 8)
		pygame.draw.rect(screen,(0,255,0), snake_part_inner_rect)
	#蛇尾用浅绿
	coord = snake.coords[-1]
	x = coord['x'] * ai_settings.cell_size
	y = coord['y'] * ai_settings.cell_size
	snake_tail_rect = pygame.Rect(x, y, ai_settings.cell_size, ai_settings.cell_size)
	pygame.draw.rect(screen,(0,255,0), snake_tail_rect)

#绘制游戏界面
def update_screen(ai_settings, screen, snake):
	screen.fill(ai_settings.bg_color)
	draw_grid(ai_settings, screen)
	draw_snake(ai_settings, screen, snake)
	#移动蛇
	del snake.coords[-1]#加蛇头
	snake.update()#去蛇尾
	pygame.display.flip()
	#暂停一下
	ai_settings.my_clock.tick(ai_settings.clock_frq)

既然有了蛇,肯定要想怎么控制它,在game_function.py文件中创建check_play_events函数

#检测游戏过程中的按键
def check_play_events(snake):
	for event in pygame.event.get():
		if event.type == pygame.QUIT:
			pygame.quit()
			sys.exit()
		elif event.type == pygame.KEYDOWN:
			if event.key == pygame.K_ESCAPE:
				pygame.quit()
				sys.exit()
			elif event.key == pygame.K_LEFT and not snake.direction == 'right':
				snake.direction = 'left'
			elif event.key == pygame.K_RIGHT and not snake.direction == 'left':
				snake.direction = 'right'
			elif event.key == pygame.K_UP and not snake.direction == 'down':
				snake.direction = 'up'
			elif event.key == pygame.K_DOWN and not snake.direction == 'up':
				snake.direction = 'down'

既然蛇已经可以动了,当蛇碰到自己或者墙壁的时候,就结束游戏,在game_function.py中创建is_game_over,同时修改一下update_screen。

def update_screen(ai_settings, screen, snake):
	screen.fill(ai_settings.bg_color)
	draw_grid(ai_settings, screen)
	#移动蛇
	del snake.coords[-1]#加蛇头
	snake.update()#去蛇尾
	if not is_game_over(ai_settings, snake):
		ai_settings.game_stats = -1
	else:
		draw_snake(ai_settings, screen, snake)
		pygame.display.flip()
		#暂停一下
		ai_settings.my_clock.tick(ai_settings.clock_frq)

def is_game_over(ai_settings,snake):
	#碰到左右墙壁
	if (snake.coords[snake.head_index]['x'] == -1 or snake.coords[snake.head_index]['x'] == ai_settings.cell_w):
		return False
	#碰到上下墙壁
	if(snake.coords[snake.head_index]['y'] == -1 or snake.coords[snake.head_index]['y'] == ai_settings.cell_h):
		return False
	#碰到自己
	if(snake.coords[snake.head_index] in snake.coords[1:]):
		return False
	return True

现在修改main,然后可以运行试试,此时可以按1键进入手动模式后,通过上下左右蛇的移动方向,碰到墙壁或自己,则显示game over

import pygame
from settings import Settings
import game_function as gf
from snake import Snake

ai_settings = Settings()
#设置屏幕
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
#添加游戏标题
pygame.display.set_caption('贪吃蛇')
#创建蛇
snake = Snake(ai_settings)

def run_game1():
	gf.check_play_events(snake)
	gf.update_screen(ai_settings, screen, snake)

def run_game():
	while True:
		if ai_settings.game_stats == 0:
			gf.show_start_interface(ai_settings,screen)
		elif ai_settings.game_stats == -1:
			gf.show_end_interface(ai_settings,screen)
			snake.reset(ai_settings)
		elif ai_settings.game_stats == 1:
			run_game1()

if __name__ == "__main__":
	pygame.init()
	run_game()

    

4.创建食物

新建food.py,在该文件中创建Food类

import random

class Food():
	def __init__(self,ai_settings, snake):
		self.update(ai_settings, snake)
	#每次食物被吃后,自动找一个空白的地方放置食物
	def update(self,ai_settings, snake):
		flag = True
		food = {}
		while flag:
			food = {'x':random.randint(0,ai_settings.cell_w - 1),
			        'y':random.randint(0,ai_settings.cell_h - 1)}
			#当新找的地方在蛇身,重新找
			if food not in snake.coords:
				flag = False
		self.coord = food

在游戏界面上绘制食物,在在game_function.py中创建show_food,同时每吃掉一个食物,得分加1,创建show_score,将得分也绘制出来。

#绘制食物
def draw_food(ai_settings,screen, food):
	x = food.coord['x'] * ai_settings.cell_size
	y = food.coord['y'] * ai_settings.cell_size
	food_rect = pygame.Rect(x, y, ai_settings.cell_size, ai_settings.cell_size)
	#将食物的位置涂成红色
	pygame.draw.rect(screen,(255,0,0),food_rect)

#绘制食物
def draw_food(ai_settings,screen, food):
	x = food.coord['x'] * ai_settings.cell_size
	y = food.coord['y'] * ai_settings.cell_size
	food_rect = pygame.Rect(x, y, ai_settings.cell_size, ai_settings.cell_size)
	#将食物的位置涂成红色
	pygame.draw.rect(screen,(255,0,0),food_rect)

建立蛇与食物的联系,即当蛇头到达食物处,食物重新放置,创建is_eat_food,更新update_screen()

#是否吃到食物
def is_eat_food(ai_settings,snake, food):
	#当蛇头在食物处,吃到食物,更新食物位置
	if snake.coords[snake.head_index] == food.coord:
		food.update(ai_settings,snake)
		ai_settings.score += 1
		return True
	return False

#绘制游戏界面
def update_screen(ai_settings, screen, snake,food):
	screen.fill(ai_settings.bg_color)
	draw_grid(ai_settings, screen)
	#移动蛇
	flag = is_eat_food(ai_settings,snake,food)
	if not flag:
		del snake.coords[-1]
	snake.update()
	if not is_game_over(ai_settings, snake):
		ai_settings.game_stats = -1
	else:
		draw_score(ai_settings,screen)
		draw_snake(ai_settings, screen, snake)
		draw_food(ai_settings, screen, food)
		pygame.display.flip()
		#暂停一下
		ai_settings.my_clock.tick(ai_settings.clock_frq)

修改main.py,至此手动模式结束,按1进入手动模式,可以完整的体验贪吃蛇。

import pygame
from settings import Settings
import game_function as gf
from snake import Snake
from food import Food

ai_settings = Settings()
#设置屏幕
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
#添加游戏标题
pygame.display.set_caption('贪吃蛇')

snake = Snake(ai_settings)#创建蛇
food = Food(ai_settings,snake)#创建食物

def run_game1():
	gf.check_play_events(snake)
	gf.update_screen(ai_settings, screen, snake, food)

def run_game():
	while True:
		if ai_settings.game_stats == 0:
			gf.show_start_interface(ai_settings,screen)
		elif ai_settings.game_stats == -1:
			gf.show_end_interface(ai_settings,screen)
			snake.reset(ai_settings)
		elif ai_settings.game_stats == 1:
			run_game1()

if __name__ == "__main__":
	pygame.init()
	run_game()

5.基于BFS实现AI版贪吃蛇。

首先既然是BFS,想想图论中怎么做BFS的,起码有一个边距离矩阵e[][],在贪吃蛇的应用的中,上述有20*20个(根据你设的screen大小可能不一样)格子,设一个1*400的向量,每个格子的标号为行号*20 + 列号,向量的每个值表示食物到该点的距离,是不是有点图论的感觉了,不妨先把这个向量实现出来。

创建一个board.py,创建board类

class Board():
	def __init__(self, ai_settings, snake, food):
		#整个游戏界面三种东西,如果是图论中,我们一定有将不能走的地方设为无穷大
		#本游戏中,不通的地方设为最大值,每走一步距离加1,所以最大距离为(ai_settings.cell_w + 1) * (ai_settings.cell_h + 1)
		#与free地方加一区别,我们直接乘以2,只要是绝对不可能达到的距离都可以的
		self.food_place = 0
		self.free_place = (ai_settings.cell_w + 1) * (ai_settings.cell_h + 1)
		self.snake_place = 2 * self.free_place

		self.num = ai_settings.num
		#1*400的向量
		self.matrix = [0] * self.num
		self.reset(ai_settings,snake,food)

	def reset(self,ai_settings,snake,food):
		for i in range(self.num):
			i_coord = {'x': (i % ai_settings.cell_w),'y' : (i // ai_settings.cell_w) }
			#按格子中的东西对应赋值
			if i_coord == food.coord:
				self.matrix[i] = self.food_place
			elif i_coord in snake.coords:
				self.matrix[i] = self.snake_place
			else:
				self.matrix[i] = self.free_place

接下来是重头戏!!怎么都感觉说不清楚,我用一段伪的不能再伪的代码来说明AI蛇的思路:

flag = 能否到达食物的位置 
#利用BFS更新蛇头到各个格子的距离,得出到达食物的距离,由此判断能否到达食物
if flag:
    放出虚拟蛇
    if 虚拟蛇吃完食物,还是安全的
        蛇沿着到食物的最短路径走
    else
        跟着尾巴走
else:
    跟着尾巴走
if 尾巴和头之间没有路径
    随便走一步
if 依旧没有方向
    游戏结束
蛇沿选择的方向走动
#######检测游戏按键,方便退出##########################
def check_play_events2():
	for event in pygame.event.get():
		if event.type == pygame.QUIT:
			pygame.quit()
			sys.exit()
		elif event.type == pygame.KEYDOWN:
			if event.key == pygame.K_ESCAPE:
				pygame.quit()
				sys.exit()

#################判断此路是否能走,即有没有走到边缘##########
def is_move_possible(ai_settings, index, direction):
	flag = False
	if direction == 'left':
		if index % ai_settings.cell_w > 0:
			flag = True
	elif direction == 'right':
		if index % ai_settings.cell_w < ai_settings. cell_w - 1:
			flag = True
	elif direction == 'up':
		if index > ai_settings.cell_w - 1:
			flag = True
	elif direction == 'down':
		if index < ai_settings.num - ai_settings.cell_w:
			flag = True
	return flag
##################BFS!!!重点###################
def board_refresh(ai_settings, snake, food, board):
	##################以食物为起点####################
	food_index = food.coord['x'] + food.coord['y'] * ai_settings.cell_w
	queue = []
	queue.append(food_index)
	###################相当于常用的visit数组################
	inqueue = [0] * ai_settings.num
	found = False
	while len(queue) != 0:
		index = queue.pop(0)
		if inqueue[index] == 1:
			continue
		inqueue[index] = 1
		for direction in ['left','right', 'up', 'down']:
			if is_move_possible(ai_settings, index, direction):
				#################如果到了蛇头位置, 说明可以吃到食物################
				if (index + snake.move_directions[direction]) == \
						(snake.coords[snake.head_index]['x']
								 + snake.coords[snake.head_index]['y'] * ai_settings.cell_w):
					found = True
				#################更新从食物到该点的距离#############
				if board.matrix[index + snake.move_directions[direction]] < board.snake_place:
					if board.matrix[index + snake.move_directions[direction]] > board.matrix[index] + 1:
						board.matrix[index + snake.move_directions[direction]] = board.matrix[index] + 1
					if inqueue[index + snake.move_directions[direction]] == 0:
						queue.append(index + snake.move_directions[direction])
	return found
#####################选择最短路径#####################
###################吃食物的时候选最短路径###################
def choose_shortest_safe_move(ai_settings, snake, board):
	snake.direction = ai_settings.ERR
	##############board记录的是从食物到该点的距离,找四个方向中board最小的值
	min_distance = board.snake_place
	for direction in ['left', 'right', 'up', 'down']:
		index = snake.coords[snake.head_index]['x'] + snake.coords[snake.head_index]['y'] * ai_settings.cell_w
		if is_move_possible(ai_settings, index, direction) and (board.matrix[index + snake.move_directions[direction]] < min_distance):
			min_distance = board.matrix[index + snake.move_directions[direction]]
			snake.direction = direction
####################放出虚拟蛇##############################
##############虚拟蛇是为了吃食物而生######################
def virtual_move(ai_settings, v_snake, food, board):
	food_eat = False
	while not food_eat:
		#每走一步都要更新board
		board_refresh(ai_settings, v_snake, food, board)
		#找最近的路吃食物
		choose_shortest_safe_move(ai_settings, v_snake, board)
		v_snake.update()#更新虚拟蛇
		#吃到食物
		if v_snake.coords[v_snake.head_index] == food.coord:
			food_index = food.coord['x'] + food.coord['y'] * ai_settings.cell_w
			board.matrix[food_index] = board.snake_place
			food_eat = True
		else:
			#没吃到食物,更新蛇和board
			new_head_index = v_snake.coords[0]['x'] + v_snake.coords[0]['y'] * ai_settings.cell_w
			board.matrix[new_head_index] = board.snake_place
			tail_index = v_snake.coords[-1]['x'] + v_snake.coords[-1]['y'] * ai_settings.cell_w
			board.matrix[tail_index] = board.free_place
			del v_snake.coords[-1]
###############判断此时是否安全,其实就是判断此时能否吃到自己的尾巴##############
def is_safe_way(ai_settings, snake, food, v_food, board):
	#此时的尾巴就是食物
	tail_index = snake.coords[-1]['x'] + snake.coords[-1]['y'] * ai_settings.cell_w
	board.matrix[tail_index] = board.food_place
	v_food.coord['x'] = snake.coords[-1]['x']
	v_food.coord['y'] = snake.coords[-1]['y']
	#该函数用于吃到食物的时候,判断虚拟蛇是否安全,所以把食物的位置改为蛇身
	food_index = food.coord['x'] + food.coord['y'] * ai_settings.cell_w
	board.matrix[food_index] = board.snake_place
	#再次用BFS更新board,判断蛇尾蛇头之间是否有路径
	result = board_refresh(ai_settings, snake, v_food, board)
	for direction in ['left', 'right', 'up', 'down']:
		#如果蛇尾和蛇头之间有路径,就选择最长的那一条路
		index = snake.coords[snake.head_index]['x'] + snake.coords[snake.head_index]['y'] * ai_settings.cell_w
		tail_index = snake.coords[-1]['x'] + snake.coords[-1]['y'] * ai_settings.cell_w
		if is_move_possible(ai_settings, index, direction) and (index + snake.move_directions[direction] == tail_index) \
			and len(snake.coords) > 3:
			result = False
	return result
#############选择最长的路,与选择最短的路相对应#############################
def choose_longest_safe_move(ai_settings, snake, board):
	snake.direction = ai_settings.ERR
	max_distance = -1
	for direction in ['left', 'right', 'up', 'down']:
		index = snake.coords[snake.head_index]['x'] + snake.coords[snake.head_index]['y'] * ai_settings.cell_w
		#在找最远的时候,与找最近的区别在于多判断一下,此路是否能走,即该距离是否小于理论最远距离
		if is_move_possible(ai_settings, index, direction) and (board.matrix[index + snake.move_directions[direction]] > max_distance) and \
			(board.matrix[index + snake.move_directions[direction]] < board.free_place):
			max_distance = board.matrix[index + snake.move_directions[direction]]
			snake.direction = direction
#######################跟着尾巴走##################################
def follow_tail(ai_settings, snake, food, v_food, board):
	board.reset(ai_settings, snake, food)
	#此时的尾巴就是食物
	tail_index = snake.coords[-1]['x'] + snake.coords[-1]['y'] * ai_settings.cell_w
	board.matrix[tail_index] = board.food_place
	v_food.coord = snake.coords[-1]

	food_index = food.coord['x'] + food.coord['y'] * ai_settings.cell_w
	board.matrix[food_index] = board.snake_place

	board_refresh(ai_settings, snake, v_food, board)
	board.matrix[tail_index] = board.snake_place
	board.matrix[food_index] = board.food_place
	#跟着尾巴走的时候,要找最长的路
	choose_longest_safe_move(ai_settings, snake, board)

def find_safe_way(ai_settings, snake, v_snake, food, v_food, board):
	#运行虚拟蛇,以最短的距离吃食物
	virtual_move(ai_settings, v_snake, food, board)
	#吃完后判断是否安全
	flag = is_safe_way(ai_settings, v_snake, food, v_food, board)

	board.reset(ai_settings, snake, food)
	board_refresh(ai_settings, snake, food, board)

	if flag:
		#安全的话,走最短的
		choose_shortest_safe_move(ai_settings, snake, board)
	else:
		#不安全的话,跟着尾巴走
		follow_tail(ai_settings, snake, food, v_food, board)

#############随便走一步###############################
#其实不要小瞧这个函数,还是很大的,目前到蛇尾和食物都不行的时候,该函数就出场了
def wander_move(ai_settings, snake, food, board):
	board.reset(ai_settings, snake, food)
	board_refresh(ai_settings, snake, food, board)
	snake.direction = ai_settings.ERR
	min_distance = board.snake_place
	#找四个方向中距离食物最近的方向
	for direction in ['left', 'right', 'up', 'down']:
		index = snake.coords[snake.head_index]['x'] + snake.coords[snake.head_index]['y'] * ai_settings.cell_w
		if is_move_possible(ai_settings, index, direction) and (board.matrix[index + snake.move_directions[direction]] < min_distance):
			min_distance = board.matrix[index + snake.move_directions[direction]]
			snake.direction = direction

def update_screen2(ai_settings, screen, snake, v_snake, food, v_food, board):
	screen.fill(ai_settings.bg_color)
	draw_grid(ai_settings, screen)
	draw_food(ai_settings, screen, food)
	draw_snake(ai_settings, screen, snake)
	draw_score(ai_settings,screen)

	board.reset(ai_settings, snake, food)
	result = board_refresh(ai_settings, snake, food, board)
	##设置虚拟蛇
	v_snake.coords = snake.coords[:]
	v_snake.head_index = snake.head_index
	v_snake.direction = ai_settings.ERR
	snake.direction = ai_settings.ERR
	#此处就是上文伪代码的意思
	if result:
		find_safe_way(ai_settings, snake, v_snake, food, v_food, board)
	else:
		follow_tail(ai_settings, snake, food, v_food, board)
	if snake.direction == ai_settings.ERR:
		wander_move(ai_settings, snake, food, board)
	if snake.direction == ai_settings.ERR:
		ai_settings.game_stats = -1
	else:
		snake.update()#更新蛇头
		#吃到食物,更新食物
		if (snake.coords[snake.head_index]['x'] == food.coord['x']) and (snake.coords[snake.head_index]['y'] == food.coord['y']):
			if len(snake.coords) < ai_settings.num:
				food.update(ai_settings, snake)
			ai_settings.score += 1
		else:
			del snake.coords[-1]#更新蛇尾
	pygame.display.flip()
	ai_settings.my_clock.tick(ai_settings.clock_frq2)

其他文件的修改

snake.py中加入

#走每一步,格子索引变化的差值
		self.move_directions = {
			'left': -1,
			'right': 1,
			'up': -ai_settings.cell_w,
			'down': ai_settings.cell_w
		}

settings.py中加入

self.clock_frq2 = 17
self.ERR = "404"

main.py修改为

import pygame
from settings import Settings
import game_function as gf
from snake import Snake
from food import Food
from board import Board

ai_settings = Settings()
#设置屏幕
screen = pygame.display.set_mode((ai_settings.screen_width, ai_settings.screen_height))
#添加游戏标题
pygame.display.set_caption('贪吃蛇')

snake = Snake(ai_settings)#创建蛇
food = Food(ai_settings,snake)#创建食物
board = Board(ai_settings, snake, food)

v_snake = Snake(ai_settings)
v_food = Food(ai_settings, snake)

def run_game1():
	gf.check_play_events(snake)
	gf.update_screen(ai_settings, screen, snake, food)

def run_game2():
	gf.check_play_events2()
	gf.update_screen2(ai_settings, screen, snake, v_snake, food, v_food, board)

def run_game():
	while True:
		if ai_settings.game_stats == 0:
			gf.show_start_interface(ai_settings,screen)
		elif ai_settings.game_stats == -1:
			gf.show_end_interface(ai_settings,screen)
			snake.reset(ai_settings)
		elif ai_settings.game_stats == 1:
			run_game1()
		else:
			run_game2()

if __name__ == "__main__":
	pygame.init()
	run_game()

猜你喜欢

转载自blog.csdn.net/qq_41562704/article/details/87991403