5

I am trying to use A* search with these heuristics to solve 8-Puzzle: - h1: number of misplaced tiles - h2: total manhattan distance - h3: sum of the above

The moving tile is known as 0.

My goal is to solve these sets:

4 1 2
5 8 3
7 0 6

and

8 6 7
2 5 4
3 0 1

The problem I am having is that with my current implementation of A*, it is able to solve the first problem, but not for the second problem..

So please help me out in understanding what is wrong with my A* code:

int[,] current = inputted from console as string (412583706) and turned into 2D int representing the puzzle. Same for correct, where 0 is in the lower right corner.

var openList = new List<int[,]> { current };
var closedList = new List<int[,]>();

while (openList.Count > 0)
{
    steps++;
    current = GetBestNodeFromList(correct, dimensions, openList, useHeuristic);
    // "GetBestNodeFromList()" finds the cheapest node in the openList.
    // cheapest node: lowest value of h3.

    openList.Remove(current);
    h1 = getHeuristic1b(current, correct, dimensions);
    h2 = getHeuristic2b(current, correct, dimensions);
    h3 = h1 + h2;
    if (h1 == 0 && h2 == 0) { break; }

    openList = Puzzle_PossibleNext(current, closedList);
    // the method "PossibleNext()" finds possible next moves from the current
    // position. if the next move exists in the closedList, it is discarded.

    // Drawing the puzzle and showing heuristics.
    DrawCurrentState(h1, h2, h3, current, steps);

    // adding last visited position to the closedlist.
    closedList.Add(current);
}

The first problem is solved with 7 steps. According to a different program I tested, the next problem can be solved with 32 steps.

Where my program differs from the other is that the first 4 steps are the same, then the other program chooses a different route, while mine just keeps going forever and cannot find a solution. It seems like my program did select the cheapest node, so this is why I cannot understand what is wrong.

It is my first time with pathfinding algorithms, so I would like to solve it. I have been having this problem for 3 days, and I feel like I have tried many solutions, but none work T_T

Best regards.

----Edit----- Additional code:

// Put heuristic value from all in list, then return list item with lowest h-value.
static int[,] GetBestNodeFromList(int[,] correct, int d, List<int[,]> list, string useHeuristic)
{
    int[,] n = new int[d,d];
    if (list.Count > 0)
    {
        List<Int32> heuristicsValueList = new List<Int32>();
        for (int i = 0; i < list.Count; i++)
        {
            if (useHeuristic == "h1")      { heuristicsValueList.Add(getHeuristic1b(list[i], correct, d)); }
            else if (useHeuristic == "h2") { heuristicsValueList.Add(getHeuristic2b(list[i], correct, d)); }
            else  { heuristicsValueList.Add(getHeuristic3(list[i], correct, d)); }
        }
        n = list[heuristicsValueList.IndexOf(heuristicsValueList.Min())];
    }
    return n;
}

---------edit 2-------- changed my code a bit, but still no luck the puzzle setup/node and its heuristics are all in the PuzzleNode object.

// returns a list over next possible moves from the current node. // does not include moves that are found inside closedNodeList.

static List<PuzzleNode> Puzzle_GenerateNextNodes(PuzzleNode node, List<PuzzleNode> closedNodeList)
        {
            List<PuzzleNode> nextList = new List<PuzzleNode>();
            Point isNow = new Point(0, 0);

            // 1) Find where [0] is.
            int dimensions = (int)Math.Sqrt((double)node.n.Length);
            for (int x = 0; x < dimensions; x++) {
                for (int y = 0; y < dimensions; y++) { if (node.n[x, y] == 0) { isNow.X = y; isNow.Y = x; break; } }
            }

            // 2) Check possible moves.
            bool moveUp = false, moveDown = false, moveLeft = false, moveRight = false;

            if (isNow.X == 0)
            {
                moveRight = true;
                if (isNow.Y == 0) { moveDown = true; }
                else if (isNow.Y == 1) { moveUp = true; moveDown = true; }
                else if (isNow.Y == 2) { moveUp = true; }
            }
            else if (isNow.X == 1)
            {
                moveRight = true;
                moveLeft = true;
                if (isNow.Y == 0) { moveDown = true; }
                else if (isNow.Y == 1) { moveUp = true; moveDown = true; }
                else if (isNow.Y == 2) { moveUp = true; }
            }
            else if (isNow.X == 2)
            {
                moveLeft = true;
                if (isNow.Y == 0) { moveDown = true; }
                else if (isNow.Y == 1) { moveUp = true; moveDown = true; }
                else if (isNow.Y == 2) { moveUp = true; }
            }
            // 3) Create list of possible moves.

// Add moved puzzle node to list over next moves 
            if (moveRight)
            {
                int[,] right = new int[dimensions, dimensions];
                Array.Copy(node.n, right, node.n.Length);
                PuzzleNode tmp = new PuzzleNode( PuzzleMoveRight(right, isNow.X, isNow.Y) );
                if (!ListHasThisValue(tmp.n, closedNodeList, dimensions)) { nextList.Add(tmp); }
            }
// moveleft, up, down, same structure as moveRight
            if (moveLeft)
            {
                ..
            }
            if (moveUp)
            {
                ..
            }
            if (moveDown)
            {
                ..
            }

            return nextList;
        }

-----------edit 3----------------

By the way, I want to ask, if my implementation of the different steps of A* are correctly understood. At the moment, my program's A* search does this:

  1. Create initial list OPEN and CLOSED, add starting node to OPEN
  2. Starting loop, removing cheapest node from OPEN, adding it to CLOSED *Cheapest node is determined by its manhattan distance value.
  3. Using the node to find neighbours/children/next moves, adding these to a SUCCESSOR list.
  4. Explore SUCCESSOR list, check if any of them contains goal state, else add to OPEN list
  5. repeat 2-4, exploring the nodes in the list.

When I try these steps with Q1, I get the solution in 7 steps, which is correct. This is also found by hand. But with Q2, it keeps going until OPEN list is empty and there is nothing else to explore. So what am I missing?

Alx
  • 651
  • 1
  • 9
  • 26
  • Have you looked at your `openList` at the 4th step? Why does your algorithm choose the “wrong” step? What does your heuristics say at this point? – Marcel B Jul 22 '14 at 05:20
  • @MarcelB according to the numbers (heuristics) it does choose the cheapest route, but it ends up being wrong when compared with the other program (which uses slightly different heuristics) – Alx Jul 22 '14 at 06:16
  • Can you please post you code for `GetBestNodeFromList(…)`? – Marcel B Jul 22 '14 at 07:04
  • @MarcelB added to opening post. – Alx Jul 22 '14 at 07:10
  • 1
    Can you also include code for "Puzzle_PossibleNext" ? The A* should revert to brute force in worst case (totally dumb heuristic). If you're stuck forever my money is on you're re-visiting already closed states. – Ondrej Svejdar Jul 22 '14 at 08:56
  • @OndrejSvejdar Added it.. Hope it is readable.. >_> – Alx Jul 22 '14 at 09:33

2 Answers2

1

I was able to find solution quickly via brute force. A* should revert to brute force if you're using totally dumb heuristic. How are you comparing your state to list of closed states ?

var set = new int[,] {
  { 1, 2, 3 },
  { 4, 5, 6 },
  { 7, 8, 0 }
};
var clone = (int[,])set.Clone();

var foo = clone == set; // foo is false
var bar = clone.Equals(set); // bar is false

var closedStates = new List<int[,]>();
closedStates.Contains(state); // wrong - contains is using Equals
closedStates.Any(cs => AreEqual(cs, state)); // correct

static bool AreEqual(int[,] stateA, int[,] stateB) {
  for (var x = 0; x < DIMENSIONS; x++) {
    for (var y = 0; y < DIMENSIONS; y++) {
      if (stateA[x, y] != stateB[x, y]) {
        return false;
      }
    }
  }
  return true;
}
Ondrej Svejdar
  • 21,349
  • 5
  • 54
  • 89
  • My state comparison is done the same way as you, but the program still cannot find a solution for Q2. Also, I read on the previously linked wikipedia article that the maximum number of steps to solve an 8-Puzzle is 31 steps. Does this also count when using algorithms like A* ? Or can it go much further if needed? This is frustrating, I don't know what is wrong with my code >__> I am using manhattan heuristics by the way. – Alx Jul 22 '14 at 14:35
  • 2
    @Mads M - it seems you have some trivial bug in your code; hover without full sources it is hard to tell where. Do you have it somewhere on git for example ? – Ondrej Svejdar Jul 22 '14 at 14:45
  • You can grab the source off this drive link [link](https://drive.google.com/file/d/0B45u9bPAuIkvd1ppQjI5RzN3NGs/edit?usp=sharing) Thanks for helping out. I just cannot comprehend what the problem is, stuck with this problem for days.. – Alx Jul 22 '14 at 17:29
0

I want to thank all for helping me with this problem.

I was able to find the problem today. I don't know how to say it, it's really stupid. The problem was not the code or the A* implementation (my current code may differ from what posted earlier).

The issue was over-relying on the heuristics used. It seems that for Q1, heuristic h1, h2 and h3(h1 and h2 had same cost) could all find the solution. However for Q2, both h2 and h3 were not able to find the path to the solution, but h1 was. In my program I kept on using h3 as the default heuristic for debugging and testing, which also was my downfall.

So lesson that should be learned is to know what you are working with. I was not able to realize the difference even the simplest heuristics were able to make.

I am now able to solve Q1 and Q2. Thank you all, again. As a programmer, I sure learned from this.

I wish I could give you all more visible credit for taking the time to help out, though.

Alx
  • 651
  • 1
  • 9
  • 26