user12027316
user12027316

Reputation: 123

Puzzle returns unsupported TypeError

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

Answers (3)

Brian
Brian

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

Nico Griffioen
Nico Griffioen

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

CrepeGoat
CrepeGoat

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

Related Questions