RFC - n-puzzle.py

Peter Otten __peter__ at web.de
Sat May 19 15:01:05 EDT 2007


Phoe6 wrote:

> I would like to request a code and design review of one of my program.
> n-puzzle.py

> I have used OO Python for the above program and would like comments on
> my approach as I am just starting with OOP.

[The following has nothing to do with OOP, I just read Raymond's post and
got interested in the context]

> class State:

>    def huristic_next_state(self, st):

It's heuristics, not huristics.

          # Choose a random item from exp_sts among those with minimal 
          # manhattan_distance()
>         exp_sts = self.expand(st)
>         mdists = []
>         for st in exp_sts:
>             mdists.append(self.manhattan_distance(st))
>         mdists.sort()
>         short_path = mdists[0]
>         if mdists.count(short_path) > 1:
>             least_paths = []
>             for st in exp_sts:
>                 if self.manhattan_distance(st) == short_path:
>                     least_paths.append(st)
>             return random.choice(least_paths)
>         else:
>             for st in exp_sts:
>                 if self.manhattan_distance(st) == short_path:
>                     return st

Looks like you do not need the count() method call at all as the branch for
multiple nearest items works with a length-one list, too. As a consequence, 
there's no need to keep a list of distances, just the minimum:

# all untested
exp_sts = self.expand(st)
short_path = min(exp_sts, key=self.manhattan_distance)
least_paths = [s for s in exp_sts 
    if self.manhattan_distance(s) == short_path]
return random.choice(least_paths)

Calling random.choice() on a list with only one item is predictable but will
do no harm if the code is not time-critical.

By the way, you could write a helper function that finds all minima
according to some criterion

>>> minima([1, 2, -1, 1.0, 3], key=abs)
[1, -1, 1.0]

With such a function the method body would become

return random.choice(minima(self.expand(st), key=self.manhattan_distance))

Almost self-documenting, I'd say :-)

Here's a quick and dirty implementation:

def minima(values, key=lambda x: x):
    d = {}
    for v in values:
        d.setdefault(key(v), []).append(v)
    return d[min(d)]

The advantage of this approach is that you

- iterate over the list just once
- call the key() function once per list item
- can test the function independently from your State class

The memory overhead for the extra lists can be avoided by a better
implementation.

Peter




More information about the Python-list mailing list