前言
最近玩了一个slg手游,发现地图不是正规的9宫格,每个点附近有6个格子,于是写了个简单的a星测试工具,万一以后有用呢。
一、代码
import time
from functools import partial
from tkinter import Tk, Canvas, Radiobutton, IntVar, Button, Checkbutton
class MapGrid(Canvas):
def __init__(self, index, x, y, can_move, master, cnf={}, **kw):
Canvas.__init__(self, master, cnf, **kw)
self.index = index
self.x = x
self.y = y
self.can_move = can_move
class StarPointNode:
def __init__(self, point):
self.point = point
self.F = 0
self.G = 0
self.H = 0
self.iter_x = 0
self.iter_y = 0
self.parent = None
def __iter__(self):
self.iter_x = self.point.x - 10
self.iter_y = self.point.y + 5
self.iter_index = 1
return self
def __next__(self):
if self.iter_index == 7:
raise StopIteration
r_x, r_y = self.iter_x, self.iter_y
match self.iter_index:
case 1:
self.iter_y -= 10
case 2:
self.iter_x += 10
self.iter_y += 15
case 3:
self.iter_y -= 20
case 4:
self.iter_x += 10
self.iter_y += 15
case 5:
self.iter_y -= 10
self.iter_index += 1
return (r_x, r_y)
class Map:
def __init__(self, master):
self.master = master
self.width = 18
self.height = 5
self.unit = 100
self.start_y = 70
self.grids = {}
self.radio_group = IntVar()
self.start_grid = None
self.end_grid = None
self.init_main_frame()
self.init_map_grid()
self.init_btn()
def init_main_frame(self):
pos_x, pos_y = 0, 0
pixel_width = self.width * (self.unit + 4)
pixel_height = self.height * (self.unit + 4) * 2 + 70
self.master.title("map")
self.master.geometry(f"{pixel_width}x{pixel_height}+{pos_x}+{pos_y}")
def init_map_grid(self):
pixel_width = self.width * (self.unit + 4)
x = pixel_width - self.unit - 4
for i in range(self.width):
y = self.start_y
for j in range(self.height):
index = self.get_point_index((j*20.0, i*10.0))
grid = MapGrid(index, j*20.0, i*10.0, True, self.master)
grid.configure(bg="white", width=self.unit, height=self.unit)
grid.place(x=x, y=y)
grid.bind("<Button-1>", partial(self.on_grid_click, grid))
grid.create_oval(40, 40, 60, 60, fill="red", state="hidden", tag="center")
grid.create_text(1, 8, tag="F", text=0, anchor="w", state="hidden")
grid.create_text(1, 92, tag="G", text=0, anchor="w", state="hidden")
grid.create_text(93, 92, tag="H", text=0, anchor="e", state="hidden")
self.grids[index] = grid
y += (self.unit + 4) * 2
x -= self.unit + 4
x = pixel_width - self.unit - 4 - self.unit / 2
for i in range(self.width-1):
y = self.start_y + self.unit + 4
for j in range(self.height):
index = self.get_point_index(((j+1)*20.0-10, (i+0.5)*10.0))
grid = MapGrid(index, (j+1)*20.0-10, (i+0.5)*10, True, self.master)
grid.configure(bg="white", width=self.unit, height=self.unit)
grid.place(x=x, y=y)
grid.bind("<Button-1>", partial(self.on_grid_click, grid))
grid.create_oval(40, 40, 60, 60, fill="red", state="hidden", tag="center")
grid.create_text(8, 8, tag="F", text=0, anchor="w", state="hidden")
grid.create_text(8, 92, tag="G", text=0, anchor="w", state="hidden")
grid.create_text(92, 92, tag="H", text=0, anchor="e", state="hidden")
self.grids[index] = grid
y += (self.unit + 4) * 2
x -= self.unit + 4
def on_grid_click(self, grid, event):
group = self.radio_group.get()
if group == 1:
if grid.can_move:
grid.configure(bg="blue")
grid.can_move = False
else:
grid.configure(bg="white")
grid.can_move = True
elif group == 2 and grid.can_move:
if self.start_grid is grid:
grid.configure(bg="white")
self.start_grid = None
else:
if self.start_grid is not None:
self.start_grid.configure(bg="white")
self.start_grid = grid
grid.configure(bg="green")
elif group == 3 and grid.can_move:
if self.end_grid is grid:
grid.configure(bg="white")
self.end_grid = None
else:
if self.end_grid is not None:
self.end_grid.configure(bg="white")
self.end_grid = grid
grid.configure(bg="red")
def init_btn(self):
block_btn = Radiobutton(self.master, name="block", text="set_block", variable=self.radio_group, value=1)
start_btn = Radiobutton(self.master, name="start", text="set_start", variable=self.radio_group, value=2)
end_btn = Radiobutton(self.master, name="end", text="set_end", variable=self.radio_group, value=3)
block_btn.place(x=10, y=20)
start_btn.place(x=200, y=20)
end_btn.place(x=400, y=20)
block_btn.after(1, block_btn.select)
a_star_btn = Button(self.master, name="a_star", text="开始寻路")
a_star_btn.bind("<Button-1>", self.on_a_star_click)
a_star_btn.place(x=580, y=20)
reset_btn = Button(self.master, name="clear", text="清空")
reset_btn.bind("<Button-1>", self.on_clear_click)
reset_btn.place(x=650, y=20)
def on_a_star_click(self, event):
if self.start_grid is None or self.end_grid is None:
return
self.a_star((self.start_grid.x, self.start_grid.y), (self.end_grid.x, self.end_grid.y))
def on_clear_click(self, event):
self.reset()
def get_point_index(self, point):
x, y = point
return f"{x}_{y}"
def get_point(self, point):
index = self.get_point_index(point)
if index is None:
return None
if index not in self.grids:
return None
return self.grids[index]
def reset(self):
for point in self.grids.values():
point.configure(highlightthickness=0)
point.itemconfig("center", state="hidden")
point.itemconfig("F", state="hidden")
point.itemconfig("G", state="hidden")
point.itemconfig("H", state="hidden")
def a_star(self, src, dst):
point_src = self.get_point(src)
point_dst = self.get_point(dst)
self.reset()
open_list = {}
close_list = {}
star_point_src = StarPointNode(point_src)
open_list[point_src.index] = star_point_src
find_count = 0
is_find = True
while point_dst.index not in open_list:
if len(open_list) <= 0:
is_find = False
break
sort_list = sorted(open_list.keys(), key=lambda x: open_list[x].F)
star_point_next = open_list.pop(sort_list[0])
star_point_next.point.configure(highlightcolor="#1500ff", highlightbackground="#1500ff", highlightthickness=1)
close_list[star_point_next.point.index] = star_point_next
find_count += 1
for neighbor in star_point_next:
point = self.get_point(neighbor)
if point is None or not point.can_move or point.index in close_list:
continue
offset = 10 if point.y == star_point_next.point.y or point.x == star_point_next.point.x else 11
G = star_point_next.G + offset
if point.index not in open_list:
star_point = StarPointNode(point)
star_point.parent = star_point_next
star_point.G = G
star_point.H = (abs(point_dst.y - point.y) + abs(point_dst.x - point.x))
star_point.F = star_point.G + star_point.H
open_list[point.index] = star_point
else:
star_point = open_list[point.index]
point.itemconfig("F", state="normal", text=str(star_point.F))
point.itemconfig("G", state="normal", text=str(star_point.G))
point.itemconfig("H", state="normal", text=str(star_point.H))
point.configure(highlightbackground="green", highlightthickness=1)
if G >= star_point.G:
continue
star_point.parent = star_point_next
star_point.G = star_point_next.G + offset
star_point.F = star_point.G + star_point.H
point.itemconfig("F", text=str(star_point.F))
point.itemconfig("G", text=str(star_point.G))
if is_find:
star_point = open_list[point_dst.index]
path = "Path: " + str(star_point.point.x) + "_" + str(star_point.point.y) + "<-"
while not (star_point.point.x == point_src.x and star_point.point.y == point_src.y):
star_point = close_list[star_point.parent.point.index]
path += str(star_point.point.x) + "_" + str(star_point.point.y) + "<-"
star_point.point.itemconfig("center", state="normal")
star_point.point.configure(highlightbackground="yellow", highlightthickness=1)
path = path.rstrip("<-")
print(f"{path} FindTimes:{find_count}")
else:
print("没找到路径")
def main():
root = Tk()
Map(root)
root.mainloop()
if __name__ == "__main__":
main()
二、界面
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/1d1972129f8e9423f03199441e31556a.png)