Search code examples
python-3.xa-starsliding-tile-puzzle

Path finding: A star not same length from A to B than from B to A


I am implementing the A star algorithm with the Manhattan distance for the 8 puzzle. [ The solution is in spiral form]

1 2 3
8 0 4
7 6 5

In some case, going from A to B will not take the same number of steps as going from B to A.

I think this is because it does not pick the same state on the open list, when they have the same cost, thus, not expanding the same nodes.

From

 7 6 4 
 1 0 8 
 2 3 5



 (A -> B)

 7 6 4 
 1 8 0 
 2 3 5 

 (B -> A)

 7 6 4 
 1 3 8 
 2 0 5

Which both have the same value using Manhattan distance. Should I explore all path with the same value? Or should I change the heuristic to have some kind of tie-breaker?

Here is the relevant part of the code

 def solve(self):
    cost = 0
    priority = 0
    self.parents[str(self.start)] = (None, 0, 0)
    open = p.pr() #priority queue
    open.add(0, self.start, cost)
    while open:
       current = open.get()
       if current == self.goal:
        return self.print_solution(current)
       parent = self.parents[str(current)]
       cost = self.parents[str(current)][2] + 1
       for new_state in self.get_next_states(current):
         if str(new_state[0]) not in self.parents or cost < self.parents[str(new_state[0])][2]:
           priority = self.f(new_state) + cost
           open.add(priority, new_state[0], cost)
           self.parents[str(new_state[0])] = (current, priority, cost)

Solution

  • After wasting so much time re-writing my "solve" function many different ways, for nothing, I finally found the problem.

     def get_next_states(self, mtx, direction):
        n = self.n
        pos = mtx.index(0)
        if  direction != 1 and pos < self.length and (pos + 1) % n: 
          yield (self.swap(pos, pos + 1, mtx),pos, 3)
        if  direction != 2 and pos < self.length - self.n:
          yield (self.swap(pos, pos + n, mtx),pos, 4)
        if  direction != 3 and pos > 0 and pos % n:
         yield (self.swap(pos, pos - 1, mtx),pos, 1)
        if  direction != 4 and pos > n - 1:
         yield (self.swap(pos, pos - n, mtx),pos, 2)
    

    It was in this function. The last if used to be "if 4 and pos > n:" So there were unexplored states.. 2 days for a "-1"

    It will teach me to do more unit testing