第二轮循环结束了,开始第三轮操作
此时
G.F = √10 + 1,由此得到G是open_list里F最小的点
A*寻路的核心代码AStar.py
GameManager.py负责可视化寻路(运行这个脚本,记住让其他脚本与这个脚本保持在同一个目录)
MapPoint.py用来生成地图的单个点
FreePoint.py用来生成玩家或目标
GameMap.py用来生成整个地图
import threading
import tkinter
import time
from AStar import a_star
from FreePoint import FreePoint
from GameMap import GameMap
from MapPoint import MapPoint
class GameManager:
def __init__(self):
self.win = None
self.canvas = None
self.point_type_dict = {
'player': ('circle', 'green'),
'obstacle': ('square', 'black'),
'AI': ('circle', 'yellow'),
'path': ('square', 'skyblue'),
}
self.player = None
self.AI = None
self.key_list = []
self.key_mapping = {
38: 'up',
40: 'down',
37: 'left',
39: 'right',
32: 'restore_map',
65: 'add_obstacle',
}
self.game_loop = True
self.path_list = []
self.width = 25
self.height = 25
self.game_map = GameMap(width=self.width, height=self.height)
def start(self):
# 创建窗口画背景
self.create_win()
self.draw_bg()
# 创建玩家和AI且画出来
self.player = FreePoint(x=22, y=9)
self.AI = FreePoint(x=0, y=0)
self.draw_point(self.point_type_dict['player'], self.player.get_coordinate(), 'player')
self.draw_point(self.point_type_dict['AI'], self.AI.get_coordinate(), 'AI')
# 初始化地图坐标讯息
self.game_map.restore_map()
# 生成随即障碍物
obstacle_count = 100
self.game_map.create_random_obstacle(self.get_occupied_points(), obstacle_count)
# 画障碍
self.draw_obstacle()
# 画路径
self.draw_path(self.AI.get_coordinate(), self.player.get_coordinate())
# 监听键盘消息
self.win.bind('' , self.listen_key_board)
# 开启画面循环更新线程
t = threading.Thread(target=self.update_canvas)
t.setDaemon(True) # 设置为守护线程,也就退出程序时,关闭该线程
t.start()
self.win.protocol("WM_DELETE_WINDOW", self.close_game) # 关闭窗口时触发
# 窗口主循环
self.win.mainloop()
def create_win(self):
if not self.win:
self.win = tkinter.Tk()
self.win.wm_title("测试") # 窗口标题
self.win.geometry("1000x600") # 窗口大小
self.win.update()
# 画背景
def draw_bg(self):
if not self.canvas:
self.canvas = tkinter.Canvas(self.win, width=520, height=520, bg='pink')
self.canvas.place(x=int((self.win.winfo_width() - int(self.canvas.cget('width'))) // 2), y=int((self.win.winfo_height() - int(self.canvas.cget('height'))) // 2))
# 画格子,一共25 * 25个格子(20单位一格)
for x in range(self.width + 1):
self.canvas.create_line(10, x * 20 + 10, 510, x * 20 + 10) # 横线26条
self.canvas.create_line(x * 20 + 10, 10, x * 20 + 10, 510) #
# 根据坐标信息描绘点
def draw_point(self, point_type, coordinate, tag):
if point_type[0] == 'circle':
self.canvas.create_oval(10 + coordinate[0] * 20, 10 + coordinate[1] * 20, 30 + coordinate[0] * 20, 30 + coordinate[1] * 20, fill=point_type[1], tags=tag)
elif point_type[0] == 'square':
self.canvas.create_rectangle(10 + coordinate[0] * 20, 10 + coordinate[1] * 20, 30 + coordinate[0] * 20, 30 + coordinate[1] * 20, fill=point_type[1], tags=tag)
# 键盘监听
def listen_key_board(self, key):
self.key_list = []
self.key_list.append(self.key_mapping.get(key.keycode))
# 更新画布
def update_canvas(self):
while self.game_loop:
command = None
if self.key_list:
command = self.key_list.pop(0)
if command:
if command == 'restore_map':
# 清理已有路径和障碍
self.clear_path()
self.clear_obstacle()
# 还原地图数据
self.game_map.obstacle_set.clear()
self.game_map.restore_map()
# 重画障碍和路径
self.game_map.create_random_obstacle(self.get_occupied_points(), 100)
self.draw_obstacle()
self.draw_path(self.AI.get_coordinate(), self.player.get_coordinate())
elif command == 'add_obstacle':
self.clear_path()
# 新增一个随机障碍
point = self.game_map.create_random_obstacle(self.get_occupied_points(), 1)
self.draw_point(self.point_type_dict['obstacle'], point, 'obstacle')
self.draw_path(self.AI.get_coordinate(), self.player.get_coordinate())
else:
check_point = self.player.check_move(command)
# 检测将要移动的点是否是障碍物或者是否出界
if check_point not in self.game_map.obstacle_set and not self.check_out(check_point):
self.player.move(command)
self.canvas.delete("player")
self.draw_point(self.point_type_dict['player'], self.player.get_coordinate(), 'player')
self.draw_path(self.AI.get_coordinate(), self.player.get_coordinate())
time.sleep(0.01)
# 获取占用点列表
def get_occupied_points(self):
occupied_points = (self.AI.get_coordinate(), self.player.get_coordinate())
return occupied_points
# 画障碍
def draw_obstacle(self):
for point in self.game_map.obstacle_set:
self.draw_point(self.point_type_dict['obstacle'], point, 'obstacle')
# 清除障碍
def clear_obstacle(self):
self.canvas.delete('obstacle')
# 获取最新路径
def draw_path(self, start, end):
# 检测起点和终点是否在地图
if start in self.game_map.map and end in self.game_map.map:
# 使用A星寻路获取路径
start_time = time.time()
result_path = a_star(start, end, self.game_map.map)
# 获取玩家对AI的寻路
result_path_two = a_star(end, start, self.game_map.map)
if len(result_path_two) < len(result_path):
result_path = result_path_two
# 若存在路径
if result_path:
self.path_list = result_path
self.canvas.delete('path')
for point in result_path[:-1]:
self.draw_point(self.point_type_dict['path'], point, 'path')
# 清除路径
def clear_path(self):
self.canvas.delete('path')
self.path_list = []
# 检测坐标点是否出界
def check_out(self, point):
return point[0] < 0 or point[0] >= self.width or point[1] < 0 or point[1] >= self.height
# 关闭程序
def close_game(self):
self.game_loop = False
self.win.destroy()
def main():
game_manager = GameManager()
game_manager.start()
if __name__ == '__main__':
main()
import copy
import random
def a_star(start, end, raw_map_point_dict, calculate_count=1):
# 计算次数
calculate_count = max(calculate_count, 1)
for _ in range(calculate_count):
pass
map_point_dict = copy.deepcopy(raw_map_point_dict)
# 建立open集合和close集合
open_dict = {}
close_dict = {}
# 初始化地图起点
map_point_dict[start].G = 0
map_point_dict[start].H = get_H(start, end)
map_point_dict[start].F = map_point_dict[start].G + map_point_dict[start].H
# 把起点加紧open集合里
open_dict[start] = map_point_dict[start].F
# open为空时退结束循环
is_find_end = False
while open_dict and not is_find_end:
# 找出F代价最小的作为基点
base_point = list(list(open_dict.items())[0])
''' 随机找法 '''
equal_list = []
for key, value in open_dict.items():
if value < base_point[1]:
base_point[0] = key
base_point[1] = value
equal_list = [base_point]
elif value == base_point[1]:
equal_list.append((key, value))
if equal_list:
random.shuffle(equal_list)
base_point = equal_list[0]
''' 固定顺序找法 '''
# for key, value in open_dict.items():
# if value < base_point[1]:
# base_point[0] = key
# base_point[1] = value
# print(base_point)
# 把选出来的基点从open集合里去掉
del open_dict[base_point[0]]
# 获取基点相邻点坐标
neighbor_list = []
neighbor_list.append(('up', (base_point[0][0], base_point[0][1] - 1)))
neighbor_list.append(('down', (base_point[0][0], base_point[0][1] + 1)))
neighbor_list.append(('left', (base_point[0][0] - 1, base_point[0][1])))
neighbor_list.append(('right', (base_point[0][0] + 1, base_point[0][1])))
# 遍历相邻点(即处理相邻点)
for key, value in neighbor_list:
# 超出限定值
if value[0] < 0 or value[0] > 24 or value[1] < 0 or value[1] > 24:
continue
# 判定为障碍物
if map_point_dict[value].is_obstacle:
continue
# 判定该点是否已经在close里,即已经计算过的点
if value in close_dict:
continue
# 判定该点是否已经在open里
elif value in open_dict:
new_G = map_point_dict[value].G + map_point_dict[value].neighbor_cost[key]
if map_point_dict[value].G > new_G:
map_point_dict[value].parent_point.append(base_point[0])
map_point_dict[value].G = new_G
map_point_dict[value].F = new_G + map_point_dict[value].H
else:
map_point_dict[value].parent_point.append(base_point[0])
map_point_dict[value].G = 0 + map_point_dict[value].neighbor_cost[key]
map_point_dict[value].H = get_H(value, end)
map_point_dict[value].F = map_point_dict[value].G + map_point_dict[value].H
open_dict[value] = map_point_dict[value].F
# 判定是否找到终点
if value == end:
is_find_end = True
close_dict[base_point[0]] = base_point[1]
if is_find_end:
result_path = get_path(start, end, map_point_dict)
return result_path
else:
return []
# 计算终点代价
def get_H(start, end):
# 计算两点间的距离
distance = ((start[0] - end[0]) ** 2 + (start[1] - end[1]) ** 2) ** 0.5
return distance
# 获取路径列表
def get_path(start, end, map_point_dict):
result_path = []
result_path.append(end)
current_point = map_point_dict[end].get_parent()
while current_point != start:
result_path.append(current_point)
current_point = map_point_dict[current_point].get_parent()
result_path.reverse()
return result_path
if __name__ == '__main__':
from MapPoint import MapPoint
# 建立地图点25 * 25 (0 ~ 24)
map_point_dict = {}
for x in range(25):
for y in range(25):
map_point_dict[(x, y)] = MapPoint(x=x, y=y)
# 输入起点和终点
start = (0, 0)
end = (22, 9)
print(a_star(start, end, map_point_dict))
class MapPoint:
def __init__(self, **kwargs):
self.x = kwargs.get('x', 0)
self.y = kwargs.get('y', 0)
self.F = kwargs.get('F')
self.G = kwargs.get('G')
self.H = kwargs.get('H')
self.parent_point = []
self.is_obstacle = False
self.neighbor_cost = {}
self.neighbor_cost['up'] = self.neighbor_cost['down'] = self.neighbor_cost['left'] = self.neighbor_cost['right'] = 1
self.neighbor_cost['ul'] = self.neighbor_cost['ur'] = self.neighbor_cost['dl'] = self.neighbor_cost['dr'] = 1.414
def set_neighbor_cost(self, direct, value):
self.neighbor_cost[direct] = value
def get_coordinate(self):
return self.x, self.y
def get_parent(self):
return self.parent_point.pop() if self.parent_point else None
if __name__ == '__main__':
# 建立地图点25 * 25 (0 ~ 24)
map_point_dict = {}
for x in range(25):
for y in range(25):
map_point_dict[(x, y)] = MapPoint(x=x, y=y)
# print(map_point_dict)
import copy
import random
from MapPoint import MapPoint
class GameMap:
def __init__(self, **kwargs):
self.width = kwargs.get('width', 0)
self.height = kwargs.get('height', 0)
self.raw_map = {}
self.map = {}
self.obstacle_set = set()
self.init_map()
def init_map(self):
for x in range(self.width):
for y in range(self.height):
self.raw_map[(x, y)] = MapPoint(x=x, y=y)
def restore_map(self):
self.map = copy.deepcopy(self.raw_map)
for point in self.obstacle_set:
self.map[point].is_obstacle = True
# self.obstacle_set.clear()
def create_random_obstacle(self, occupied_points, create_num):
map_list = []
for point in self.map.keys():
if point not in occupied_points and not self.map[point].is_obstacle:
map_list.append(point)
create_num = min(create_num, len(map_list))
for _ in range(create_num):
point = map_list.pop(random.choice(range(len(map_list))))
self.map[point].is_obstacle = True
self.obstacle_set.add(point)
return point
def clear_obstacle(self):
while self.obstacle_set:
self.map[self.obstacle_set.pop()].is_obstacle = False
def delete_obstacle(self, points):
for point in points:
self.map[point].is_obstacle = False
if point in self.obstacle_set:
self.obstacle_set.remove(point)
return self.obstacle_set
if __name__ == '__main__':
GameMap(width=10, height=10).restore_map()
class FreePoint:
def __init__(self, **kwargs):
self.x = kwargs.get('x', 0)
self.y = kwargs.get('y', 0)
self.step = kwargs.get('step', 1)
self.speed = kwargs.get('speed', 1)
def move(self, direct):
if direct == 'up':
self.y -= self.step * self.speed
elif direct == 'down':
self.y += self.step * self.speed
elif direct == 'left':
self.x -= self.step * self.speed
elif direct == 'right':
self.x += self.step * self.speed
def check_move(self, direct):
x = self.x
y = self.y
if direct == 'up':
y = self.y - self.step * self.speed
elif direct == 'down':
y = self.y + self.step * self.speed
elif direct == 'left':
x = self.x - self.step * self.speed
elif direct == 'right':
x = self.x + self.step * self.speed
return x, y
def set_speed(self, num):
self.speed = num
def get_coordinate(self):
return self.x, self.y
if __name__ == '__main__':
test_point = FreePoint(x=2, y=3, step=2, speed=4)
print((test_point.x, test_point.y), test_point.step, test_point.speed)
# 默认初始直线代价为1,斜线代价为1.414
neighbor_cost = {}
neighbor_cost['up'] = neighbor_cost['down'] = neighbor_cost['left'] = neighbor_cost['right'] = 1
neighbor_cost['ul'] = neighbor_cost['ur'] = neighbor_cost['dl'] = neighbor_cost['dr'] = 1.414
关于我写的这个A星算法还是可以考虑下进一步的优化。
我的文底有限,关于A*寻路的原理也可以看下这一篇文章A星寻论算法解读