3

Here I have a query regarding the A Star Search Algorithm. I'm building what is known as the 8 piece puzzle. This is a game with 9 places (1 is empty) and you have to arrange the tiles in a correct order to meet the goal position.

I just need to verify that I have written the algorithm correctly so I can seek elsewhere in my code for the issue.

I personally believe that the algorithm is correct because it is able to solve the first pre-set puzzle I have created which only requires one move to reach the goal position. However, it is unable to solve the puzzles which require more moves.

I've tried to get this to work in 3 different ways and all bring the same issue.

Attempt 1:

while (openList.Count() > 0)
        {
            PuzzleNode currentNode;
            var orderedOpenList = openList.OrderBy(PuzzleNode => PuzzleNode.getPathCost());

            currentNode = orderedOpenList.First();
            if (compare(currentNode.getPuzzle(), goalArray) == true)
            {
                //openList.RemoveAt(0); //POLL
                break;
                // otherwise push currentNode onto closedList & remove from openList
            }
            else
            {
                openList.Remove(currentNode);
                closedList.Add(currentNode);
            }

            //generate all the neighbor nodes
            generateSuccessors(currentNode, tempList);

            for (int i = 0; i < tempList.Count(); i++)
            {
                PuzzleNode tempNode = tempList[i];

                //skip the node if it's in the closed list
                if (closedList.Contains(tempNode))
                {
                    continue;
                }//end if

                //We need to ensure that the G we have seen here is the shortest one
                int gScore = currentNode.getG() + 1;

                if (!openList.Contains(tempNode) || gScore < tempNode.getG())
                {
                    tempNode.setParentNode(currentNode);
                    tempNode.setH(tempNode.calcH(currentHueristic, tempNode.getPuzzle(), goalArray));
                    tempNode.setG(gScore);
                    tempNode.updatePathCost();

                    if (!openList.Contains(tempNode))
                    {
                        openList.Add(tempNode);
                    }//end if


                }//end if
            }//end for

        }//end while

Attempt 2:

while (openList.Count() > 0)
        {
            PuzzleNode currentNode = GetBestNodeFromOpenList(openList);

            openList.Remove(currentNode);
            closedList.Add(currentNode);

            generateSuccessors(currentNode, tempList);

            foreach (PuzzleNode successorNode in tempList)
            {
                if (compare(successorNode.getPuzzle(), goalArray) == true)
                {
                    //goto thebreak;
                    return successorNode;
                }
                successorNode.setG(currentNode.getG() + 1);
                successorNode.setH(successorNode.calcH(currentHueristic, successorNode.getPuzzle(), goalArray));
                successorNode.updatePathCost();


                if (OpenListHasBetterNode(successorNode, openList))
                    continue;

                openList.Add(successorNode);
            }
        }//end while

private static PuzzleNode GetBestNodeFromOpenList(IEnumerable<PuzzleNode> openList)
    {
        return openList.OrderBy(n => n.getPathCost()).First();
    }

private static bool OpenListHasBetterNode(PuzzleNode successor, IEnumerable<PuzzleNode> list)
    {
        return list.FirstOrDefault(n => n.getG().Equals(successor.getG())
                && n.getPathCost() < successor.getPathCost()) != null;
    }

Attempt 2 is an alteration of an algorithm I found on the internet: Solving the 8-Puzzle Problem

However I done my best to follow the psuedocode on wikipedia:

function A*(start,goal)
     closedset := the empty set    // The set of nodes already evaluated.
     openset := {start}    // The set of tentative nodes to be evaluated, initially containing the start node
     came_from := the empty map    // The map of navigated nodes.

     g_score[start] := 0    // Cost from start along best known path.
     // Estimated total cost from start to goal through y.
     f_score[start] := g_score[start] + heuristic_cost_estimate(start, goal)

     while openset is not empty
         current := the node in openset having the lowest f_score[] value
         if current = goal
             return reconstruct_path(came_from, goal)

         remove current from openset
         add current to closedset
         for each neighbor in neighbor_nodes(current)
             tentative_g_score := g_score[current] + dist_between(current,neighbor)
             if neighbor in closedset
                 if tentative_g_score >= g_score[neighbor]
                     continue

             if neighbor not in openset or tentative_g_score < g_score[neighbor] 
                 came_from[neighbor] := current
                 g_score[neighbor] := tentative_g_score
                 f_score[neighbor] := g_score[neighbor] + heuristic_cost_estimate(neighbor, goal)
                 if neighbor not in openset
                     add neighbor to openset

I ask whether you can find an issue because I'm confused as to why it would work for only one of the puzzles. The only this seperate these puzzles is the amount of moves needed to solve to the goal state.

I've debugged for hours and I just can't see it, I also can't see where else in my code could have an issue. So I guess I'm just asking, does this look correct to you?

Any questions be sure to ask and I'll provide more information where possible! Thank you in advance!

Not loved
  • 30,848
  • 21
  • 111
  • 180
Glitchezz
  • 333
  • 2
  • 6
  • 20
  • 1
    For questions like this, where you're basically asking if there's a bug in your code, we need the whole project. I have no idea what PuzzleNode is for example, and assumptions I might make could easily be wrong and a waste of time. Please post a zip of your project somewhere for us. – Jasmine Apr 19 '13 at 21:11
  • 2
    Also, given that this is a fairly complex bit of code, it might be a better use of your time to use the debugger and step through it, keeping track of each variable, to see if it is doing what you think it should be. – NotMe Apr 19 '13 at 21:15
  • 7
    "*I personally believe that the algorithm is correct because it is able to solve the first pre-set puzzle I have created which only requires one move to reach the goal position. However, it is unable to solve the puzzles which require more moves.*" This doesn't make any sense. You believe the algorithm is correct *because* it gives the wrong answer for everything but the most trivial cases? – Eric Lippert Apr 19 '13 at 21:27
  • Is there a way to share the project privately as this is a University assignment and I wouldn't want anyone just downloading my progress thus far and potentially uploading it before me. I've stepped through the code but with my expectations I reckon I could just be missing something. As to the paragraph not making sense. I see where you're coming from , but I said that because the algorithm works as it solves the first puzzle. I thought that perhaps there could be an issue somewhere else that's causing the issue. – Glitchezz Apr 22 '13 at 15:02

1 Answers1

0

Note: I'm using CustomPriorityQueue class (written by itowlson) that you can find here

So here I basically use PriorityQueue which will arrange problem states by their heuristic value

Here is a simple A* template in C# that illustrate how A* search works:

void enqueueAll(CustomPriorityQueue<PuzzleNode> a, ArrayList<PuzzleNode> b)
{
    foreach(PuzzleNode c in b) a.enqueue(c, h(c));
}

// A* heuritic: h = f + g ; h <= h*
int h(PuzzleNode n);

// returns TRUE if n is a solution
bool isSolution(PuzzleNode n);

// expand n into list of neighbors
ArrayList<PuzzleNode> expand(PuzzleNode n);

PuzzleNode Astar(PuzzleNode startPoint)
{
    CustomPriorityQueue list = new CustomPriorityQueue<PuzzleNode>();
    PuzzleNode current = null;

    list.enqueue(startPoint, h(startPoint));

    while (list.size() > 0)
    {
        current = list.dequeue();

        if (isSolution(current))
        {
            return current;
        }

        enqueueAll(list, expand(current));
    }
}

hope this helps

Community
  • 1
  • 1
Khaled.K
  • 5,516
  • 1
  • 30
  • 47