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)
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