#!/usr/bin/python3
import heapq
class state(object):
__slots__ = ['zero', 'puzzle', 'moves', 'parent', 'key']
def __init__(self, zero, puzzle, moves, parent=None):
self.zero = zero
self.puzzle = puzzle
self.moves = moves
self.parent = parent
self.key = self.heuristics() + self.moves
def heuristics(self):
h = 0
for i, p in enumerate(self.puzzle):
if p != 0:
r1, c1 = divmod(i, 3)
r2, c2 = divmod(p - 1, 3)
h += abs(r1 - r2) + abs(c1 - c2)
return h
def children(self):
row, col = divmod(self.zero, 3)
if row > 0: yield self.child(self.zero - 3)
if row < 2: yield self.child(self.zero + 3)
if col > 0: yield self.child(self.zero - 1)
if col < 2: yield self.child(self.zero + 1)
def child(self, newpos):
p = list(self.puzzle)
p[self.zero] = p[newpos]
p[newpos] = 0
return state(newpos, tuple(p), self.moves + 1, self)
def ancestry(self):
a = []
s = self
while s:
a.append(s)
s = s.parent
return reversed(a)
def __lt__(self, other):
return self.key < other.key
def __le__(self, other):
return self.key <= other.key
def __gt__(self, other):
return self.key > other.key
def __ge__(self, other):
return self.key >= other.key
def __str__(self):
s = tuple((str(i) if i else ' ') for i in self.puzzle)
return '{} {} {}\n{} {} {}\n{} {} {}'.format(*s)
def puzzle8(p):
assert len(p) == 9
assert frozenset(p) == frozenset(range(9))
p = tuple(p)
zero = None
for i, pi in enumerate(p):
if pi == 0:
zero = i
break
assert p[zero] == 0
queue = [state(zero, p, 0)]
seen = dict()
seen[queue[0].puzzle] = queue[0]
goal = (1, 2, 3,
4, 5, 6,
7, 8, 0)
while queue:
s = heapq.heappop(queue)
if (s.puzzle == goal):
for a in s.ancestry():
print(a)
print('')
print('Minimal number of moves: {}'.format(s.moves))
print('')
return
for c in s.children():
try:
o = seen[c.puzzle]
if o.key > c.key:
o.moves = c.moves
o.parent = c.parent
o.key = c.key
heapq.heapify(queue) # heapq has no decrease key op
except KeyError:
seen[c.puzzle] = c
heapq.heappush(queue, c)
puzzle8([1, 3, 0,
8, 4, 5,
7, 2, 6])
puzzle8([3, 5, 1,
6, 8, 0,
7, 2, 4])
IyEvdXNyL2Jpbi9weXRob24zCgppbXBvcnQgaGVhcHEKCmNsYXNzIHN0YXRlKG9iamVjdCk6CiAgICBfX3Nsb3RzX18gPSBbJ3plcm8nLCAncHV6emxlJywgJ21vdmVzJywgJ3BhcmVudCcsICdrZXknXQogICAgZGVmIF9faW5pdF9fKHNlbGYsIHplcm8sIHB1enpsZSwgbW92ZXMsIHBhcmVudD1Ob25lKToKICAgICAgICBzZWxmLnplcm8gPSB6ZXJvCiAgICAgICAgc2VsZi5wdXp6bGUgPSBwdXp6bGUKICAgICAgICBzZWxmLm1vdmVzID0gbW92ZXMKICAgICAgICBzZWxmLnBhcmVudCA9IHBhcmVudAogICAgICAgIHNlbGYua2V5ID0gc2VsZi5oZXVyaXN0aWNzKCkgKyBzZWxmLm1vdmVzCiAgICBkZWYgaGV1cmlzdGljcyhzZWxmKToKICAgICAgICBoID0gMAogICAgICAgIGZvciBpLCBwIGluIGVudW1lcmF0ZShzZWxmLnB1enpsZSk6CiAgICAgICAgICAgIGlmIHAgIT0gMDoKICAgICAgICAgICAgICAgIHIxLCBjMSA9IGRpdm1vZChpLCAzKQogICAgICAgICAgICAgICAgcjIsIGMyID0gZGl2bW9kKHAgLSAxLCAzKQogICAgICAgICAgICAgICAgaCArPSBhYnMocjEgLSByMikgKyBhYnMoYzEgLSBjMikKICAgICAgICByZXR1cm4gaAogICAgZGVmIGNoaWxkcmVuKHNlbGYpOgogICAgICAgIHJvdywgY29sID0gZGl2bW9kKHNlbGYuemVybywgMykKICAgICAgICBpZiByb3cgPiAwOiB5aWVsZCBzZWxmLmNoaWxkKHNlbGYuemVybyAtIDMpCiAgICAgICAgaWYgcm93IDwgMjogeWllbGQgc2VsZi5jaGlsZChzZWxmLnplcm8gKyAzKQogICAgICAgIGlmIGNvbCA+IDA6IHlpZWxkIHNlbGYuY2hpbGQoc2VsZi56ZXJvIC0gMSkKICAgICAgICBpZiBjb2wgPCAyOiB5aWVsZCBzZWxmLmNoaWxkKHNlbGYuemVybyArIDEpCiAgICBkZWYgY2hpbGQoc2VsZiwgbmV3cG9zKToKICAgICAgICBwID0gbGlzdChzZWxmLnB1enpsZSkKICAgICAgICBwW3NlbGYuemVyb10gPSBwW25ld3Bvc10KICAgICAgICBwW25ld3Bvc10gPSAwCiAgICAgICAgcmV0dXJuIHN0YXRlKG5ld3BvcywgdHVwbGUocCksIHNlbGYubW92ZXMgKyAxLCBzZWxmKQogICAgZGVmIGFuY2VzdHJ5KHNlbGYpOgogICAgICAgIGEgPSBbXQogICAgICAgIHMgPSBzZWxmCiAgICAgICAgd2hpbGUgczoKICAgICAgICAgICAgYS5hcHBlbmQocykKICAgICAgICAgICAgcyA9IHMucGFyZW50CiAgICAgICAgcmV0dXJuIHJldmVyc2VkKGEpCiAgICBkZWYgX19sdF9fKHNlbGYsIG90aGVyKToKICAgICAgICByZXR1cm4gc2VsZi5rZXkgPCBvdGhlci5rZXkKICAgIGRlZiBfX2xlX18oc2VsZiwgb3RoZXIpOgogICAgICAgIHJldHVybiBzZWxmLmtleSA8PSBvdGhlci5rZXkKICAgIGRlZiBfX2d0X18oc2VsZiwgb3RoZXIpOgogICAgICAgIHJldHVybiBzZWxmLmtleSA+IG90aGVyLmtleQogICAgZGVmIF9fZ2VfXyhzZWxmLCBvdGhlcik6CiAgICAgICAgcmV0dXJuIHNlbGYua2V5ID49IG90aGVyLmtleQogICAgZGVmIF9fc3RyX18oc2VsZik6CiAgICAgICAgcyA9IHR1cGxlKChzdHIoaSkgaWYgaSBlbHNlICcgJykgZm9yIGkgaW4gc2VsZi5wdXp6bGUpCiAgICAgICAgcmV0dXJuICd7fSB7fSB7fVxue30ge30ge31cbnt9IHt9IHt9Jy5mb3JtYXQoKnMpCgpkZWYgcHV6emxlOChwKToKICAgIGFzc2VydCBsZW4ocCkgPT0gOQogICAgYXNzZXJ0IGZyb3plbnNldChwKSA9PSBmcm96ZW5zZXQocmFuZ2UoOSkpCiAgICBwID0gdHVwbGUocCkKICAgIHplcm8gPSBOb25lCiAgICBmb3IgaSwgcGkgaW4gZW51bWVyYXRlKHApOgogICAgICAgIGlmIHBpID09IDA6CiAgICAgICAgICAgIHplcm8gPSBpCiAgICAgICAgICAgIGJyZWFrCiAgICBhc3NlcnQgcFt6ZXJvXSA9PSAwCiAgICBxdWV1ZSA9IFtzdGF0ZSh6ZXJvLCBwLCAwKV0KICAgIHNlZW4gPSBkaWN0KCkKICAgIHNlZW5bcXVldWVbMF0ucHV6emxlXSA9IHF1ZXVlWzBdCiAgICBnb2FsID0gKDEsIDIsIDMsCiAgICAgICAgICAgIDQsIDUsIDYsCiAgICAgICAgICAgIDcsIDgsIDApCiAgICB3aGlsZSBxdWV1ZToKICAgICAgICBzID0gaGVhcHEuaGVhcHBvcChxdWV1ZSkKICAgICAgICBpZiAocy5wdXp6bGUgPT0gZ29hbCk6CiAgICAgICAgICAgIGZvciBhIGluIHMuYW5jZXN0cnkoKToKICAgICAgICAgICAgICAgIHByaW50KGEpCiAgICAgICAgICAgICAgICBwcmludCgnJykKICAgICAgICAgICAgcHJpbnQoJ01pbmltYWwgbnVtYmVyIG9mIG1vdmVzOiB7fScuZm9ybWF0KHMubW92ZXMpKQogICAgICAgICAgICBwcmludCgnJykKICAgICAgICAgICAgcmV0dXJuCiAgICAgICAgZm9yIGMgaW4gcy5jaGlsZHJlbigpOgogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBvID0gc2VlbltjLnB1enpsZV0KICAgICAgICAgICAgICAgIGlmIG8ua2V5ID4gYy5rZXk6CiAgICAgICAgICAgICAgICAgICAgby5tb3ZlcyA9IGMubW92ZXMKICAgICAgICAgICAgICAgICAgICBvLnBhcmVudCA9IGMucGFyZW50CiAgICAgICAgICAgICAgICAgICAgby5rZXkgPSBjLmtleQogICAgICAgICAgICAgICAgICAgIGhlYXBxLmhlYXBpZnkocXVldWUpICMgaGVhcHEgaGFzIG5vIGRlY3JlYXNlIGtleSBvcAogICAgICAgICAgICBleGNlcHQgS2V5RXJyb3I6CiAgICAgICAgICAgICAgICBzZWVuW2MucHV6emxlXSA9IGMKICAgICAgICAgICAgICAgIGhlYXBxLmhlYXBwdXNoKHF1ZXVlLCBjKQoKcHV6emxlOChbMSwgMywgMCwKICAgICAgICAgOCwgNCwgNSwKICAgICAgICAgNywgMiwgNl0pCgpwdXp6bGU4KFszLCA1LCAxLAogICAgICAgICA2LCA4LCAwLAogICAgICAgICA3LCAyLCA0XSkK