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_score(ai_settings, screen):
Main_Font = pygame.font.Font('simkai.ttf', 20)
score_Content = Main_Font.render('scores:%s' % (ai_settings.score), True, (255, 255, 255))
score_Rect = score_Content.get_rect()
score_Rect.topleft = (ai_settings.screen_width-120, 10)
screen.blit(score_Content, score_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 依旧没有方向
游戏结束
蛇沿选择的方向走动
修改game_function.py
#######检测游戏按键,方便退出##########################
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()