Zacząłem pracować nad tym problemem i do tej pory chciałem współpracować z moim kodem. Jak stwierdził Gareth, problem jest porównywalny z 8-kafelkową łamigłówką, więc kod oparty jest na wspaniałym rozwiązaniu Keitha Randalla, a więc w Pythonie. To rozwiązanie może rozwiązać wszystkie 5 przypadków testowych o łącznej sumie mniejszej niż 400 ruchów, a także inne zagadki. Zawiera zoptymalizowane i brutalne rozwiązanie. Kod jest już trochę rozdęty. Dane wyjściowe są skrócone jak „llururd ..”. Mam nadzieję, że jest to pomocne.
http://www.penschuck.org/joomla/tmp/15Tile.txt (wyjaśnienie)
http://www.penschuck.org/joomla/tmp/tile15.txt (kod python)
# Author: Heiko Penschuck
# www.penschuck.org
# (C) 2012
# import os;os.chdir('work')
# os.getcwd()
# def execfile(file, globals=globals(), locals=locals()):
# with open(file, "r") as fh: exec(fh.read()+"\n", globals, locals)
#
#
# execfile("tile15.py");
#
## run these
# solve_brute();
# solve();
# some boards to play with
board2=(15,14,7,3,13,10,2,9,11,12,4,6,5,0,1,8);
# best: 76(52)
# 72(56)
# 68(51) uurddlurrulldrrdllluuruldrddlururulddruurdllldrurddlurdruuldrdluurdd
board3=(13, 8, 9, 4, 15, 11, 5, 3, 14, 6, 12, 7, 1, 10, 2, 0)
# best: 106(77)
#best: 90(64) ullldruuldrrdrlluurulldrrdldluruulddrulurrdrddlluuurdldrrulddrulldrurullldrdluurrrddllurdr
board4=(4, 8, 12, 1, 13, 7, 3, 11, 9, 15, 6, 14, 5, 2, 10, 0) ;# best 100(74)
board5=(15,2,3,4,5,6,7,8,9,10,11,12,13,1,14,0); # best 44(32)
board6=( 1, 2, 3, 4, 6, 11, 0, 12, 8, 14, 9, 13, 5, 10, 7, 15);
# testcases
board7=(5,1,7,3,9,2,11,4,13,6,15,8,0,10,14,12); # 15 (7)
board8=(2,5,13,12,1,0,3,15,9,7,14,6,10,11,8,4); # 124 (94)
board9=(5,2,4,8,10,0,3,14,13,6,11,12,1,15,9,7) ; # 72 (56)
board10=(11,4,12,2,5,10,3,15,14,1,6,7,0,9,8,13) ;# 71 (57)
board11=(5,8,7,11,1,6,12,2,9,0,13,10,14,3,4,15) ;# 99 (73)
board12=(1,2,3,4,5,6,7,8,9,10,11,12,13,0,14,15); #pretty simple board
board13=(4, 10, 5, 12, 11, 7, 15, 2, 13, 1, 14, 8, 6, 3, 9, 0)
board=board3 ; # used by solve()
bboard=list(board) ;# used by solve_brute()
# init
clean=(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0)
i=0;
solution={};
invsolution={};
E={board:0}
# derived from Keith Randall 8-tile solution
# a: a board, d: offset to move from i: index in board
def Y(a,d,i):
b=list(a); # b is now an indexable board
b[i],b[i+d]=b[i+d],0; # make a move (up down left right)
b=tuple(b); # now back to searchable
if b not in E:E[b]=a;# store new board in E
def Calc():
ii=0;
# memory error when x is 21
for x in ' '*14:
if ii>10:
print(ii);
ii+=1
for a in E.copy():
# for all boards, make possible moves (up,left,right,down) and store the new boards
i=list(a).index(0)
if i>3:Y(a,-4,i)
if i%4:Y(a,-1,i)
if i%4 <3:Y(a,1,i)
if i<12:Y(a,4,i)
def weigh(a,goal):
factor=[26,8,4,6, 8,8,4,4, 4,4,1,1, 3,2,1,0]
weight=0;
for element in a:
i=list(a).index(element);
ix,iy=divmod(i,4); # ist
if element == 0:
# special for gap
weight=weight+ix;
#weight+=(ix+iy)
continue;
i=list(a).index(element);
ix,iy=divmod(i,4); # ist
j=list(goal).index(element);
sx,sy=divmod(j,4); # soll
#k=list(a).index(0); # gap
#kx,ky=divmod(k,4)
# try solving from topleft to bottom right (because clean board has gap at bottomright)
tmp= abs(sx-ix)*abs(sx-ix)*factor[j]+ abs(sy-iy)*abs(sy-iy)*factor[j]
#tmp += ((sx!=ix )& (sy!=iy)) *(4-sx)*(4-sy)*4
weight+=tmp
#(10-sx-sy-sy)
# 8*abs(sx-ix) + (16-j)*(sx!=ix)
#print('%2d %2d_%2d (%2d_%2d)=> %d'%(element,i,j,(sx-ix),(sy-iy),weight))
return weight
# read numbers seperated by a whitespace
def readboard():
global E,D,board,clean,i
reset()
g=[]
for x in' '*4:g+=map(int,input().split())
board=tuple(g)
# read 'a' till 'o'
def readasciiboard():
global E,D,board,clean,i
trans={"0":0,"a":1,"b":2,"c":3,"d":4,"e":5,"f":6,"g":7,"h":8,"i":9,"j":10,"k":11,"l":12,"m":13,"n":14,"o":15}
reset()
g=[]
vec=tuple(input().split());
for x in vec: g.append(trans[x])
board=tuple(g)
def printasciiboard(a):
trans={"0":0,"a":1,"b":2,"c":3,"d":4,"e":5,"f":6,"g":7,"h":8,"i":9,"j":10,"k":11,"l":12,"m":13,"n":14,"o":15}
itrans={}
for x in trans: itrans[trans[x]]=x
g=[]
for x in a: g.append(itrans[x])
for i in(0,4,8,12): print('%s %s %s %s'%tuple(g[i:i+4]))
# find the board with the smallest weight
def minimum():
global minn,E,clean
minn=1111111;# start with a huge number
qq=board
for q in E:
if weigh(q,clean) < minn:
minn=weigh(q,clean)
qq=q
return qq
# run this and printsolution()
# (you might have to reverse the order of the printed solution)
def solve():
global start,board,E,clean,minn,solution
start=board;
solution={};
E={ board:0 }
for x in range(0,11):
Calc(); # walks all possible moves starting from board to a depth of 10~20 moves
if clean in E:
print('Solution found')
q=clean;
tmp=[];
while q:
tmp.append(q)
q=E[q]
for x in reversed(tmp):
solution[len(solution)]=x;
printsolution();
return
q=minimum(); # calculates the "weight" for all Calc()-ed boards and returns the minimum
#print("Len %3d"%len(E))
print("weight %d"%minn)
# stitch solution
newboard=q;
tmp=[];
while q:
tmp.append(q)
q=E[q]
for x in reversed(tmp):
solution[len(solution)]=x;
board=newboard;
E={board:0}; #reset the Calc()-ed boards
print("No Solution")
# collects and prints the moves of the solution
# from clean board to given board
# (you have to reverse the order)
def printsolution():
global invsolution,solution,moves,clean,start
moves=""
g=start; # start from board to clean
y=g
#invsolution[clean]=0;
for x in solution:
# uncomment this if you want to see each board of the solution
#print(g);
g=solution[x];
#sys.stdout.write(transition(y,g))
if (transition(g,y)=="E"): continue
moves+=transition(g,y)
# or as squares
#print('%10s %d %s'%("step",len(moves),transition(g,y)));
#print(" %s -- %s "%(y,g))
#for i in(0,4,8,12): print('%2d %2d %2d %2d'%g[i:i+4])
y=g
llen=len(moves)
print(" moves%3d "%llen)
print(moves)
# processing moves. funny, but occysionally ud,du,lr or rl appears due to the stitching
while 'lr' in moves:
a,b,c=moves.partition('lr')
moves=a+c
llen-=2
while 'rl' in moves:
a,b,c=moves.partition('rl')
moves=a+c
llen-=2
while 'ud' in moves:
a,b,c=moves.partition('ud')
moves=a+c
llen-=2
while 'du' in moves:
a,b,c=moves.partition('du')
moves=a+c
llen-=2
# processing moves. concatenating lll to 3l
while 'lll' in moves:
a,b,c=moves.partition('lll')
moves=a+' 3l '+c
llen-=2
while 'rrr' in moves:
a,b,c=moves.partition('rrr')
moves=a+' 3r '+c
llen-=2
while 'uuu' in moves:
a,b,c=moves.partition('uuu')
moves=a+' 3u '+c
llen-=2
while 'ddd' in moves:
a,b,c=moves.partition('ddd')
moves=a+' 3d '+c
llen-=2
while 'll' in moves:
a,b,c=moves.partition('ll')
moves=a+' 2l '+c
llen-=1
while 'rr' in moves:
a,b,c=moves.partition('rr')
moves=a+' 2r '+c
llen-=1
while 'uu' in moves:
a,b,c=moves.partition('uu')
moves=a+' 2u '+c
llen-=1
while 'dd' in moves:
a,b,c=moves.partition('dd')
moves=a+' 2d '+c
llen-=1
print(" processed:%3d "%llen)
print(moves)
return
def transition(a,b):
# calculate the move (ie up,down,left,right)
# between 2 boards (distance of 1 move and a weight of 1 only)
i=list(a).index(0);
j=list(b).index(0);
if (j==i+1): return "l"
if (j==i-1): return "r"
if (j==i-4): return "d"
if (j==i+4): return "u"
#print("transition not possible")
return "E"
###################################################
# below this line are functions for the brute force solution only
# added for comparision
#
# its using a global variable bboard and works destructively on it
def solve_brute():
global bboard,board;
bboard=list(board); # working copy
move(1,0);move(2,1);
move(3,14); # <== additional move, move 3 out of way
move(4,2);move(3,6);
gap_down();gap_down();gap_right();gap_right();gap_up();gap_up();gap_up();gap_left();gap_down();
#first line solved
print("first line");printbboard();
move(5,4);move(6,5);move(7,14);move(8,6);move(7,10);
gap_down();gap_down();gap_right();gap_right();gap_up();gap_up();gap_left();gap_down();
#second line solved (upper half)
print("2nd line");printbboard();
move(9,15);move(13,8);move(9,9)
gap_down();gap_left();gap_left();gap_up();gap_right();
print("left border");printbboard();
#left border solved
move(10,15);move(14,9);move(10,10);
gap_down();movegap(1+3*4);gap_up();gap_right();
print("left half");printbboard();
#left half solved
#rotating last 4 tiles 5 times
for x in ' '*5:
gap_right();gap_down(); # gap is now on 15
if (bboard==[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0]):
print("solution found");printbboard();
return;
gap_left();gap_up();
print("No solution found");
printbboard();
return
def printbboard():
global bboard
for i in(0,4,8,12): print('%2d %2d %2d %2d'%tuple(bboard[i:i+4]))
def gap_up():
global bboard
i=bboard.index(0);
if (i<4):
print("Err up()")
return
bboard[i],bboard[i-4] = bboard[i-4] , 0 ;
def gap_down():
global bboard
i=bboard.index(0);
if (i>11):
print("Err down()")
return
bboard[i],bboard[i+4] = bboard[i+4] , 0 ;
def gap_left():
global bboard
i=bboard.index(0);
if (i%4<1):
print("Err left()")
return
bboard[i],bboard[i-1]= bboard[i-1] , 0 ;
def gap_right():
global bboard
i=bboard.index(0);
if (i%4>2):
print("Err right()")
return
bboard[i],bboard[i+1] = bboard[i+1] , 0 ;
def movegap(d):
global bboard;
# d: destination location (0-15)
k=bboard.index(0);
ky,kx=divmod(k,4);
dy,dx=divmod(d,4);
# moving the gap
while (ky>dy):
gap_up();ky-=1;
while (ky<dy):
gap_down();ky+=1;
while (kx>dx):
gap_left();kx-=1;
while (kx<dx):
gap_right();kx+=1;
def move(s,d):
global bboard
i=bboard.index(s);
iy,ix=divmod(i,4);
dy,dx=divmod(d,4);
#moving a number
while (ix<dx):
move1right(s);
print("1right ");
ix+=1;
while (ix>dx):
move1left(s);
ix-=1;
print("1left ");
while(iy<dy):
move1down(s);
print("1down ");
iy+=1;
while(iy>dy):
move1up(s);
print("1up");
iy-=1;
def move1up(s):
global bboard
i=bboard.index(s);
iy,ix=divmod(i,4);
k=bboard.index(0);
ky,kx=divmod(k,4);
if (ky<iy):
# above: move 1 above, then leftorright, then 1 down
movegap(kx+4*(iy-1))
movegap(ix+4*(iy-1))
movegap(ix+4*iy)
return; # fin
if (ky==iy):
# if equal, then first try 1 down
# (not nescessary if gap is right of s)
if (kx<ix):
if (ky<=2):
movegap(kx+4*(iy+1))
movegap(ix+1+4*(iy+1)); # 1right 1down of s
movegap(ix+1+4*(iy-1)); # 1right 1up of s
movegap(ix+4*(iy-1));# right over s
gap_down(); # fin
return;
# bottom border, must go up first
movegap(kx+4*(iy-1));
movegap(ix+4*(iy-1));
gap_down();
return; # fin
else:
movegap(ix+1+4*iy); # move 1 right of s
gap_up()
gap_left()
gap_down();
return; # fin
movegap(ix+1+4*ky); # move 1 right of s
movegap(ix+1+4*(iy+1)); # move 1 right and 1 down of s
gap_up();
gap_up();
gap_left();
gap_down();
def move1left(s):
global bboard
i=bboard.index(s);
iy,ix=divmod(i,4);
k=bboard.index(0);
ky,kx=divmod(k,4);
if (ky<iy):
# if above gap move 1 over s
if (kx<ix):
movegap(kx+4*iy);
movegap(ix+4*iy);
return;# fin
if (kx==ix):
#gap over s
if (ix<3):
# try to move under s and then left
if (iy<3):
movegap(ix+1+4*ky)
movegap(ix+1+4*(iy+1))
movegap(ix-1+4*(iy+1))
movegap(ix-1+4*iy)
movegap(ix+4*iy)
return; #fin
# have to move left
movegap(kx-1+4*ky)
movegap(ix-1+4*iy)
movegap(ix+4*iy)
return;# fin
# move 1 right of s
if (iy==3):
# cant go under, have to go left over
movegap(kx+4*(iy-1))
movegap(ix-1+4*(iy-1))
movegap(ix-1+4*iy)
movegap(ix+4*iy);
return; #fin
movegap(ix+1+4*(iy-1))
gap_down();gap_down();gap_left();gap_left();gap_up();gap_right();
return; #fin
if (ky==iy):
if (kx<ix):
movegap(ix-1+4*iy)
gap_right();
return; # fin
if (ky<3):
gap_down();
ky+=1;
else:
#have to move up
movegap(ix+4*(iy-1))
movegap(ix-1+4*(iy-1))
movegap(ix-1+4*iy)
gap_right();
return; #fin
# gap below s
movegap(ix+4*(iy+1));
gap_left();gap_up();gap_right();
def move1right(s):
global bboard
i=bboard.index(s);
iy,ix=divmod(i,4);
k=bboard.index(0);
ky,kx=divmod(k,4);
if (ky<iy):
if (kx==ix):
movegap(kx+1+4*ky)
movegap(kx+1+4*iy)
movegap(ix+4*iy);
return; #fin
movegap(kx+4*iy)
if (kx>ix):
movegap(ix+4*iy);
return; #fin
movegap(kx+4*(iy+1))
movegap(ix+1+4*(iy+1))
movegap(ix+1+4*iy);
movegap(ix+4*iy);
return; #fin
if (ky==iy):
if (kx<ix):
if (ky>2):
# bottom row, left of s, have to move 1 up
gap_up()
# move 1 right 1 up of s
movegap(ix+1+4*(ky-1));
gap_down()
gap_left()
return; # fin
# first 1 down
movegap(kx+4*(ky+1))
# to the right of s
movegap(ix+1+4*(ky+1))
gap_up()
gap_left()
return; # fin
# already 1 right of s
movegap(ix+4*iy);
return; #fin
# move gap 1 right and 1 down of s
movegap(kx+4*(iy+1))
movegap(ix+1+4*(iy+1))
gap_up();
gap_left();
def move1down(s):
global bboard
i=bboard.index(s);
iy,ix=divmod(i,4);
k=bboard.index(0);
ky,kx=divmod(k,4);
if (ky<iy):
# gap is over s, move it below
if (kx==ix):
if (ix>2):
# right border, have to move 1 to the left
movegap(kx+4*(iy-1))
movegap(kx-1+4*(iy-1))
movegap(kx-1+4*(iy+1))
gap_up();
return; #fin
# move right of s
movegap(kx+4*(iy-1))
movegap(kx+1+4*(iy-1))
movegap(kx+1+4*(iy+1))
movegap(kx+4*(iy+1))
gap_up(); #fin
movegap(kx+4*(iy+1))
movegap(ix+4*(iy+1))
gap_up(); #fin
if (ky==iy):
gap_down();
ky+=1;
# gap is below s, move 1 under s
movegap(ix+4*(iy+1))
gap_up();
#fin