01-贪吃蛇自动寻路

01-贪吃蛇自动寻路

项目背景

利用turtle库编写的贪吃蛇小程序,要求:

  1. 实现自动吃食物
  2. 不能将自己绕死。

自动寻路算法

算法核心:A*算法的简化(完全实现应该可以,但耗时太多了,我选择放弃)

算法实现:

首先是基础的贪吃蛇程序的实现,以下为预置代码:

import pathfinding as pf
from turtle import *
from random import *

snake = [[0, 0]]  # 蛇的起始位置
aim_x, aim_y = 0, -10  # 蛇的起始方向


def square(x, y, size, sq_color):
    """绘制小正方形, 代表一格"""
    color(sq_color)
    up()
    goto(x, y)
    down()
    begin_fill()
    for i in range(4):
        fd(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(x, y):
    """改变蛇的运动方向"""
    global aim_x, aim_y
    if x != -aim_x or y != -aim_y:
        aim_x, aim_y = x, 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


food_x, food_y = new_food()  # 食物的起始位置



def move():
    global food_x, food_y
    pf.pathfinding(food_x, food_y)
    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()
    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()
    # 根据得分调节速度, 每到达一定分数会提高速度
    if len(snake) < 10:
        ontimer(move, 40)
    elif len(snake) < 20:
        ontimer(move, 30)
    elif len(snake) < 30:
        ontimer(move, 20)
    elif len(snake) < 40:
        ontimer(move, 10)
    else:
        ontimer(move, 0)


setup(420, 420)
title('贪吃蛇')
hideturtle()
tracer(False)
#listen()
#onkey(lambda: change(0, 10), 'Up')
#onkey(lambda: change(-10, 0), 'Left')
#onkey(lambda: change(0, -10), 'Down')
#onkey(lambda: change(10, 0), 'Right')

move()
done()

出于自动寻路的实现,对接口做了改动

算法:

  • 以蛇头作为起点,食物位置为终点,当广度搜索找到食物所在坐标时停止
start = (s.snake[-1][0],s.snake[-1][1])
    came_from[start] = None
    frontier_put(start)
    

    while frontier_empty():
        current = frontier_get()
        if (x,y) in came_from.keys():
            break

        for next in current.values():
            if not_in_came_from(next) and not_in_reached(next):
                #if rightway(next, start):
                #    continue
                #else:
                frontier_put(next)
                came_from[next] = frontier[miner()]
                reached_add(next)
               
        frontier.pop(miner())

  • 将能寻到食物的路径用字典储存
current = (x, y)
    path = []
    while current != start: 
        path.append(current)
        try:
            current = came_from[current]
        except:
            break

    path.reverse() # 使第一步变成首位
    came_from.clear()
    reached.clear()
    frontier.clear()

    turn(path[0])

代码不足:

理论上完善的自动寻路应该能处理没有到达食物路径的情况,但是不会码这个,也就是说当身体横跨地图,挡住蛇头通往食物的路时会出bug,主要还是犯懒没有深入研究A*和dfs

下面是完整代码,希望有大佬指出不足,比如说代码风格或者某些部分的实现,理论上来说有些部分应该用类来做,但是类还没学。。。。。。

import snake as s

 

frontier = dict()
f_count = 0
came_from = dict()# path A->B is stored as came_from[B] == A
reached = dict()
r_count = 0

def frontier_put(ch):#添加
    global f_count
    frontier[f_count] = ch
    f_count += 1

def reached_add(ch):
    global r_count
    reached[r_count] = ch
    r_count += 1


def frontier_empty():#判断是否为控
    if frontier:
        return True
    else: return False

def miner():#获得最小键值
    arr = frontier.keys()
    ch = []
    for i in arr:
        ch.append(i)
    tep = ch[0]
    for i in ch:
        if tep >= i:
            tep = i
    return tep

def frontier_get():
    ch = dict()
    count = 0
    m = miner()
    x = frontier[m][0]
    y = frontier[m][1]
    if  s.inside(x+10, y) and [x+10, y] not in s.snake:#判断其周围是否合法
        ch[count] = (x+10, y)
        count +=1
    if  s.inside(x-10, y) and [x-10, y] not in s.snake:
        ch[count] = (x-10, y)     
        count +=1                 
    if  s.inside(x, y+10) and [x, y+10] not in s.snake:
        ch[count] = (x, y+10)    
        count +=1                
    if  s.inside(x, y-10) and [x, y-10] not in s.snake:
        ch[count] = (x, y-10)
        count +=1
    return ch

def not_in_came_from(tep):
    ch = came_from.values()
    for i in ch:
        if tep == i:
            return False
    return True

def not_in_reached(tep):
    ch = reached.values()
    for i in ch:
        if tep == i:
            return False
    return True

def pathfinding(x, y):

    start = (s.snake[-1][0],s.snake[-1][1])
    came_from[start] = None
    frontier_put(start)
    

    while frontier_empty():
        current = frontier_get()
        if (x,y) in came_from.keys():
            break

        for next in current.values():
            if not_in_came_from(next) and not_in_reached(next):
                #if rightway(next, start):
                #    continue
                #else:
                frontier_put(next)
                came_from[next] = frontier[miner()]
                reached_add(next)
               
        frontier.pop(miner())

    current = (x, y)
    path = []
    while current != start: 
        path.append(current)
        try:
            current = came_from[current]
        except:
            break

    path.reverse() # 使第一步变成首位
    came_from.clear()
    reached.clear()
    frontier.clear()

    turn(path[0])

def turn(path):
    
    # 0 右
    # 1 上
    # 2 左
    # 3 下
    x = path[0]
    y = path[1]
    start_x, start_y = s.snake[-1][0],s.snake[-1][1]
    pre_x = x - start_x
    pre_y = y - start_y
    if pre_x>0:
        pre_x = 10
    elif pre_x<0:
        pre_x = -10
    if pre_y>0:
        pre_y = 10
    elif pre_y<0:
        pre_y = -10
    
    s.change(pre_x, pre_y)
    

#def goto(x, y):
#    s.snake[-1][0]
    #以下为实验输入端口
    #if s.snake[-1][0] > x:
    #    if s.aim_x != 10:
    #        turn(2)
    #    elif s.aim_x == 10:
    #        if s.snake[-1][1] > y:
    #            turn(3)
    #        elif s.snake[-1][1] < y:
    #            turn(1)
    #        elif s.snake[-1][1] == y:
    #            if not s.inside(s.snake[-1][0], s.snake[-1][1]-10) or s.inside(s.snake[-1][0], s.snake[-1][1]-10) in s.snake:
    #                turn(1)
    #            else: turn(3)

    #elif s.snake[-1][0] < x:
    #    if s.aim_x != -10:
    #        turn(0)
    #    elif s.aim_x == -10:
    #        if s.snake[-1][1] > y:
    #            turn(3)
    #        elif s.snake[-1][1] < y:
    #            turn(1)
    #        elif s.snake[-1][1] == y:
    #            if not s.inside(s.snake[-1][0], s.snake[-1][1]-10) or s.inside(s.snake[-1][0], s.snake[-1][1]-10) in s.snake:
    #                turn(1)
    #            else: turn(3)
    #elif s.snake[-1][0] == x:
    #    if s.snake[-1][1] < y:
    #        if s.aim_y == 10:
    #            turn(1)
    #        elif s.aim_y == -10:
    #            if not s.inside(s.snake[-1][0]+10, s.snake[-1][1]) or s.inside(s.snake[-1][0]+10, s.snake[-1][1]) in s.snake:
    #                turn(2)
    #            else: turn(0)
    #        else:
    #            turn(1)
    #    elif s.snake[-1][1] > y:
    #        if s.aim_y == -10:
    #            turn(3)
    #        elif s.aim_y == 10:
    #            if not s.inside(s.snake[-1][0]+10, s.snake[-1][1]) or s.inside(s.snake[-1][0]+10, s.snake[-1][1]) in s.snake:
    #                turn(2)
    #            else: turn(0)
    #        else:
    #            turn(3)
    #    else:
    #        1
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值