贪吃蛇自动寻路

 76e2c65f628d476a8f2da33eb7401c49.png

 游戏逻辑

snake.py

from turtle import *
from random import *
from pathfinding import GameGraph

def square(x, y, size, sq_color):
    '''绘制小正方形, 代表一格'''
    penup()
    goto(x, y)
    color(sq_color)
    pendown()

    begin_fill()
    for i in range(4):
        forward(size)
        left(90)
    end_fill()

def frame():
    '''绘制边框'''
    for i in range(-210, 200, 10):
        square(i, -200, 10, 'gray')
        square(i, 200, 10, 'gray')
    for i in range(-200, 200, 10):
        square(-210, i, 10, 'gray')
        square(190, i, 10, 'gray')

def change(direction):
    '''改变蛇的运动方向'''
    global aim_x, aim_y
    x, y = direction
    if x != -aim_x or y != -aim_y:     
        aim_x = x
        aim_y = y

def inside(head_x, head_y):
    '''判断蛇是否在边框内'''
    if -210 < head_x < 190 and -200 < head_y < 200:
        return True
    else:
        return False

all_food = []                  # 所有食物的位置
for x in range(-200, 190, 10):
    for y in range(-190, 200, 10):
        all_food.append((x, y))

def new_food():

    food = all_food.copy()
    for i in snake:            # 去掉蛇所在的位置
        food.remove(i)
    new_food_x, new_food_y = food.pop(randint(0, len(food) - 1))
    return new_food_x, new_food_y


snake = [(0, 0)]               # 蛇的起始位置
food_x, food_y = new_food()    # 食物的起始位置
aim_x, aim_y = 0, 0            # 蛇的起始方向

def move():
    global food_x, food_y, game_graph

    direction = game_graph.safe_move()
    if direction is not None:
        change(direction)
        head_move_x = snake[-1][0] + aim_x
        head_move_y = snake[-1][1] + aim_y
    

    # 判断是否撞到边框或者撞到自己
    if not inside(head_move_x, head_move_y) or [head_move_x, head_move_y] in snake:
        square(head_move_x, head_move_y, 10, 'red')
        update()
        print('得分: ', len(snake))
        return
    
    snake.append((head_move_x, head_move_y))

    # 判断是否吃到食物以及是否胜利
    if head_move_x == food_x and head_move_y == food_y:
        if(len(snake) == len(all_food)):
            print('YOU WIN!')
            return
        else:
            food_x, food_y = new_food()
            game_graph.set_food((food_x, food_y))
    else:
        snake.pop(0)

    clear()

    # 绘制边框, 蛇和食物
    frame()
    for body in snake:
        square(body[0], body[1], 10, 'black')
    square(food_x, food_y, 10, 'green')
    update()
    game_graph = GameGraph(snake.copy(), (food_x, food_y), {'xmin': -200, 'xmax': 180, 'ymin': -190, 'ymax': 190})
    ontimer(move)

game_graph = GameGraph(snake.copy(), (food_x, food_y), {'xmin': -200, 'xmax': 180, 'ymin': -190, 'ymax': 190})

寻路算法

pathfinding.py

from turtle import *
from random import *
from queue import PriorityQueue
import random

class GameGraph:
    def __init__(self, snake, food, boundaries):
        self.snake = snake
        self.food = food
        self.boundaries = boundaries

    def set_food(self, food):           #更新食物位置
        self.food = food

    def heuristic(self, a, b):          #计算曼哈顿距离
        return abs(a[0] - b[0]) + abs(a[1] - b[1])

    def get_neighbors(self, node):          #获取相邻位置
        dirs = [(0, 10), (0, -10), (10, 0), (-10, 0)]
        result = []
        for dir in dirs:
            neighbor = (node[0] + dir[0], node[1] + dir[1])
            if self.boundaries['xmin'] <= neighbor[0] <= self.boundaries['xmax'] and self.boundaries['ymin'] <= neighbor[1] <= self.boundaries['ymax'] and neighbor not in self.snake:
                result.append(neighbor)
        return result

    def pathfinding_to_tail(self):          #判断能否找到尾巴
            path = self._pathfinding(self.snake[0])
            if path is None or len(path) < 2:
                return None
                
            return (path[1][0] - path[0][0], path[1][1] - path[0][1])
    
    def pathfinding(self):          #判断能否找到食物,找不到则寻找自己的尾巴
        path = self._pathfinding(self.food)
        if path is None or len(path) < 2:
            return self.pathfinding_to_tail()
        
        return (path[1][0] - path[0][0], path[1][1] - path[0][1])
    

    def _pathfinding(self,end):  # A*算法
        start = self.snake[-1]  # 获取蛇的当前位置作为起始点
        frontier = PriorityQueue()  # 创建一个优先队列,用于存储待探索的节点
        frontier.put((0, start))  # 将起始点放入队列,优先级为0
        came_from = {}  # 创建一个字典,用于存储每个节点的前驱节点
        cost_so_far = {}  # 创建一个字典,用于存储从起始点到每个节点的已知最小代价
        came_from[start] = None  # 起始点的前驱节点为None
        cost_so_far[start] = 0  # 起始点的代价为0

        while not frontier.empty():  # 当队列不为空时,继续循环
            current = frontier.get()[1]  # 从队列中取出优先级最高(代价最小)的节点

            if current == end:  # 如果当前节点就是目标节点,结束循环
                break

            for next in self.get_neighbors(current):  # 遍历当前节点的所有邻居
                new_cost = cost_so_far[current] + 1  # 计算从起始点到邻居节点的新代价
                if next not in cost_so_far or new_cost < cost_so_far[next]:  # 如果邻居节点还未被探索过,或者新代价小于已知代价
                    cost_so_far[next] = new_cost  # 更新代价
                    priority = new_cost + self.heuristic(end, next)  # 计算优先级,等于新代价加上邻居节点到目标节点的估计代价(启发式函数)
                    frontier.put((priority, next))  # 将邻居节点放入队列
                    came_from[next] = current  # 记录邻居节点的前驱节点

        if end not in came_from:  # 如果目标节点没有被探索过,返回None
            return None

        current = end  # 从目标节点开始
        path = []  # 创建一个列表,用于存储从起始点到目标节点的路径
        while current != start:  # 当前节点不是起始点时,继续循环
            path.append(current)  # 将当前节点添加到路径中
            current = came_from[current]  # 移动到前驱节点
        path.append(start)  # 将起始点添加到路径中
        path.reverse()  # 反转路径,使其从起始点开始

        return path  # 返回路径

    def getACellThatIsFarthestToGoal(self):         # 获取离目标最远的格子
        max_distance = -1
        farthest_cell = None
        for neighbor in self.get_neighbors(self.snake[-1]):
            distance = self.heuristic(neighbor, self.food)
            self.snake.append(neighbor)
            if self.pathfinding_to_tail() is not None and distance > max_distance:
                max_distance = distance
                farthest_cell = neighbor
            self.snake.pop()
        if farthest_cell is None:  # 如果所有的邻居格子都不能到达尾巴,选择一个默认的格子
            farthest_cell = self.get_neighbors(self.snake[-1])[0]
        return farthest_cell

    def safe_move(self):

        direction = self.pathfinding()
        if direction is None:
            direction = self.pathfinding_to_tail()  # 如果找不到吃苹果的路线,尝试找到尾巴的路线
        if direction is None:
            farthest_cell = self.getACellThatIsFarthestToGoal()
            if farthest_cell is not None:
                direction = (farthest_cell[0] - self.snake[-1][0], farthest_cell[1] - self.snake[-1][1])
            else:
                direction = random.choice([(-10,0),(10,0),(0,10),(0,-10)])  # 如果找不到安全的移动方向,选择一个默认的方向
        return direction

运行

main.py

from snake import *
from pathfinding import *
from turtle import *
from random import *

setup(420, 420)
title('贪吃蛇')
hideturtle()
tracer(False)
game_graph = GameGraph(snake.copy(), (food_x, food_y), {'xmin': -200, 'xmax': 180, 'ymin': -190, 'ymax': 190})
move()
done()

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值