目的:我这里希望实现一个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)