| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- package astar0
- import (
- "fmt"
- "sort"
- clog "github.com/cherry-game/cherry/logger"
- )
- var (
- straightCost int32 = 10 // 直线的移动成本
- diagonalCost int32 = 14 // 对角线的移动成本
- )
- type (
- Config struct {
- Width int32 // 地图宽(x)
- Height int32 // 地图高(y)
- MapData [][]int32 // 地图数据
- DirectionFunc func(node *Node) [][]int32 // 获取各方向节点函数
- IsWalkableFunc func(x, y, value int32) bool // 节点是否可走计算函数
- HeuristicFunc func(node, end *Node) int32 // 启发算法函数
- IsDebug bool // 是否为调试模式
- }
- AStar struct {
- config Config // 配置
- nodes [][]*Node // 预初始化的节点对象 x,y
- start *Node // 开始节点
- end *Node // 结束节点
- openList []*Node // 开放列表
- closeList []*Node // 关闭列表
- }
- )
- func New(config Config) *AStar {
- instance := &AStar{
- config: config,
- }
- instance.init()
- return instance
- }
- func (p *AStar) init() {
- p.nodes = make([][]*Node, p.config.Width)
- for x := int32(0); x < p.config.Width; x++ {
- p.nodes[x] = make([]*Node, p.config.Height)
- for y := int32(0); y < p.config.Height; y++ {
- p.nodes[x][y] = newNode(x, y, 0)
- }
- }
- for y := int32(0); y < int32(len(p.config.MapData)); y++ {
- for x := int32(0); x < int32(len(p.config.MapData)); x++ {
- if x > p.config.Width || y > p.config.Height {
- continue
- }
- p.nodes[x][y].value = p.config.MapData[y][x]
- }
- }
- p.config.MapData = nil
- if p.config.DirectionFunc == nil {
- p.config.DirectionFunc = Direction8Func
- }
- if p.config.IsWalkableFunc == nil {
- p.config.IsWalkableFunc = IsWalkableFunc()
- }
- if p.config.HeuristicFunc == nil {
- p.config.HeuristicFunc = ManhattanFunc
- }
- }
- func (p *AStar) SetNodeValue(x, y, value int32) {
- if x < 0 || y < 0 {
- return
- }
- if x > p.config.Width-1 || y > p.config.Height-1 {
- return
- }
- p.nodes[x][y].value = value
- }
- func (p *AStar) SetStraightCost(value int32) {
- if value > 0 {
- straightCost = value
- }
- }
- func (p *AStar) SetDiagonalCost(value int32) {
- if value > 0 {
- diagonalCost = value
- }
- }
- func (p *AStar) calcG(start, end *Node) int32 {
- // 判断移动方式是水平(或垂直)、对角,计算成本
- if start.x == end.x || start.y == end.y {
- return straightCost
- }
- return diagonalCost
- }
- func (p *AStar) calcH(start, end *Node) int32 {
- return p.config.HeuristicFunc(start, end)
- }
- func (p *AStar) FindPath(sx, sy, ex, ey int32) ([]*Node, error) {
- end, err := p.findPath(sx, sy, ex, ey)
- if err != nil {
- return nil, err
- }
- pathList := BuildPathing(end)
- p.Reset()
- return pathList, nil
- }
- func (p *AStar) CheckNodes() {
- for x, n1 := range p.nodes {
- for y := range n1 {
- node := p.nodes[x][y]
- if node.status > 0 {
- fmt.Println("status:", node)
- }
- if node.parent != nil {
- fmt.Println("parent:", node, node.parent)
- }
- }
- }
- }
- func (p *AStar) findPath(sx, sy, ex, ey int32) (*Node, error) {
- if !p.isWalkable(sx, sy) {
- return nil, ErrStartNotWalkable
- }
- if !p.isWalkable(ex, ey) {
- return nil, ErrEndNotWalkable
- }
- p.start = p.nodes[sx][sy]
- p.end = p.nodes[ex][ey]
- p.addOpenNode(p.start)
- for len(p.openList) > 0 {
- openNode := p.openList[0]
- p.openList = p.openList[1:]
- if p.isEnd(openNode) {
- return openNode, nil
- }
- nodes := p.getNodes(openNode)
- if p.config.IsDebug {
- clog.Infof("%v -> %v", openNode, nodes)
- }
- for _, node := range nodes {
- g := openNode.g + p.calcG(openNode, node)
- if !node.IsOpen() || g < node.g {
- node.g = g
- node.h = p.calcH(node, p.end)
- node.f = node.g + node.h
- node.parent = openNode
- if !node.IsOpen() {
- p.addOpenNode(node)
- }
- }
- }
- p.addCloseNode(openNode)
- sort.Slice(p.openList, func(i, j int) bool {
- return p.openList[i].f < p.openList[j].f
- })
- }
- return nil, ErrFindPathIsNil
- }
- func (p *AStar) Reset() {
- for _, node := range p.openList {
- node.Clean()
- }
- for _, node := range p.closeList {
- node.Clean()
- }
- p.start.Clean()
- p.end.Clean()
- p.start = nil
- p.end = nil
- p.openList = nil
- p.closeList = nil
- }
- func (p *AStar) addOpenNode(node *Node) {
- node.status = statusOpen
- p.openList = append(p.openList, node)
- }
- func (p *AStar) addCloseNode(node *Node) {
- node.status = statusClose
- p.closeList = append(p.closeList, node)
- }
- func (p *AStar) getNodes(node *Node) []*Node {
- var (
- list []*Node
- directionList = p.config.DirectionFunc(node)
- )
- for _, v := range directionList {
- x, y := node.x+v[0], node.y+v[1]
- if !p.isWalkable(x, y) {
- continue
- }
- tmpNode := p.nodes[x][y]
- if tmpNode.IsClose() {
- continue
- }
- list = append(list, tmpNode)
- }
- return list
- }
- func (p *AStar) CheckPoint(x, y int32) bool {
- if x < 0 || y < 0 {
- return false
- }
- if x > p.config.Width-1 || y > p.config.Height-1 {
- return false
- }
- return true
- }
- func (p *AStar) isWalkable(x, y int32) bool {
- if !p.CheckPoint(x, y) {
- return false
- }
- return p.config.IsWalkableFunc(x, y, p.nodes[x][y].value)
- }
- func (p *AStar) isEnd(node *Node) bool {
- return node.x == p.end.x && node.y == p.end.y
- }
- func (p *AStar) Print(list []*Node) {
- if len(list) < 1 {
- return
- }
- var (
- fromNode = list[0]
- toNode = list[len(list)-1]
- minX, minY, maxX, maxY = GetRectanglePoint(list)
- )
- mapText := "\n"
- mapText += fmt.Sprintf(" Rectangle: [(%d,%d) -> (%d,%d)] \n", minX, minY, maxX, maxY)
- mapText += fmt.Sprintf(" Pathing: %v\n", list)
- mapText += " Pattern: start = ◉, point = ◦, end = ●\n"
- mapText += "Coordinate: row = x, col = y\n\n"
- for y := minY; y <= maxY; y++ {
- for x := minX; x <= maxX; x++ {
- if InList(list, x, y) {
- if x == fromNode.x && y == fromNode.y {
- mapText += "◉ "
- } else if x == toNode.x && y == toNode.y {
- mapText += "● "
- } else {
- mapText += "◦ "
- }
- } else {
- mapText += fmt.Sprintf("%d ", p.nodes[x][y].value)
- }
- }
- mapText += "\n"
- }
- clog.Debug(mapText)
- }
|