Reputation: 123
I'm trying to write a python A*. I believe that my traversal and my heuristic are right but I get an error that I don't understand. I can't change the main
test function but the rest can be manipulated.
The code for the puzzle is:
def pop(self):
pairs = list()
for item in self.frontier:
pairs.append((self.priority[item], item))
(p, item) = min(pairs)
self.frontier.remove(item)
return item
The error that I get when I run the code reads:
125
487
36
Traceback (most recent call last):
File "C:/Users/mchri/Desktop/AI/Homework/eightpuzzle/eightpuzzle.py", line 129, in <module>
main()
File "C:/Users/mchri/Desktop/AI/Homework/eightpuzzle/eightpuzzle.py", line 119, in main
path = agent.astar(puzzle, goal)
File "C:/Users/mchri/Desktop/AI/Homework/eightpuzzle/eightpuzzle.py", line 86, in astar
parent = self.pop()
File "C:/Users/mchri/Desktop/AI/Homework/eightpuzzle/eightpuzzle.py", line 106, in pop
(p, item) = min(pairs)
TypeError: '<' not supported between instances of 'Puzzle' and 'Puzzle'
The puzzle almost solves itself but begins to misplace the numbers and I don't understand how that's a result of the unsupported <
instance.
Upvotes: 1
Views: 70
Reputation: 1604
Because, your code doesn't know which puzzle is "greater" than or "less" than the other. You need to define some -- if not all -- of these magic/dunder methods to tell python how to compare them:
__lt__(a, b)
__le__(a, b)
__eq__(a, b) # <-- You already have this
__ne__(a, b) # <-- You already have this
__ge__(a, b)
__gt__(a, b)
read more here: "rich comparison" methods.
Upvotes: 1
Reputation: 5405
You are using min()
to get the lowest priority value out of a list of tuples, with priority values and puzzles. The way min()
works, is that it compares both values in the tuple, so it will first sort by priority value, then by Puzzle. There is, however no <
operator for Puzzle, because one puzzle is not less than the other.
I assume you just want to sort by priority value, which can be done like this:
(p, item) = min(pairs, key=lambda t: t[0])
If you do want to sort by both priority value and puzzle, you'll need to implement a lt
an eq
operator for Puzzle (See Brian Joseph's answer)
Upvotes: 3
Reputation: 2515
So I added a print statement, like so:
for item in self.frontier:
pairs.append((self.priority[item], item))
print(pairs) # <- My addition
(p, item) = min(pairs)
And your code fails when your pairs
list looks like this:
[
(10, <__main__.Puzzle object at 0x000002019C4C7278>),
(10, <__main__.Puzzle object at 0x000002019C4C7358>),
(12, <__main__.Puzzle object at 0x000002019C4C72B0>)
]
The way that min
works on tuples is this: it compares the first item in each tuple. If those are equal, then it compares the next item. And so on and so forth until it finds an item pair that "breaks the tie", so to speak.
For the above pairs
list, there are two tuples with a first element of 10
; since these are equal, the min
function then tries to compare the two Puzzle
objects. But Puzzle
objects don't know how to be compared! So your code throws an error saying so.
But really, you just want to find the tuple with the smallest p
value, right? So you can replace (p, item) = min(pairs)
with this:
(p, item) = min(pairs, key=lambda t: t[0])
Upvotes: 0