首页 > 其他分享 >迷宫机器人最短路径使用tkinter绘制

迷宫机器人最短路径使用tkinter绘制

时间:2023-01-12 16:45:13浏览次数:67  
标签:index tkinter 迷宫 最短 width coordinate maze height ratio

起因

我想要写一个玩家和机器对战的迷宫游戏。这个项目我没有写完,我实现了最短机器人路径并绘制在tkinter上,以及玩家移动的功能。更多的关于GUI的设计太花时间了我没有写完。

算法介绍

我在写机器人路径时想到了学习强化学习时的方法,创建一个迷宫大小(height×width)的列表,对迷宫每一个可移动的位置设置一个价值,价值的具体值是这样得到的:机器人从终点开始(从起点也可以逻辑反着来)每移动一步就得到-1的奖励值,也就是说机器人应该以尽可能少的步数走完迷宫,因为这个奖励值在逻辑上其实惩罚,没走一步就得到-1的惩罚。终点的价值为0,这样和终点相邻的位置因为只需要移动一步所以价值就是-1(0+(-1)),移动两步就是-2(上一个位置的价值+奖励值)。这样使用广度优先算法遍历整个列表就可以得到每一个可移动的位置的价值。然后只需要让机器人从起点开始每一步都走价值最高的格子一直到终点就是最短路径了。

代码实现

我在上一篇博客已经实现了迷宫生成,之后我又改了一下把plt绘图单独放到了一个方法具体我更新在了github,下面的maze.generate_maze()调用后会得到一个墙的列表,可以使用maze.edges访问

maze = Maze(height=height, width=width)
maze.generate_maze()

使用tkinter绘制迷宫

这里老师的要求是使用tkinter,所以我对绘制迷宫做了如下更改,目的是使绘的图变的大一点,比如一个50*50的迷宫大小绘制到屏幕上会非常小,ratio就是就是按比例放大的倍数。can_line先不要管,在图上画线时也要等比例放大edge[0] * ratio后面又加了ratio变成了这样edge[0] * ratio + ratio是因为使用tkinter绘图时y为0的坐标画不出来所以加上ratio

window = tk.Tk()
window.title("简单绘画")
height_can = 0;
width_can = 0;
ratio = 0
while height_can < 500 or width_can < 600:
	ratio += 1
	height_can = maze.HEIGHT * ratio
	width_can = maze.WIDTH * ratio
maze_can = tk.Canvas(window, width=width_can + ratio, height=height_can + ratio)
maze_can.pack()
can_line = [(ratio, ratio, ratio, 1 * ratio + ratio),
			(maze.WIDTH * ratio + ratio, (maze.HEIGHT - 1) * ratio + ratio, maze.WIDTH * ratio + ratio,
			 maze.HEIGHT * ratio + ratio)]
for edge in maze.edges:
	q = (edge[0] * ratio + ratio, edge[1] * ratio + ratio, edge[2] * ratio + ratio, edge[3] * ratio + ratio)
	can_line.append(q)
	maze_can.create_line((q[0], q[1]), (q[2], q[3]), width=2)

绘制玩家起始位置

maze_can.x_coordinate是起始开始的x坐标,也就是玩家的起始坐标,迷宫的入口左上角的坐标,使用maze_can.create_oval绘圆只需要提供圆的左上角和右下角坐标就可以了,这是tkinter自带的方法不是我自己写的。maze_can.x_coordinate, maze_can.y_coordinate是左上角坐标,maze_can.x_coordinate + ratio, maze_can.y_coordinate + ratio是右下角坐标。bind方法的用处看下一段

maze_can.x_coordinate = ratio
maze_can.y_coordinate = ratio
maze_can.player = maze_can.create_oval(maze_can.x_coordinate, maze_can.y_coordinate,
									   maze_can.x_coordinate + ratio, maze_can.y_coordinate + ratio,
									   width=1, fill='red')
window.bind('<Left>', left_arrow)
window.bind('<Right>', right_arrow)
window.bind('<Up>', up_arrow)
window.bind('<Down>', down_arrow)

下面代码的目的是让玩家可以移动

上面的都是绘图,下面则要开始实现一些逻辑,玩家想要移动首先需要判断往哪个方向可以移动,这个问题实现在left_move(x_coordinate, y_coordinate)。这个问题简化下来比如我们想要向左移动,只需要知道当前位置的左上角坐标和左下角坐标是否在墙的列表can_line中,如果在里面则不能向左移动,can_linemaze.edges的坐标等比例放大,目的也是绘图...其实逻辑上反而是花时间比较少的,绘图才花时间。判断方向应该有4个函数,这里写出来的是其中一个能否向左移动。left_arrow是把事件绑定到左箭头上,如果可以移动则删除当前位置的圆在移动的位置画一个圆,目的是像游戏一样有交互感

def left_move(x_coordinate, y_coordinate):
	x = (x_coordinate, y_coordinate,
		 x_coordinate, y_coordinate + ratio)
	if x not in can_line:
		return True
	return False
	
def left_arrow(event):
	if left_move(maze_can.x_coordinate, maze_can.y_coordinate):
		maze_can.x_coordinate -= ratio
		maze_can.delete(maze_can.player)
		maze_can.player = maze_can.create_oval(maze_can.x_coordinate, maze_can.y_coordinate,
											   maze_can.x_coordinate + ratio, maze_can.y_coordinate + ratio,
											   width=1, fill='red')

初始化价值列表

maze_can.agent一开始我是打算让机器人一步一步走到出口的,但太花时间了,也就是这段代码可以删掉没有一丝影响,在后面我直接画出了路径没有用到这个属性。好了,要开始写最短路径的机器人了!首先我们创建一个和迷宫相同大小的列表agent_list然后把每一个值赋值-100000这个值可以是任意的只要别太大就行,比如-100就不行,因为迷宫很大时迷宫入口的价值很可能小于-100。然后在出口位置agent_list[height - 1][width - 1]赋值为0代表出口位置的价值为0。valid_queue是用来广度优先搜索的列表,其实在逻辑上是一个队列,先加入列表的先遍历,后加入列表的一个一个往后排

maze_can.agent = maze_can.create_oval(maze_can.x_coordinate, maze_can.y_coordinate,
									  maze_can.x_coordinate + ratio, maze_can.y_coordinate + ratio,
									  width=1, fill='blue')
agent_list = np.zeros((height, width))
agent_list -= 100000
agent_list[height - 1][width - 1] = 0
valid_queue = []
valid_queue.append((height - 1, width - 1))

下面的目的是得到迷宫每个位置的价值

get_queue(height_index, width_index)函数的目的是获得与当前位置相邻且可移动(没有墙的阻碍)的位置坐标(准确的说是索引,在后面的其他函数会转换为坐标)。reward_value是得到所有的与当前位置相邻且可移动位置的价值(最大是4个,代表上下左右),结果是一个字典比如{-13:'left'},之所以是字典因为后面还有一个逻辑要知道这个价值对应的方向。后面的for循环里的逻辑是把当前位置的价值设置为上面列表里最大值-1的值,即最大值+奖励值。然后遍历队列里的下一个位置。这段代码执行结束后就会得到迷宫每个位置的价值并存储在agent_list

def get_queue(height_index, width_index):
	if left_move((width_index + 1) * ratio, (height_index + 1) * ratio) and agent_list[height_index][
		width_index - 1] == -100000:
		valid_queue.append((height_index, width_index - 1))
	if right_move((width_index + 1) * ratio, (height_index + 1) * ratio) and agent_list[height_index][
		width_index + 1] == -100000:
		valid_queue.append((height_index, width_index + 1))
	if up_move((width_index + 1) * ratio, (height_index + 1) * ratio) and agent_list[height_index - 1][
		width_index] == -100000:
		valid_queue.append((height_index - 1, width_index))
	if down_move((width_index + 1) * ratio, (height_index + 1) * ratio) and agent_list[height_index + 1][
		width_index] == -100000:
		valid_queue.append((height_index + 1, width_index))


def reward_value(height_index, width_index):
	i = {}
	if left_move((width_index + 1) * ratio, (height_index + 1) * ratio):
		i[agent_list[height_index][width_index - 1]] = 'left'
	if right_move((width_index + 1) * ratio, (height_index + 1) * ratio):
		i[agent_list[height_index][width_index + 1]] = 'right'
	if up_move((width_index + 1) * ratio, (height_index + 1) * ratio):
		i[agent_list[height_index - 1][width_index]] = 'up'
	if down_move((width_index + 1) * ratio, (height_index + 1) * ratio):
		i[agent_list[height_index + 1][width_index]] = 'down'
	return i


for index in valid_queue:
	get_queue(index[0], index[1])
	if agent_list[index[0]][index[1]] == -100000:
		nearby_dic = reward_value(index[0], index[1])
		agent_list[index[0]][index[1]] = max(nearby_dic.keys()) - 1

这时如果打印agent_list,假如迷宫是5*5大小则可以得到下图
image

下面是在图上画出最短路径

使机器人在入口位置然后每一步都走价值最高的格子,get_path(height_index, width_index)是得到这样的格子。x = index[1] * ratio + 1.3 * ratio以及后面的绘图代码都是为了矩形在图里小一点,起到美观的作用,不然太丑了。

def get_path(height_index, width_index):
	nearby_dic = reward_value(height_index, width_index)
	direction = nearby_dic[max(nearby_dic.keys())]
	if direction == 'left':
		path_queue.append((height_index, width_index - 1))
	if direction == 'right':
		path_queue.append((height_index, width_index + 1))
	if direction == 'up':
		path_queue.append((height_index - 1, width_index))
	if direction == 'down':
		path_queue.append((height_index + 1, width_index))


path_queue = [(0, 0)]
for index in path_queue:
	x = index[1] * ratio + 1.3 * ratio
	y = index[0] * ratio + 1.3 * ratio
	maze_can.create_rectangle(x, y, x + 0.4 * ratio, y + 0.4 * ratio, fill='green', outline='green')
	if index == (height - 1, width - 1):
		break
	get_path(index[0], index[1])

window.mainloop()

假如一个50*50的迷宫最终结果是这样的
image

完整的代码我放在了这里agent_maze.py

标签:index,tkinter,迷宫,最短,width,coordinate,maze,height,ratio
From: https://www.cnblogs.com/zaoan660/p/17047048.html

相关文章

  • 【图论】浅谈JohnSon全源最短路
    前置知识SPFA、Dijkstra.引文先是给一道题目:给定一个包含n个结点和m条带权边的有向图,求所有点对间的最短路径长度,一条路径的长度定义为这条路径上所有边的权值和。......
  • python利用matplotlib生成迷宫
    起因我想要写一个项目叫python迷宫游戏,需求是玩家能和机器对抗率先走出迷宫,至少要有两个等级的电脑。慢慢来,首先迷宫游戏需要有一个迷宫并展示出来,这便是这篇博客的目的......
  • java:路径———(最短路径)
    题目描述小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。小蓝的图由2021个结点组成,依次编号1至2021。对于两个不同的结点a,b......
  • C++ 图进阶系列之纵横对比 Bellman-Ford 和 Dijkstra 最短路径求解算法
    1.前言因无向、无加权图的任意顶点之间的最短路径由顶点之间的边数决定,可以直接使用原始定义的广度优先搜索算法查找。但是,无论是有向、还是无向,只要是加权图,最短路径长......
  • 「最短路」最简单的最短路问题
    本题为1月10日22寒假集训每日一题题解题目来源:(未知)题面题目描述给出一个图,起始点是1,结束点是N,边是双向的。求点1到点N的最短距离。哈哈,这就是标准的最短路径问题......
  • P1141 01迷宫
    这题数据有点高级啊(这么高级的数据能不能把它变成黄题呢?不然显得我很垃圾(虽然是事实))思路联通块,把周围四格与自己不同的联通起来,看成一个大块,知道要的坐标属于哪个大块并......
  • 「最短路」农场派对
    本题为1月4日22寒假集训每日一题题解题目来源:(未知)题面题目描述N(1<=N<=1000)头牛要去参加一场在编号为x(1<=x<=N)的牛的农场举行的派对。有M(1<=M<=100000)条有向......
  • 最短路问题学习笔记
    之前对最短路的认识不是很充分,结合之前的博客来重新总结一下最短路的问题的解法。Floyd算法——小数据杀手相信最先接触的最短路算法就是Floyd算法了,因为他精简的代码实......
  • 算法之Dijkstra及其堆优化和SPFA:图上单源最短路径神器
    签到题……题目传送门SPFA算法本人曾经写过一篇有关Bellman-ford的博,但就算是挂了优化的ford也只能过这道题的弱化版。今天就先填个坑,先讲SPFA。在这里我直接认为你们......
  • hdu:最短路(堆优化的dijkstra)
    ProblemDescription在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现......