【记录】python3.10 tkinter slg A星寻路


前言

最近玩了一个slg手游,发现地图不是正规的9宫格,每个点附近有6个格子,于是写了个简单的a星测试工具,万一以后有用呢。

一、代码

#!/usr/bin/python
# -*- coding: utf-8 -*-

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()

二、界面

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

archmage199

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值