golang实现障碍、转弯最少的A*寻路
目标:
- 优先寻找无障碍的路径
- 目标不可达时,寻找障碍最少的路径
- 路径长度相等时,优先转弯最少的路径
- 多个目标点时,根据以上要求到达其中一个目标点即可
要点:
- 最优格子的选取,先对open list排序,然后从open list中出队
源码:
package main
import (
"errors"
"fmt"
"math"
"sort"
)
type Node struct {
Parent *Node
Pos []int32
GDir []int32
G float32
H float32
F float32
B int // 障碍Block
T int // 转弯turn
}
func (node *Node) String() string {
return fmt.Sprintf("%#v", node)
}
// 优先障碍最少,其次路径最短,再次转弯最少
func (best *Node) IsBetterThan(node *Node) bool {
if best.B < node.B {
return true
}
if best.B > node.B {
return false
}
if best.F < node.F {
return true
}
if best.F > node.F {
return false
}
if best.T < node.T {
return true
}
if best.T > node.T {
return false
}
return false
}
type NodeList []*Node
func (nl NodeList) Len() int { return len(nl) }
func (nl NodeList) Swap(i, j int) { nl[i], nl[j] = nl[j], nl[i] }
func (nl NodeList) Less(i, j int) bool {
best, node := nl[i], nl[j]
return best.IsBetterThan(node)
}
func (nl NodeList) String() string {
s := "*******NodeList********\n"
for i, node := range nl {
s = fmt.Sprintf("%s%d = %s\n", s, i, node.String())
}
return s
}
func (nl NodeList) NodeIdx(node *Node) int {
for i, n := range nl {
if n.Pos[0] == node.Pos[0] && n.Pos[1] == node.Pos[1] {
return i
}
}
return -1
}
type AStar struct {
W int32
H int32
SPos []int32
EPos []int32
Open NodeList // 使用list,起点,终点相同时每次的路径都一样,换成map时,每次的路径可能不一样。
CloseMap map[int32]struct{}
}
const (
NODE_OPEN int32 = iota // 可通行
NODE_BARRIER // 障碍,无法到达目的地时,返回障碍最少的路径
NODE_NO_PASS // 不可通行,边界等不能通行
)
func (a *AStar) newNode(parent *Node, x, y int32, dir []int32) *Node {
state := a.GetNodeState(x, y)
if state != NODE_OPEN && state != NODE_BARRIER {
a.addClose(&Node{Pos: []int32{x, y}})
return nil
}
b, t := 0, 0
if parent != nil {
b = parent.B
if dir[0] == parent.GDir[0] && dir[1] == parent.GDir[1] {
t = parent.T
} else {
t = parent.T + 1
}
}
if state == NODE_BARRIER {
b++
}
pos := []int32{x, y}
g := a.getG(pos, parent)
h := a.getH(pos, a.EPos)
return &Node{
Parent: parent,
Pos: pos,
G: g,
H: h,
F: g + h,
B: b,
T: t,
GDir: dir,
}
}
func (a *AStar) GetNodeState(x, y int32) int32 {
if x < 0 || x >= a.W || y < 0 || y >= a.H {
return NODE_NO_PASS
}
//fmt.Println("GetNodeState", x, y, GMap[x][y])
return GMap[x][y]
}
// 计算g值;直走=1;斜走=1.4
func (a *AStar) getG(pos1 []int32, parent *Node) float32 {
if parent == nil {
return 0
}
pos2 := parent.Pos
g := parent.G
if pos1[0] == pos2[0] {
return g + float32(math.Abs(float64(pos1[1]-pos2[1])))
} else if pos1[1] == pos2[1] {
return g + float32(math.Abs(float64(pos1[0]-pos2[0])))
} else {
return g + float32(math.Abs(float64(pos1[0]-pos2[0])*1.4))
}
}
// 计算h值
func (a *AStar) getH(pos1, pos2 []int32) float32 {
return float32(math.Abs(float64(pos1[0]-pos2[0])) + math.Abs(float64(pos1[1]-pos2[1])))
}
func (a *AStar) addOpen(node *Node) {
//idx := a.getNodeIdxInOpen(node)
idx := a.Open.NodeIdx(node)
//fmt.Println(a.Open)
//fmt.Println("addOpen:", idx, "node:", node, "patrnt ", node.Parent)
if idx >= 0 {
n := a.Open[idx]
if node.IsBetterThan(n) {
a.Open[idx] = node
}
} else {
a.Open = append(a.Open, node)
}
}
// x, y 对应的node在open中的idx
func (a *AStar) getNodeIdxInOpen(node *Node) int {
x, y := node.Pos[0], node.Pos[1]
l := len(a.Open)
if l == 0 {
return -1
}
idx := sort.Search(len(a.Open),
func(i int) bool {
pos := a.Open[i].Pos
return pos[0] == x && pos[1] == y
})
// sort.Search 对已排序的list使用二分查找,
// sort.Search 的坑,未找到返回的值等于list长度
if idx == l {
return -1
}
return idx
}
// 从open中出栈,即最优的格子
func (a *AStar) getMinNode() *Node {
sort.Sort(a.Open)
node := a.Open[0]
a.Open = a.Open[1:]
return node
}
// 添加到close中,
func (a *AStar) addClose(n *Node) {
x, y := n.Pos[0], n.Pos[1]
key := x*a.W*10 + y
a.CloseMap[key] = struct{}{}
}
// 判断是否在close中
func (a *AStar) isInClose(x, y int32) (ok bool) {
key := x*a.W*10 + y
_, ok = a.CloseMap[key]
return
}
// 拓展周边方向的node
func (a *AStar) extendNeighbours(c *Node) {
for _, dir := range GDir {
x := c.Pos[0] + dir[0]
y := c.Pos[1] + dir[1]
if a.isInClose(x, y) {
continue
}
node := a.newNode(c, x, y, dir)
if node == nil {
continue
}
a.addOpen(node)
}
}
func (a *AStar) isTarget(n *Node, ePos []int32) bool {
if n == nil {
return false
}
if n.Pos[0] == ePos[0] && n.Pos[1] == ePos[1] {
return true
}
return false
}
// 从结束点回溯到开始点,开始点的parent == None
func (a *AStar) makePath(p *Node) [][]int32 {
path := make([][]int32, 0)
for p != nil {
path = append([][]int32{p.Pos}, path[:]...)
p = p.Parent
}
fmt.Println("********makePath:", path)
return path
}
// 路径查找主函数
func (a *AStar) findPath(sPos, ePos []int32) (path [][]int32, block, turn int, err error) {
state := a.GetNodeState(sPos[0], sPos[1])
if state != NODE_OPEN && state != NODE_BARRIER {
err = errors.New(fmt.Sprintf("spos state is %d", state))
return
}
estate := a.GetNodeState(ePos[0], ePos[1])
if estate != NODE_OPEN && estate != NODE_BARRIER {
err = errors.New(fmt.Sprintf("ePos state is %d", estate))
return
}
a.SPos, a.EPos = sPos, ePos
// 构建开始节点
s := a.newNode(nil, sPos[0], sPos[1], []int32{0, 0})
a.addOpen(s)
for {
if len(a.Open) <= 0 {
err = errors.New("not find Open list is nil")
return
}
p := a.getMinNode()
//fmt.Println(fmt.Sprintf("---min p = %#v", p))
if a.isTarget(p, ePos) {
path = a.makePath(p)
block, turn = p.B, p.T
return
}
a.extendNeighbours(p)
a.addClose(p)
}
return
}
// 查询过的所有格子
func (a *AStar) getSearched() []int32 {
l := make([]int32, 0)
for _, i := range a.Open {
if i != nil {
l = append(l, i.Pos[0], i.Pos[1])
}
}
for k, _ := range a.CloseMap {
x := k / a.W * 10
y := k % a.W * 10
l = append(l, x, y)
}
return l
}
// 清空open和close
func (a *AStar) clean() {
a.Open = make(NodeList, 0)
a.CloseMap = make(map[int32]struct{})
}
func NewAstar() *AStar {
a := &AStar{
W: int32(len(GMap)),
H: int32(len(GMap[0])),
Open: make(NodeList, 0),
CloseMap: make(map[int32]struct{}),
}
fmt.Println("NewAstar", a.W, a.H)
return a
}
// 终点不唯一时找到最优的路径
func FindPathIgnoreBlock(sPos []int32, ePos [][]int32) {
a := NewAstar()
if a == nil {
return
}
var path [][]int32
var block, turn int
for _, e := range ePos {
a.clean()
p, b, t, err := a.findPath(sPos, e)
fmt.Println("FindPathIgnoreBlock:",
fmt.Sprint("sPos", sPos),
fmt.Sprint("ePos", e),
fmt.Sprint("path", p),
fmt.Sprint("Block", b),
fmt.Sprint("turn", t),
fmt.Sprint("err", err))
if err != nil {
continue
}
if path == nil {
path, block, turn = p, b, t
continue
}
stepNum, newStep := len(path), len(p)
fmt.Println("stepNum", stepNum, "newStep", newStep)
if stepNum < newStep {
continue
}
if stepNum > newStep {
path, block, turn = p, b, t
continue
}
if block < b {
continue
}
if block > b {
path, block, turn = p, b, t
continue
}
if turn < t {
continue
}
if turn > b {
path, block, turn = p, b, t
continue
}
}
fmt.Println("********end, FindPathIgnoreBlock", path, block, turn)
}
var (
GDir [][]int32 //方向
GMap [][]int32 //地图
)
func init() {
GDir = [][]int32{{1, 0}, {0, 1}, {0, -1}, {-1, 0}}
GMap = [][]int32{
{0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 2, 2, 2, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 2, 1, 0, 0, 1, 0, 2, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
{0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0},
}
fmt.Println("----init:", len(GMap[0]), len(GMap), NODE_OPEN, NODE_BARRIER, NODE_NO_PASS)
}
func main() {
FindPathIgnoreBlock([]int32{3, 2}, [][]int32{{5, 3}})
}
golang实现障碍、转弯最少的A*寻路的更多相关文章
- BZOJ 4723: [POI2017]Flappy Bird
Description 从一个点到一条直线,每次纵坐标只能增加或减少1,有些位置有障碍,求最少增加步数. Sol 贪心. 或许是贪心吧...反正在可到达的范围内,纵坐标尽量小... 做的时候维护一下两 ...
- POJ 3041 Asteroids (最小点覆盖集)
题意 给出一个N*N的矩阵,有些格子上有障碍,要求每次消除一行或者一列的障碍,最少消除多少次可以全部清除障碍. 思路 把关键点取出来:一个障碍至少需要被它的行或者列中的一个消除. 也许是最近在做二分图 ...
- hdu 1728 逃离迷宫(dFS+优先队列)
求转弯最少的走路方式!!!! #include<stdio.h> #include<string.h> #include<queue> using namespac ...
- POJ-3041 Asteroids---二分图&最小覆盖点
题目链接: https://vjudge.net/problem/POJ-3041 题目大意: 给一个N*N的矩阵,有些格子有障碍,要求我们消除这些障碍,问每次消除一行或一列的障碍, 最少要几次. 解 ...
- [POJ3041] Asteroids(最小点覆盖-匈牙利算法)
传送门 题意: 给一个N*N的矩阵,有些格子有障碍,要求我们消除这些障碍,问每次消除一行或一列的障碍,最少要几次. 解析: 把每一行与每一列当做二分图两边的点. 某格子有障碍,则对应行与列连边. ...
- leetcode_1293. Shortest Path in a Grid with Obstacles Elimination_[dp动态规划]
题目链接 Given a m * n grid, where each cell is either 0 (empty) or 1 (obstacle). In one step, you can m ...
- 【bfs】最少转弯问题
题目描述 给出一张地图,这张地图被分为n×m(n,m<=100)个方块,任何一个方块不是平地就是高山.平地可以通过,高山则不能.现在你处在地图的(x1,y1)这块平地,问:你至少需要拐几个弯才能 ...
- 69.广搜练习: 最少转弯问题(TURN)
[问题描述] 给出一张地图,这张地图被分为n×m(n,m<=100)个方块,任何一个方块不是平地就是高山.平地可以通过,高山则不能.现在你处在地图的(x1,y1)这块平地,问:你至少需要拐几个弯 ...
- 【a802】最少转弯问题
Time Limit: 10 second Memory Limit: 2 MB 问题描述 给出一张地图,这张地图被分为n*m(n,m<=100)个方块,任何一个方块不是平地就是高山.平地可以通 ...
随机推荐
- [Deep Learning] 正则化
在总结正则化(Regularization)之前,我们先谈一谈正则化是什么,为什么要正则化. 个人认为正则化这个字眼有点太过抽象和宽泛,其实正则化的本质很简单,就是对某一问题加以先验的限制或约束以达到 ...
- Go语言入门: Chapter1
书籍官网: http://www.gopl.io 环境配置: https://studygolang.com/articles/8284 安装go和vscode中go的相关插件 主要命令学习: go ...
- 如何用ps简单快速扣头发丝
好久不用PS抠图,今天接到一个小任务,换背景,以前一直用通道的办法,但用通道比较费劲,发现一个更简单的办法,就是用快速蒙版+调整边缘. 这张是原图: 1.先用快速蒙版制作选取(Q) 再按Q,退出快速蒙 ...
- 在Windows2008r2 安装.net4.5
WebApi 是比较高的环境下面开发 需要的环境是net4.5 或以上. Windows2008r2 里面没有这个环境必须自己安装.安装net4.5 必须sp1环境以上才能安装. 将Windows20 ...
- 【作业3.0】HansBug的第三次博客规格总结
转眼间第三次作业了,似乎需要说点啥,那就说点. 规格&工业 说到这个,不得不提一下软件开发的发展史. 历史的进程 早在上世纪50年代,就已经有早期的编程语言出现,也开始有一些程序编写者出现(多 ...
- # 20175333曹雅坤《Java程序设计》第五周学习总结
教材学习内容总结 第六章要点: 1.接口:1)接口声明: interface //接口的名字 2)接口体 2.实现接口:类实现接口:一个类需要在类声明中使用关键字implements声明该类实现一个或 ...
- Charles+iPhone配置ssl证书
Charles+iPhone配置ssl证书 一.手机 1. 配置代理 设置->无线局域网->选和电脑同一网络的无线->配置代理->手动 服务器配置电脑的IP,端口设置为8888 ...
- gym 102059A 树链剖分后odt维护区间
题意 一棵树 多次修改,每次修改一个点到根的所有边的颜色,并询问现在有哪些颜色染了恰好$m$条边 题解: 稍加思考可以知道,从某个点到根节点的颜色数,均摊复杂度很低,因此,可以考虑珂朵莉树维护重链剖分 ...
- Lesson 2-1 (数据结构,序列通用的操作)
2.0 数据结构 --- 数据结构是以某种方式组合起来的数据元素集合. --- python的常见的数据结构 2.1 序列(sequence) --- 序列中的每个元素都有编号,即索引(也称为下标). ...
- CentOS7没有eth0网卡
本人刚刚进去运维圈,写写博客,记录一下自己日常工作学习中的各种问题,方便自己,方便他人. CentOS7系统安装完毕之后,输入ifconfig命令发现没有eth0,不符合我们的习惯.而且也无法远程ss ...