我正在为流行的游戏编写自动化脚本,其中一部分需要一个探路者才能走远距离。
我写了这个A *探路者来实现这个目标,但是它有一些问题:
这是我的AStar.java:
package Webwalker;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.osbot.rs07.api.map.Position;
import org.osbot.rs07.script.Script;
import Pickpocketer.PickpocketScript;
public class AStar {
List<Position> open, closed;
List<PathTile> holdingList;
Position s, e, current;
int horizontalCost, diagonalCost, index;
Script scr;
public AStar(Script script) {
open = new ArrayList<Position>();
closed = new ArrayList<Position>();
holdingList = new ArrayList<PathTile>();
horizontalCost = 10;
index = 0;
diagonalCost = 14;
scr = script;
}
public List<Position> findPath(Position start, Position end) {
s = current = start;
closed.add(s);
holdingList.add(new PathTile(start, 0, 0, null));
e = end;
while (!nextToEnd()) {
//scr.log("Finding next node");
PathTile[] nodes = getAdjacent(current);
PathTile node = getLeastCost(nodes);
current = node.p;
if (!node.walkable) {
scr.log("Node " + node.p + " - f " + node.f);
}
closed.add(current);
open.remove(current);
Position mapP = PickpocketScript.map.getMapPos(current);
int x = mapP.getX();
int y = mapP.getY();
PickpocketScript.map.drawPixel(x, y, node.walkable);
//holdingList.add(node);
//scr.log(current);
index++;
}
/*scr.log("Cleaning nodes!");
List<Position> actualPath = new ArrayList<Position>();
for (PathTile pt : holdingList) {
if (pt == null || pt.parent == null) continue;
if (pt.parent.p.equals(start)) {
actualPath.add(pt.parent.p);
break;
}
if (pt.p.equals(start)) break;
actualPath.add(pt.parent.p);
Position mapP = PickpocketScript.map.getMapPos(pt.parent.p);
int x = mapP.getX();
int y = mapP.getY();
PickpocketScript.map.drawPixel(x, y, pt.parent.walkable);
}*/
scr.log("Finished finding " + closed.size() + " nodes");
//Collections.reverse(actualPath);
return closed;
}
public boolean nextToEnd() {
int mh = ManhattenValue(current);
return (mh < 21);
//return false;
}
public int[] distance(Position a, Position b) {
int x = Math.abs(a.getX() - b.getX());
int y = Math.abs(a.getY() - b.getY());
return new int[] { x, y };
}
public PathTile getLeastCost(PathTile[] nodes) {
int lowestIndex = 0;
int lowestF = Integer.MAX_VALUE;
for (int i = 0; i < nodes.length; i++) {
//if (!nodes[i].walkable) scr.log("Node " + nodes[i].p + " - f " + nodes[i].f +" getleastcost");
if (nodes[i] != null && nodes[i].walkable && nodes[i].f < lowestF && !closed.contains(nodes[i].p)) {
lowestF = nodes[i].f;
lowestIndex = i;
}
}
//scr.log(lowestIndex);
return nodes[lowestIndex];
}
public PathTile[] getAdjacent(Position p) {
PathTile[] nodes = new PathTile[8];
int x = p.getX();
int y = p.getY();
PathTile old = null;
if (holdingList.size() > 0) old = holdingList.get(holdingList.size() - 1);
Position newNode = new Position(x - 1, y - 1, p.getZ()); //top left
if (!open.contains(newNode)) open.add(newNode);
int h = OctileValue(newNode);
int g = diagonalCost;
nodes[0] = new PathTile(newNode, g, h, old);
//nodes[0] = nodes[2] = nodes[4] = nodes[5] = nodes[7] = null;
newNode = new Position(x, y - 1, p.getZ()); //top centre
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = horizontalCost;
nodes[1] = new PathTile(newNode, g, h, old);
newNode = new Position(x + 1, y - 1, p.getZ()); //top right
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = diagonalCost;
nodes[2] = new PathTile(newNode, g, h, old);
newNode = new Position(x - 1, y, p.getZ()); //centre left
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = horizontalCost;
nodes[3] = new PathTile(newNode, g, h, old);
/* Centre centre would be "p", so we skip it (we are not moving back to p) */
newNode = new Position(x + 1, y, p.getZ()); //centre right
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = horizontalCost;
nodes[4] = new PathTile(newNode, g, h, old);
newNode = new Position(x - 1, y + 1, p.getZ()); //bottom left
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = diagonalCost;
nodes[5] = new PathTile(newNode, g, h, old);
newNode = new Position(x, y + 1, p.getZ()); //bottom centre
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = horizontalCost;
nodes[6] = new PathTile(newNode, g, h, old);
newNode = new Position(x + 1, y + 1, p.getZ()); //bottom right
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = diagonalCost;
nodes[7] = new PathTile(newNode, g, h, old);
/*PathTile[] temp = nodes.clone();
for (int i = 0; i < nodes.length; i++) {
if (!temp[i].walkable) {
nodes[i] = null;
}
}*/
return nodes;
}
public int OctileValue(Position start) { //This is our "H" value
int x = start.getX();
int y = start.getY();
int horizontal = Math.abs(x - e.getX());
int vertical = Math.abs(y - e.getY());
//return Math.max(horizontal, vertical) * horizontalCost; //bad algorithm lol
//return (horizontal + vertical) * horizontalCost; //Manhatten
//double tieBreaker = (1.0 + (diagonalCost / ManhattenValue(start)));
if (horizontal == 0 && vertical == 0) return 0;
//int h = horizontalCost * (horizontal + vertical) + (diagonalCost - 2 * horizontalCost) * Math.min(horizontal, vertical);
int h = Math.max(horizontal, vertical) + (diagonalCost - horizontalCost) * Math.min(horizontal, vertical);
//h *= (tieBreaker);
return h; //Octile distance
}
public int ManhattenValue(Position start) {
int x = start.getX();
int y = start.getY();
int horizontal = Math.abs(x - e.getX());
int vertical = Math.abs(y - e.getY());
return (horizontal + vertical) * horizontalCost;
}
}
class PathTile {
public Position p;
public int g, h, f;
public boolean walkable;
public PathTile parent;
public PathTile(Position pos, int G, int H, PathTile par) {
p = pos;
g = G;
h = H;
if (par != null) {
g += par.g;
parent = par;
}
if (h == 0) f = 0;
else f = g + h;
System.out.println(p + " - g " + g + " - h " + h + " - f " + f);
walkable = (PickpocketScript.map.isWalkable(pos));
if (!walkable) {
f = Integer.MAX_VALUE;
}
}
}
我知道这些图块是不可走的,因为它们的F值为int.maxvalue(在PathTile类中定义)
此场景中的位置只有一个用于x,y,z的int。
我不太确定如何解决这些问题,因为我对编写这样的算法很陌生:)
答案 0 :(得分:0)
您的findPath()
方法看起来正在执行贪婪的最佳优先搜索。检查行PathTile node = getLeastCost(nodes);
...您正在从当前PathTile的邻居中选择成本最低的节点,而不是从开放集中的条目中选择。相反,使用按 f -value排序的PriorityQueue作为打开列表,并从打开列表的头部获取下一个节点。
其他资源:我建议您关注维基百科的A* Search Algorithm Pseudocode。另外,您可能需要查看{* 3}}上的A *简介。
最终注意事项:在网格图中使用路径查找时,使用枚举来定义路线会很有帮助。这使您的代码更具可读性和可维护性。这是一个例子:
enum Direction{
TOP_LEFT(-1,-1,diagonalCost),
TOP_CENTER(0,-1,horizontalCost),
TOP_RIGHT(1,-1,diagonalCost),
MIDDLE_LEFT(-1,0,horizontalCost),
MIDDLE_RIGHT(1,0,horizontalCost),
BOTTOM_LEFT(-1,1,diagonalCost),
BOTTOM_CENTER(0,1,horizontalCost),
BOTTOM_RIGHT(1,1,diagonalCost);
public final int x;
public final int y;
public final int cost;
Direction(int x, int y, int cost){
this.x = x;
this.y = y;
this.cost = cost;
}
}
您的(未经修正的)getAdjacent
方法可简化为以下内容:
public PathTile[] getAdjacent(Position p) {
PathTile[] nodes = new PathTile[8];
int x = p.getX();
int y = p.getY();
PathTile old = null;
if (holdingList.size() > 0) old = holdingList.get(holdingList.size() - 1);
for(Direction d: Direction.values()){
newNode = new Position(x+ d.x, y + d.y, p.getZ()); //top centre
if (!open.contains(newNode)) open.add(newNode);
h = OctileValue(newNode);
g = d.cost;
nodes[d.ordinal()] = new PathTile(newNode, g, h, old);
}
return nodes;
}