0

I'm trying to implement the A* algorithm in Java. I followed this tutorial,in particular, this pseudocode: http://theory.stanford.edu/~amitp/GameProgramming/ImplementationNotes.html

The problem is my code doesn't work. It goes into an infinite loop. I really don't know why this happens... I suspect that the problem are in F = G + H function implemented in Graph constructors. I suspect I am not calculate the neighbor F correclty.

Here's my code:

List<Graph> open;
    List<Graph> close;

    private void createRouteAStar(Unit u)
    {
        open = new ArrayList<Graph>();
        close = new ArrayList<Graph>();

        u.ai_route_endX = 11;
        u.ai_route_endY = 5;

        List<Graph> neigh;

        int index;
        int i;
        boolean finish = false;

        Graph current;
        int cost;
        Graph start = new Graph(u.xMap, u.yMap, 0, ManhattanDistance(u.xMap, u.yMap, u.ai_route_endX, u.ai_route_endY));

        open.add(start);
        current = start;
        while(!finish)
        {
            index = findLowerF();
            current = new Graph(open, index);
            System.out.println(current.x);
            System.out.println(current.y);
            if (current.x == u.ai_route_endX && current.y == u.ai_route_endY)
            {
                finish = true;
            }
            else
            {
                close.add(current);
                open.remove(open.indexOf(current)); //EDITED LATER
                neigh = current.getNeighbors();
                for (i = 0; i < neigh.size(); i++)
                {
                    cost = current.g + ManhattanDistance(current.x, current.y, neigh.get(i).x, neigh.get(i).y);

                    if (open.contains(neigh.get(i)) && cost < neigh.get(i).g)
                    {
                        open.remove(open.indexOf(neigh));
                    } 
                    else if (close.contains(neigh.get(i)) && cost < neigh.get(i).g)
                    {
                        close.remove(close.indexOf(neigh));
                    }
                    else if (!open.contains(neigh.get(i)) && !close.contains(neigh.get(i)))
                    {
                        neigh.get(i).g = cost;
                        neigh.get(i).f = cost + ManhattanDistance(neigh.get(i).x, neigh.get(i).y, u.ai_route_endX, u.ai_route_endY);
                        neigh.get(i).setParent(current);
                        open.add(neigh.get(i));
                    }
                }
            }

        }

        System.out.println("step");
        for (i=0; i < close.size(); i++)
        {
            if (close.get(i).parent != null)
            {
                System.out.println(i);
                System.out.println(close.get(i).parent.x);
                System.out.println(close.get(i).parent.y);
            }
        }
    }

    private int findLowerF()
    {
        int i;
        int min = 10000;
        int minIndex = -1;
        for (i=0; i < open.size(); i++)
        {
            if (open.get(i).f < min)
            {
                min = open.get(i).f;
                minIndex = i;
                System.out.println("min");
                System.out.println(min);
            }
        }
        return minIndex;
    }


    private int ManhattanDistance(int ax, int ay, int bx, int by)
    {
        return Math.abs(ax-bx) + Math.abs(ay-by);
    }

And, as I've said. I suspect that the Graph class has the main problem. However I've not been able to detect and fix it.

public class Graph {

    int x, y;
    int f,g,h;
    Graph parent;

    public Graph(int x, int y, int g, int h)
    {
        this.x = x;
        this.y = y;
        this.g = g;
        this.h = h;
        this.f = g + h;
    }

    public Graph(List<Graph> list, int index)
    {
        this.x = list.get(index).x;
        this.y = list.get(index).y;
        this.g = list.get(index).g;
        this.h = list.get(index).h;
        this.f = list.get(index).f;
        this.parent = list.get(index).parent;
    }

    public Graph(Graph gp)
    {
        this.x = gp.x;
        this.y = gp.y;
        this.g = gp.g;
        this.h = gp.h;
        this.f = gp.f;
    }

    public Graph(Graph gp, Graph parent)
    {
        this.x = gp.x;
        this.y = gp.y;
        this.g = gp.g;
        this.h = gp.h;
        this.f = g + h;
        this.parent = parent;
    }

    public List<Graph> getNeighbors()
    {
        List<Graph> aux = new ArrayList<Graph>();
        aux.add(new Graph(x+1, y, g,h));
        aux.add(new Graph(x-1, y, g,h));
        aux.add(new Graph(x, y+1, g,h));
        aux.add(new Graph(x, y-1, g,h));
        return aux;
    }

    public void setParent(Graph g)
    {
        parent = g;
    }

    //Added later. Generated by Eclipse

    @Override
    public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + x;
    result = prime * result + y;
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Graph other = (Graph) obj;
    if (x != other.x)
        return false;
    if (y != other.y)
        return false;
    return true;
}
}

Little Edit:

Using the System.out and the Debugger I discovered that the program ALWAYS is check the same "current" graph, (15,8) which is the (u.xMap, u.yMap) position. Looks like it keeps forever in the first step.

Victor Buendía
  • 451
  • 11
  • 21
  • 3
    Put a debugger on it, and trace out the execution. See also [How to debug small programs.](http://ericlippert.com/2014/03/05/how-to-debug-small-programs/) – Robert Harvey Aug 25 '14 at 15:19
  • I did it, I forgot to put that info. It may be useful. Thank you for reminding ^^" – Victor Buendía Aug 25 '14 at 15:33
  • 1
    Did you implement `equals()` on `Graph` so that `list.contains()` works correctly? (You should also implement `hashCode()`, and use sets when possible.) – Mike Strobel Aug 25 '14 at 15:43
  • You can see the complete class. So no, I didn't use `equals()` and `hashCode()`. I understand `equals()`, but I've never used `hashCode()`. What should I do with them? – Victor Buendía Aug 25 '14 at 15:46
  • 1
    There are plenty of resources that describe the relationship between `equals()` and `hashCode()` in detail, and the rules you should follow when overriding them ([here's one](http://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java)). First, try just overriding `equals()` and see if it helps you with your current problem. – Mike Strobel Aug 25 '14 at 15:52
  • That helped a lot, now the infinite loop has dissapeared. However, the object doesn't reach the final position. The output says it finishes on (13,5) instead of (11,5). I've also proved with (8,6) and it finishes in (9,5); always at a 2 positions distance. – Victor Buendía Aug 25 '14 at 16:06

1 Answers1

1

You don't remove your start node from open list (i mean, that after each cycle in while you need to remove node that was just eximining). Also, some hints:

  1. there is priority queue in java. So, you don't need to do it on list by yourself
  2. int f,g,h; try to rename variables. it is impossible to understand their meaning in the code.
  3. then you create neighbors, check that they are in the area (i.e: x,y>0 and less that maxX,maxY)
Natalia
  • 4,362
  • 24
  • 25
  • Added the removing of start node. That was important. About the hints, 1. What you mean with the priority queue? Did I make something bad with lists? 2. That variables are called so in the theory, and they appears with that names in many tutorials, like in [wikipedia](http://en.wikipedia.org/wiki/A*_search_algorithm#Pseudocode) and also [this popular one](http://www.policyalmanac.org/games/aStarTutorial.htm). 3 You've reason, I've forgotten the bounds. Thank you! – Victor Buendía Aug 25 '14 at 17:07
  • 1
    I disagree with renaming the variables. In general, they would be unclear. In the context of A*, they're the standard names. – harold Aug 25 '14 at 17:13
  • Ok, i'm not familiar with naming conventions in this particular algorithm. – Natalia Aug 26 '14 at 07:50
  • Priority queue helps you to get the first (max/min) element by O(1). In your case you get it by O(n) going thought the whole list each time. See [priority queue in java](http://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html) – Natalia Aug 26 '14 at 07:52