【算法】A*改进算法

目的:我这里希望实现一个java A* 游戏里的战斗寻径

 

定义部分: 这个定义引用自 http://www.cnblogs.com/kanego/archive/2011/08/30/2159070.html

这个伪代码说的很详细

如下的状态空间:(起始位置是A,目标位置是P,字母后的数字表示节点的估价值) 

    搜索过程中设置两个表:OPEN和CLOSED。OPEN表保存了所有已生成而未考察的节点,CLOSED 表中记录已访问过的节点。算法中有一步是根据估价函数重排OPEN表。这样循环中的每一 步只考虑OPEN表中状态最好的节点。具体搜索过程如下: 

    1)初始状态: 
    OPEN=[A5];CLOSED=[]; 
    2)估算A5,取得搜有子节点,并放入OPEN表中; 
    OPEN=[B4,C4,D6];CLOSED=[A5] 
    3)估算B4,取得搜有子节点,并放入OPEN表中; 
    OPEN=[C4,E5,F5,D6];CLOSED=[B4,A5] 
    4)估算C4;取得搜有子节点,并放入OPEN表中; 
    OPEN=[H3,G4,E5,F5,D6];CLOSED=[C4,B4,A5] 
    5)估算H3,取得搜有子节点,并放入OPEN表中; 
    OPEN=[O2,P3,G4,E5,F5,D6];CLOSED=H3C4,B4,A5] 
    6)估算O2,取得搜有子节点,并放入OPEN表中; 
    OPEN=[P3,G4,E5,F5,D6];CLOSED=[O2,H3,C4,B4,A5] 
    7)估算P3,已得到解; 
    看了具体的过程,再看看伪程序吧。算法的伪程序如下: 

 1 关于A*算法 伪代码
 2 Best_First_Search()
 3 {
 4  Open   =   [起始节点];
 5  Closed   =   [];
 6  while   (Open表非空)
 7  {
 8   从Open中取得一个节点X,并从OPEN表中删除。
 9   if   (X是目标节点)
10   {
11    求得路径PATH;
12    返回路径PATH;
13   }
14   for   (每一个X的子节点Y)
15   {
16    if   (Y不在OPEN表和CLOSE表中)
17    {
18     求Y的估价值;
19     并将Y插入OPEN表中;
20    }
21    //还没有排序
22    else   if   (Y在OPEN表中)
23    {
24     if   (Y的估价值小于OPEN表的估价值)
25      更新OPEN表中的估价值;
26    }
27    else   //Y在CLOSE表中
28    {
29     if   (Y的估价值小于CLOSE表的估价值)
30     {
31      更新CLOSE表中的估价值;
32      从CLOSE表中移出节点,并放入OPEN表中;
33     }
34    }
35    将X节点插入CLOSE表中;
36    按照估价值将OPEN表中的节点排序;
37   }//end   for
38  }//end   while
39 }//end   func 

 

看看java的A*改进在游戏的战斗里怎么搞

  1 package ...combat.service.impl;
  2 
  3 /*    
  4  * A* algorithm implementation.
  5  * Copyright (C) 2007, 2009 Giuseppe Scrivano <gscrivano@gnu.org>
  6 
  7  * This program is free software; you can redistribute it and/or modify
  8  * it under the terms of the GNU General Public License as published by
  9  * the Free Software Foundation; either version 3 of the License, or
 10  * (at your option) any later version.
 11 
 12  * This program is distributed in the hope that it will be useful,
 13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15  * GNU General Public License for more details.
 16 
 17  * You should have received a copy of the GNU General Public License along
 18  * with this program; if not, see <http://www.gnu.org/licenses/>.
 19  */
 20 
 21 import java.util.HashMap;
 22 import java.util.LinkedList;
 23 import java.util.List;
 24 import java.util.PriorityQueue;
 25 
 26 import org.slf4j.Logger;
 27 import org.slf4j.LoggerFactory;
 28 
 29 /*
 30  * 网上找的AStar寻路算法改编版
 31  * 
 32  * 地图的Y轴必须在100以内,point的hashCode
 33  */
 34 public class PathFinder {
 35     protected static final Logger log = LoggerFactory
 36             .getLogger(PathFinder.class);
 37     private static final int MAXLOOPCOUNT = 4000;
 38 
 39     public static final byte MAP_FLAG_NONE = 0;//可以走
 40     public static final byte MAP_FLAG_ONE = 1;//攻击方区域
 41     public static final byte MAP_FLAG_TWO = 2;//防守方区域
 42     public static final byte MAP_FLAG_THREE = 3;//不可走的障碍区域
 43     /**
 44      * 0表示没有障碍的点 , 1,2都是障碍点,行走者自身包含1或者2的属性,与自身属性相同的表示可穿过不可停留的点 ,不同表示不可穿越不可停留的点
 45      * 3表示不可移动到的点
 46      */
 47     private byte[][] map;//地图
 48 
 49     private Node goal;//目标点
 50     private PriorityQueue<Path> paths;//路径优先级队列
 51     private HashMap<Integer, Integer> mindists;
 52     /*
 53      * 目标范围,表示距离目标多少个格子就算到达目标
 54      */
 55     private int goalRange;
 56 
 57     //private int maxStep;// 移动的最大距离
 58 
 59     private byte mapFlag;// 1或者2
 60     // private double lastCost;
 61     private int expandedCounter;
 62 
 63     private int xstep = 1;
 64     private int ystep = 1;
 65 
 66     public PathFinder(byte[][] map) {
 67         this.map = map;
 68         paths = new PriorityQueue<Path>();
 69         mindists = new HashMap<Integer, Integer>();
 70         expandedCounter = 0;
 71     }
 72     //goalRange 攻击范围   maxStep移动范围   mapFlag本方属性1or2
 73     public List<Node> getAtkPath(Node start, Node goal, int goalRange,
 74             int maxStep, byte mapFlag) {
 75         Path path = computeAtkPath(start, goal, goalRange, maxStep, mapFlag);
 76         this.clean();
 77         if (path != null) {
 78             return path.getNodes();
 79         } else {
 80             return null;
 81         }
 82     }
 83 
 84     // 这是为城墙单独做的一个方法,主要是目前寻路,不支持一个目标站多个位置
 85     public List<Node> getAtkPath(Node start, List<Node> goals, int goalRange,
 86             int maxStep, byte mapFlag) {
 87         Path bestP = null;
 88         for (Node goal : goals) {
 89             Path path = computeAtkPath(start, goal, goalRange, maxStep, mapFlag);
 90             this.clean();
 91             if (bestP == null) {
 92                 bestP = path;
 93             } else {
 94                 if (path != null && path.depth < bestP.depth) {
 95                     bestP = path;
 96                 }
 97             }
 98         }
 99         if (bestP != null) {
100             return bestP.getNodes();
101         } else {
102             return null;
103         }
104 
105     }
106 
107     /**
108      * 寻找攻击路线
109      * 
110      * @param start
111      * @param goal
112      * @param goalRange 攻击范围
113      * @param maxStep 移动力【移动范围】
114      * @param mapFlag 移动方阵营,如果移动到的位置不为0,但是与本方的阵营相同,可以穿过去
115      * @return
116      */
117     public Path computeAtkPath(Node start, Node goal, int goalRange,
118             int maxStep, byte mapFlag) {
119         if (goal != null) {
120             this.setGoal(goal);
121         }
122         if (goalRange > 0) {
123             this.goalRange = goalRange;
124         }
125         // if (maxStep>0){
126         //this.maxStep = maxStep;
127         // }
128         if (mapFlag != MAP_FLAG_ONE && mapFlag != MAP_FLAG_TWO) {
129             this.mapFlag = MAP_FLAG_ONE;
130         } else {
131             this.mapFlag = mapFlag;
132         }
133 
134         if (start.x > goal.x) {
135             xstep = -1;
136         } else {
137             xstep = 1;
138         }
139 
140         if (start.y > goal.y) {
141             ystep = -1;
142         } else {
143             ystep = 1;
144         }
145 
146         try {
147 
148             Path root = new Path();
149             root.setPoint(start);
150 
151             if (isGoal(start) && (map[start.x][start.y] != MAP_FLAG_ONE)) {// 不需要移动,当前点就在目标点范围内
152                 return root;
153             }
154             /* Needed if the initial point has a cost. */
155             f(root, start, start);
156 
157             expand(root);
158 
159             for (int j = 0; j < MAXLOOPCOUNT; j++) {
160                 Path p = paths.poll();
161 
162                 if (p == null) {
163                     return null;
164                 }
165 
166                 if (p.f < 0) {// 该路径不可达,必然有敌方阻挡,则有更合适的目标,放弃当前目标
167                     continue;
168                 }
169 
170                 Node last = p.getPoint();
171 
172                 if (isGoal(last)) {
173                     // 停留点不是友军已经停留位置
174                     if (map[last.x][last.y] == MAP_FLAG_NONE) {
175                         return p;
176                     }
177                 }
178                 if (p.depth < maxStep) {// 超过最大步数
179                     expand(p);
180                 }
181 
182             }
183         } catch (Exception e) {
184             e.printStackTrace();
185         } finally {
186             // if (log.isDebugEnabled()){
187             // log.debug("尝试的节点数:{}"+this.getExpandedCounter());
188             // }
189         }
190         return null;
191 
192     }
193 
194     /**
195      * 
196      * 移动目标未必可达,只是找可移动到的离目标最近的点
197      * 
198      * @param start
199      * @param goal
200      * @param goalRange
201      *            目标周围goalRange-1格都作为目标
202      * @param maxStep
203      *            移动步数超过maxStep的忽略
204      * @param mapFlag
205      * @return
206      */
207     public List<Node> getMovePath(Node start, Node goal, int goalRange,
208             int maxStep, byte mapFlag) {
209         Path path = computeMovePath(start, goal, goalRange, maxStep, mapFlag);
210         this.clean();
211         if (path != null) {
212             return path.getNodes();
213         } else {
214             return null;
215         }
216     }
217 
218     // 这是为城墙单独做的一个方法,主要是目前寻路,不支持一个目标站多个位置
219     public List<Node> getMovePath(Node start, List<Node> goals, int goalRange,
220             int maxStep, byte mapFlag) {
221         Path bestP = null;
222         for (Node goal : goals) {
223             Path path = computeMovePath(start, goal, goalRange, maxStep,
224                     mapFlag);
225             this.clean();
226             if (bestP == null) {
227                 bestP = path;
228             } else {
229                 if (path != null && path.depth < bestP.depth) {
230                     bestP = path;
231                 }
232             }
233         }
234         if (bestP != null) {
235             return bestP.getNodes();
236         } else {
237             return null;
238         }
239 
240     }
241 
242     public Path computeMovePath(Node start, Node goal, int goalRange,
243             int maxStep, byte mapFlag) {
244 
245         Path bestPath = null;// 最佳路径
246 
247         if (goal != null) {
248             this.setGoal(goal);
249         }
250         if (goalRange > 0) {
251             this.goalRange = goalRange;
252         }
253         // if (maxStep>0){
254         // this.maxStep = maxStep;
255         // }
256         if (mapFlag != MAP_FLAG_ONE && mapFlag != MAP_FLAG_TWO) {
257             this.mapFlag = MAP_FLAG_ONE;
258         } else {
259             this.mapFlag = mapFlag;
260         }
261 
262         if (start.x > goal.x) {
263             xstep = -1;
264         } else {
265             xstep = 1;
266         }
267 
268         if (start.y > goal.y) {
269             ystep = -1;
270         } else {
271             ystep = 1;
272         }
273 
274         try {
275 
276             Path root = new Path();
277             root.setPoint(start);
278 
279             if (isGoal(start) && (map[start.x][start.y] != MAP_FLAG_ONE)) {// 不需要移动,当前点就在目标点范围内
280                 return root;
281             }
282             /* Needed if the initial point has a cost. */
283             f(root, start, start);
284 
285             expand(root);
286             int j=0;
287             for (j = 0; j < MAXLOOPCOUNT; j++) {
288                 Path p = paths.poll();
289 
290                 if (p == null) {
291                     //System.out.println("=========is null=================");
292                     break;
293                 }
294 
295                 if (p.f < 0) {// 该路径不可达,必然有敌方阻挡,则有更合适的目标,放弃当前目标
296                     continue;
297                 }
298 
299                 if (p.depth == maxStep) {
300                     // 如果前maxstep都不能站立,则放弃该路径
301                     boolean valid = false;
302                     for (Path i = p; i != null && i.parent != null; i = i.parent) {
303                         if (i.depth <= maxStep) {
304                             Node tmp = i.getPoint();
305                             if (map[tmp.x][tmp.y] == MAP_FLAG_NONE) {
306                                 valid = true;
307                                 break;
308                             }
309                         }
310                     }
311 
312                     if (!valid) {
313                         continue;
314                     }
315                 }
316 
317                 Node last = p.getPoint();
318                 // 是目标,并且不是友军站住的位置
319                 if (isGoal(last) && map[last.x][last.y] != MAP_FLAG_ONE) {
320                     if (map[last.x][last.y] == MAP_FLAG_NONE) {
321                         for (Path i = p; i != null && i.parent != null; i = i.parent) {
322                             if (i.depth <= maxStep) {
323                                 Node tmp = i.getPoint();
324                                 if (map[tmp.x][tmp.y] == MAP_FLAG_NONE) {
325                                     return i;// 只返回能走的最大步数
326                                 }
327                             }
328                         }
329 
330                     }
331                 }
332 
333                 // 保留不可达路径中与目标点最近的路径
334                 if (map[last.x][last.y] == MAP_FLAG_NONE) {
335                     if (bestPath == null) {
336                         bestPath = p;
337                     } else if (p.depth <= maxStep) {
338                         Node bestNode = bestPath.getPoint();
339                         if (Math.abs(last.x - goal.x)
340                                 + Math.abs(last.y - goal.y) < Math
341                                 .abs(bestNode.x - goal.x)
342                                 + Math.abs(bestNode.y - goal.y)) {
343                             bestPath = p;
344                         }
345                     }
346                 }
347                 expand(p);
348 
349             }
350             //System.out.println("=========================="+j);
351         } catch (Exception e) {
352             e.printStackTrace();
353         } finally {
354             // if (log.isDebugEnabled()){
355             // log.debug("尝试的节点数:{}"+this.getExpandedCounter());
356             // }
357         }
358 
359         return bestPath;
360 
361     }
362 
363     public int getExpandedCounter() {
364         return expandedCounter;
365     }
366 
367     protected int g(Node from, Node to) {
368 
369         if (from.x == to.x && from.y == to.y)
370             return 0;
371         if (map[to.x][to.y] == MAP_FLAG_NONE || map[to.x][to.y] == mapFlag)
372             return 1;
373         if (isGoal(to))
374             return 1;
375 
376         return Integer.MIN_VALUE;// 敌方阻挡位置,设置为负值,表示不可到达
377     }
378 
379     protected int h(Node from, Node to) {
380         /* Use the Manhattan distance heuristic. */
381         // return new Double(Math.abs(map[0].length - 1 - to.x)
382         // + Math.abs(map.length - 1 - to.y));
383         return Math.abs(goal.x - to.x) + Math.abs(goal.y - to.y);
384     }
385 
386     protected int f(Path p, Node from, Node to) {
387         int g = g(from, to) + ((p.parent != null) ? p.parent.g : 0);
388         int h = h(from, to);
389 
390         p.g = g;
391         p.f = g + h;
392 
393         return p.f;
394     }
395 
396     private void expand(Path path) {
397         Node p = path.getPoint();
398         Integer min = mindists.get(path.getPoint().hashCode());
399 
400         /*
401          * If a better path passing for this point already exists then don't
402          * expand it.
403          */
404         if (min == null || min.intValue() > path.f)
405             mindists.put(path.getPoint().hashCode(), path.f);
406         else
407             return;
408 
409         List<Node> successors = generateSuccessors(p);
410 
411         for (Node t : successors) {
412             if (!mindists.containsKey(t.hashCode())) {
413                 Path newPath = new Path(path);
414                 newPath.setPoint(t);
415                 f(newPath, path.getPoint(), t);
416                 // System.out.println(newPath.toString());
417                 paths.offer(newPath);
418             }
419         }
420 
421         expandedCounter++;
422     }
423 
424     protected Node getGoal() {
425         return this.goal;
426     }
427 
428     protected void setGoal(Node node) {
429         this.goal = node;
430 
431     }
432 
433     protected boolean isGoal(Node node) {
434         if (goalRange <= 0) {
435             return (node.x == goal.x) && (node.y == goal.y);
436         } else {
437             return (Math.abs(goal.x - node.x) + Math.abs(goal.y - node.y) <= goalRange);
438         }
439     }
440 
441     protected boolean isGoal(int x, int y) {
442         if (goalRange <= 0) {
443             return (x == goal.x) && (y == goal.y);
444         } else {
445             return (Math.abs(goal.x - x) + Math.abs(goal.y - y) <= goalRange);
446         }
447     }
448 
449     protected boolean isBarrier(int x, int y) {
450         return (map[x][y] != MAP_FLAG_NONE && map[x][y] != mapFlag);
451     }
452 
453     protected List<Node> generateSuccessors(Node node) {
454         List<Node> ret = new LinkedList<Node>();
455         int x = node.x;
456         int y = node.y;
457 
458         // 先遍历X
459         int tmp = x + xstep;
460         if (tmp < map.length && tmp >= 0 && !isBarrier(tmp, y))
461             ret.add(new Node(tmp, y));
462 
463         tmp = y + ystep;
464         if (tmp < map[0].length && tmp >= 0 && !isBarrier(x, tmp))
465             ret.add(new Node(x, tmp));
466 
467         tmp = y - ystep;
468         if (tmp < map[0].length && tmp >= 0 && !isBarrier(x, tmp))
469             ret.add(new Node(x, tmp));
470         
471         tmp = x - xstep;
472         if (tmp < map.length && tmp >= 0 && !isBarrier(tmp, y))
473             ret.add(new Node(tmp, y));
474 
475 
476 
477         return ret;
478     }
479 
480     public static class Node {
481         public int x;
482         public int y;
483 
484         public Node(int x, int y) {
485             this.x = x;
486             this.y = y;
487         }
488 
489         public String toString() {
490             return "(" + x + ", " + y + ") ";
491         }
492 
493         public int hashCode() {
494             // 这里假设x,y都在1000000之内
495             return x * 1000000 + y;
496         }
497 
498     }
499 
500     public class Path implements Comparable {
501         public Node point;
502         public int f;
503         public int g;
504         public int depth;// 路径的长度
505         public Path parent;
506 
507         /**
508          * Default c'tor.
509          */
510         public Path() {
511             // parent = null;
512             // point = null;
513             // g = f = 0;
514         }
515 
516         /**
517          * C'tor by copy another object.
518          * 
519          * @param p
520          *            The path object to clone.
521          */
522         public Path(Path p) {
523             // this();
524             parent = p;
525             g = p.g;
526             f = p.f;
527             depth = p.depth + 1;
528         }
529 
530         /**
531          * Compare to another object using the total cost f.
532          * 
533          * @param o
534          *            The object to compare to.
535          * @see Comparable#compareTo()
536          * @return <code>less than 0</code> This object is smaller than
537          *         <code>0</code>; <code>0</code> Object are the same.
538          *         <code>bigger than 0</code> This object is bigger than o.
539          */
540         public int compareTo(Object o) {
541             Path p = (Path) o;
542             return (f < p.f) ? -1 : (f == p.f ? 1 : 2);
543             // int i= (int) (f - p.f);
544             // if (i==0)
545             // return 1;
546             // else
547             // return i;
548         }
549 
550         /**
551          * Get the last point on the path.
552          * 
553          * @return The last point visited by the path.
554          */
555         public Node getPoint() {
556             return point;
557         }
558 
559         public String toString() {
560             StringBuilder sb = new StringBuilder();
561             sb.append(point.toString());
562             Path pa = this.parent;
563             while (pa != null) {
564                 sb.append("<-");
565                 sb.append(pa.point.toString());
566                 pa = pa.parent;
567             }
568             return sb.toString();
569         }
570 
571         /**
572          * Set the
573          */
574         public void setPoint(Node p) {
575             point = p;
576         }
577 
578         public List<Node> getNodes() {
579             LinkedList<Node> retPath = new LinkedList<Node>();
580             // 去头
581             for (Path i = this; i != null && i.parent != null; i = i.parent) {
582                 retPath.addFirst(i.getPoint());
583             }
584             return retPath;
585         }
586         // public int hashCode(){
587         // return point.x*100+point.y;
588         // }
589     }
590 
644 
645     public void clean() {
646         paths.clear();
647         mindists.clear();
648         expandedCounter = 0;
649     }
650 
651 }

 

看看main方法,用个例子来看看,看下运行结果

public static void main(String[] args) {

        byte[][] map = new byte[10][3];

        System.out
                .println("Find a path from the top left corner to the right bottom one.");

        List<Node> nodes = null;

        map[6][1] = 1;
        map[5][1] = 1;
        map[5][2] = 1;
        // map[9][2] = 1;

        map[7][1] = 2;

        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[0].length; j++)
                System.out.print(map[i][j] + " ");
            System.out.println();
        }
        long begin = System.currentTimeMillis();
        PathFinder pf = new PathFinder(map);
//        nodes = pf.getMovePath(new PathFinder.Node(5,1), new PathFinder.Node(7,1),
//                1, 2, (byte)1);

        nodes = pf.getAtkPath(new PathFinder.Node(5,2), new PathFinder.Node(7,0),
                0, 5, (byte)1);

//        nodes = pf.getMovePath(new PathFinder.Node(2, 0), new PathFinder.Node(
//                1, 2), 1, 1, (byte) 1);
        // for (int i = 0; i < 5000; i++) {
        // PathFinder pf = new PathFinder(map);
        // nodes = pf.getPaths(new PathFinder.Node(0, 0), new PathFinder.Node(
        // 10, 2), 1, 15, (byte) 1);
        //
        // }

        long end = System.currentTimeMillis();

        System.out.println("Time = " + (end - begin) + " ms");
        // System.out.println("Expanded = " + pf.getExpandedCounter());
        // System.out.println("Cost = " + pf.getCost());

        if (nodes == null)
            System.out.println("No path");
        else {
            System.out.print("Path = ");
            for (Node n : nodes)
                System.out.print(n);
            System.out.println();
        }
    }

 

Find a path from the top left corner to the right bottom one.
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 1 1
0 1 0
0 2 0
0 0 0
0 0 0
Time = 0 ms
Path = (6, 2) (6, 1) (6, 0) (7, 0)

posted on 2015-08-18 10:32  dagangzi  阅读(1856)  评论(0编辑  收藏  举报