Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/tensorflow/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Prolog 序言:在非常努力的尝试之后优化一个谜题解算器_Prolog_Sliding Tile Puzzle - Fatal编程技术网

Prolog 序言:在非常努力的尝试之后优化一个谜题解算器

Prolog 序言:在非常努力的尝试之后优化一个谜题解算器,prolog,sliding-tile-puzzle,Prolog,Sliding Tile Puzzle,我试图在Prolog中解决15个难题,我需要找到最小的移动次数 这里我们有一个详细答案的拼图示例 我使用的是*搜索,使用曼哈顿距离作为启发 首先,我确保程序是确定性的。规则要么不适用,要么快速失败,要么在没有回溯的情况下运行到完成。为了优化空间,我使用了一个数字来表示状态。使用二项式队列优化搜索队列,使用hashtrie优化访问检查。然而,表现仍然很糟糕 以下是我对二项式队列的实现: :- module(myheap, [myHeapInsert/4, myHeapDeleteMin/4]).

我试图在Prolog中解决15个难题,我需要找到最小的移动次数

这里我们有一个详细答案的拼图示例

我使用的是*搜索,使用曼哈顿距离作为启发

首先,我确保程序是确定性的。规则要么不适用,要么快速失败,要么在没有回溯的情况下运行到完成。为了优化空间,我使用了一个数字来表示状态。使用二项式队列优化搜索队列,使用hashtrie优化访问检查。然而,表现仍然很糟糕

以下是我对二项式队列的实现:

:- module(myheap, [myHeapInsert/4, myHeapDeleteMin/4]).

mergeOneTree(Tree, [], [Tree]) :-
  Tree = binomialQueueNode(_, _, _, _), !.

mergeOneTree(Tree, [Head|Tail], [Tree,Head|Tail]) :- 
  Tree = binomialQueueNode(Size, _, _, _),
  Head = binomialQueueNode(Head_Size, _, _, _),
  Size < Head_Size,
  !.

mergeOneTree(Tree, [Head|Tail], Result) :-
  Tree = binomialQueueNode(Size, Data, Priority, Subtree_Content-Subtree_Indeterminate),
  Head = binomialQueueNode(Size, _, Head_Priority, _),
  Priority < Head_Priority, 
  Concatenation = Subtree_Content-Concatenation_Indeterminate,
  Subtree_Indeterminate = [Head|Concatenation_Indeterminate],
  DoubleSize is Size *2,
  mergeOneTree(binomialQueueNode(DoubleSize, Data, Priority, Concatenation), Tail, Result),
  !.

mergeOneTree(Tree, [Head|Tail], Result) :-
  Tree = binomialQueueNode(Size, _, Priority, _),
  Head = binomialQueueNode(Size, Head_Data, Head_Priority, Head_Subtree_Content-Head_Subtree_Indeterminate),
  Priority >= Head_Priority, 
  Concatenation = Head_Subtree_Content-Concatenation_Indeterminate,
  Head_Subtree_Indeterminate = [Tree|Concatenation_Indeterminate],
  DoubleSize is Size *2,
  mergeOneTree(binomialQueueNode(DoubleSize, Head_Data, Head_Priority, Concatenation), Tail, Result),
  !.

mergeOneTree(Tree, [Head|Tail], [Head|TailResult]) :-
  Tree = binomialQueueNode(Size, _, _, _),
  Head = binomialQueueNode(Head_Size, _, _, _),
  Size > Head_Size,
  mergeOneTree(Tree, Tail, TailResult),
  !.

merge([], X, X) :- !.
merge([H|T], X, R) :- mergeOneTree(H, X, I), merge(T, I, R), !.

findMinTree([H|T], MinTree, Others) :-
  findMinTree(H, T, MinTree, Others), !.

findMinTree(CurrentMin, [], CurrentMin, []) :- !.

findMinTree(CurrentMin, [Candidate|Tail], ResultMinTree, [Candidate|ResultOthers]) :-
  CurrentMin = binomialQueueNode(_, _, CurrentMin_Priority, _),
  Candidate = binomialQueueNode(_, _, Candidate_Priority, _),
  Candidate_Priority > CurrentMin_Priority,
  findMinTree(CurrentMin, Tail, ResultMinTree, ResultOthers),
  !.

findMinTree(CurrentMin, [Candidate|Tail], ResultMinTree, [CurrentMin|ResultOthers]) :-
  CurrentMin = binomialQueueNode(_, _, CurrentMin_Priority, _),
  Candidate = binomialQueueNode(_, _, Candidate_Priority, _),
  Candidate_Priority =< CurrentMin_Priority,
  findMinTree(Candidate, Tail, ResultMinTree, ResultOthers),
  !.

myHeapInsert(BeforeTree, Data, Priority, AfterTree) :- 
  mergeOneTree(binomialQueueNode(1, Data, Priority, Dummy-Dummy), BeforeTree, AfterTree), !.

myHeapDeleteMin(BeforeTree, MinData, MinPriority, AfterTree) :- 
  findMinTree(BeforeTree, MinTree, Others), 
  MinTree = binomialQueueNode(_, MinData, MinPriority, MinTreeSubTree_Content-MinTreeSubTree_Indeterminate),
  MinTreeSubTree_Indeterminate = [],
  merge(Others, MinTreeSubTree_Content, AfterTree),
  !.
最后是解谜代码:

:- set_prolog_stack(global, limit(100 000 000 000)).
:- set_prolog_stack(trail,  limit(20 000 000 000)).
:- set_prolog_stack(local,  limit(2 000 000 000)).

:- use_module(myheap).
:- use_module(myhash).

hash([], 0) :- !.
hash([H|T], Hash) :- hash(T, R), Hash is (H + 23 * R) mod 1073741824, !.

flatten([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]],[A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P]) :- !.

moves([0,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([X10,X01,X02,X03,0,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X10),move([X01,0,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X01)]) :- !.
moves([X00,0,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([0,X00,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X00),move([X00,X11,X02,X03,X10,0,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X11),move([X00,X02,0,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X02)]) :- !.
moves([X00,X01,0,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([X00,0,X01,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X01),move([X00,X01,X12,X03,X10,X11,0,X13,X20,X21,X22,X23,X30,X31,X32,X33],X12),move([X00,X01,X03,0,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X03)]):- !.
moves([X00,X01,X02,0,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([X00,X01,0,X02,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X02),move([X00,X01,X02,X13,X10,X11,X12,0,X20,X21,X22,X23,X30,X31,X32,X33],X13)]):- !.
moves([X00,X01,X02,X03,0,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([0,X01,X02,X03,X00,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X00),move([X00,X01,X02,X03,X20,X11,X12,X13,0,X21,X22,X23,X30,X31,X32,X33],X20),move([X00,X01,X02,X03,X11,0,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X11)]):- !.
moves([X00,X01,X02,X03,X10,0,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([X00,0,X02,X03,X10,X01,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X01),move([X00,X01,X02,X03,0,X10,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X10),move([X00,X01,X02,X03,X10,X21,X12,X13,X20,0,X22,X23,X30,X31,X32,X33],X21),move([X00,X01,X02,X03,X10,X12,0,X13,X20,X21,X22,X23,X30,X31,X32,X33],X12)]):- !.
moves([X00,X01,X02,X03,X10,X11,0,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([X00,X01,0,X03,X10,X11,X02,X13,X20,X21,X22,X23,X30,X31,X32,X33],X02),move([X00,X01,X02,X03,X10,0,X11,X13,X20,X21,X22,X23,X30,X31,X32,X33],X11),move([X00,X01,X02,X03,X10,X11,X22,X13,X20,X21,0,X23,X30,X31,X32,X33],X22),move([X00,X01,X02,X03,X10,X11,X13,0,X20,X21,X22,X23,X30,X31,X32,X33],X13)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,0,X20,X21,X22,X23,X30,X31,X32,X33],[move([X00,X01,X02,0,X10,X11,X12,X03,X20,X21,X22,X23,X30,X31,X32,X33],X03),move([X00,X01,X02,X03,X10,X11,0,X12,X20,X21,X22,X23,X30,X31,X32,X33],X12),move([X00,X01,X02,X03,X10,X11,X12,X23,X20,X21,X22,0,X30,X31,X32,X33],X23)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,0,X21,X22,X23,X30,X31,X32,X33],[move([X00,X01,X02,X03,0,X11,X12,X13,X10,X21,X22,X23,X30,X31,X32,X33],X10),move([X00,X01,X02,X03,X10,X11,X12,X13,X30,X21,X22,X23,0,X31,X32,X33],X30),move([X00,X01,X02,X03,X10,X11,X12,X13,X21,0,X22,X23,X30,X31,X32,X33],X21)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,0,X22,X23,X30,X31,X32,X33],[move([X00,X01,X02,X03,X10,0,X12,X13,X20,X11,X22,X23,X30,X31,X32,X33],X11),move([X00,X01,X02,X03,X10,X11,X12,X13,0,X20,X22,X23,X30,X31,X32,X33],X20),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X31,X22,X23,X30,0,X32,X33],X31),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X22,0,X23,X30,X31,X32,X33],X22)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,0,X23,X30,X31,X32,X33],[move([X00,X01,X02,X03,X10,X11,0,X13,X20,X21,X12,X23,X30,X31,X32,X33],X12),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,0,X21,X23,X30,X31,X32,X33],X21),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X32,X23,X30,X31,0,X33],X32),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X23,0,X30,X31,X32,X33],X23)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,0,X30,X31,X32,X33],[move([X00,X01,X02,X03,X10,X11,X12,0,X20,X21,X22,X13,X30,X31,X32,X33],X13),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,0,X22,X30,X31,X32,X33],X22),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X33,X30,X31,X32,0],X33)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,0,X31,X32,X33],[move([X00,X01,X02,X03,X10,X11,X12,X13,0,X21,X22,X23,X20,X31,X32,X33],X20),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X31,0,X32,X33],X31)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,0,X32,X33],[move([X00,X01,X02,X03,X10,X11,X12,X13,X20,0,X22,X23,X30,X21,X32,X33],X21),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,0,X30,X32,X33],X30),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X32,0,X33],X32)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,0,X33],[move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,0,X23,X30,X31,X22,X33],X22),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,0,X31,X33],X31),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X33,0],X33)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,0],[move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,0,X30,X31,X32,X23],X23),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,0,X32],X32)]):- !.
debug([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33]) :- true,write(X00),write('\t'),write(X01),write('\t'),write(X02),write('\t'),write(X03),write('\t'),write('\n'),write(X10),write('\t'),write(X11),write('\t'),write(X12),write('\t'),write(X13),write('\t'),write('\n'),write(X20),write('\t'),write(X21),write('\t'),write(X22),write('\t'),write(X23),write('\t'),write('\n'),write(X30),write('\t'),write(X31),write('\t'),write(X32),write('\t'),write(X33),write('\t'),write('\n'),write('\n').

easy([[1, 2, 3, 4],[5, 6, 0, 8],[9, 10, 7,  11],[13, 14, 15, 12]]).
hard([[15, 14,  1,  6],[ 9, 11,  4, 12],[ 0, 10,  7,  3],[13,  8,  5,  2]]).

dist(A, B, C) :- C is A - B, C >= 0, !.
dist(A, B, C) :- C is B - A, C >= 0, !.

manhattan(L, D) :- manhattan(L, 0, D), !.
manhattan([], _, 0) :- !.
manhattan([0|T], L, D) :- 
  NextL is L + 1, 
  manhattan(T, NextL, D),
  !.
manhattan([H|T], L, D) :- 
  H > 0, 
  CellCol is L mod 4, 
  CellRow is (L - CellCol) / 4, 
  DataCol is (H - 1) mod 4, 
  DataRow is (H - 1- DataCol) / 4, 
  dist(CellCol, DataCol, CD), 
  dist(CellRow, DataRow, RD), 
  NextL is L + 1, 
  manhattan(T, NextL, TD), 
  D is CD + RD + TD,
  !.

compress([], 0) :- !.
compress([H|T], L) :- compress(T, I), L is I * 16 + H, !.

uncompress(L, R) :- uncompress(L, 16, R), !.
uncompress(_, 0, []) :- !.
uncompress(L, C, [H|T]) :- C > 0, D is C - 1, H is L mod 16, R is (L - H) / 16, uncompress(R, D, T), !.

search(CurrentBoard) :-
  myHashEmpty(EmptyHash),
  hash(CurrentBoard, CurrentBoardHash),  
  manhattan(CurrentBoard, CurrentBoardPriority),
  compress(CurrentBoard, CurrentBoardCompressed),
  myHashPut(EmptyHash, CurrentBoardCompressed, CurrentBoardHash, _, Enqueued),  
  search(CurrentBoard, CurrentBoardPriority, 0, Enqueued, []), !.

search(CurrentBoard, _, _, _, _) :-
  manhattan(CurrentBoard, 0),
  !.

search(CurrentBoard, CurrentBoardPriority, CurrentStep, Enqueued, Queue) :-
  CurrentBoardPriority > 0,
  moves(CurrentBoard, NextMoves),
  update_enqueued_queue_all(Enqueued, Queue, NextMoves, CurrentStep, NextEnqueued, ImmediateQueue),
  myHeapDeleteMin(ImmediateQueue, NextState, NextBoardPriority, NextQueue),
  state(NextBoardCompressed, _, NextStep) = NextState,
  uncompress(NextBoardCompressed, NextBoard),
  search(NextBoard, NextBoardPriority, NextStep, NextEnqueued, NextQueue),
  !.

update_enqueued_queue_all(Enqueued, Queue, [], _, Enqueued, Queue) :- !.
update_enqueued_queue_all(Enqueued, Queue, [Head|Tail], CurrentStep, NextEnqueued, NextQueue) :-
  move(HeadBoard, HeadMove) = Head,
  hash(HeadBoard, HeadHash),
  compress(HeadBoard, HeadBoardCompressed),
  update_enqueued_queue(Enqueued, Queue, HeadBoard, HeadBoardCompressed, HeadHash, HeadMove, CurrentStep, ImmediateEnqueued, ImmediateQueue),
  update_enqueued_queue_all(ImmediateEnqueued, ImmediateQueue, Tail, CurrentStep, NextEnqueued, NextQueue),
  !.

update_enqueued_queue(Enqueued, Queue, Board, BoardCompressed, BoardHash, Move, CurrentStep, NextEnqueued, NextQueue) :-
  myHashGet(Enqueued, BoardCompressed, BoardHash, _), NextEnqueued = Enqueued, NextQueue = Queue, !;  
  NextStep is CurrentStep + 1, manhattan(Board, BoardHeuristic), BoardPriority is NextStep + BoardHeuristic, myHashPut(Enqueued, BoardCompressed, BoardHash, _, NextEnqueued), myHeapInsert(Queue, state(BoardCompressed, Move, NextStep), BoardPriority, NextQueue), !.

solve(X) :- flatten(X, Y), search(Y).

run :- hard(X), solve(X).
代码在我的计算机上不会在几分钟内运行完成。我做了一个小目标的简介(跑步)(例如,在曼哈顿距离为10时停车),大部分时间都花在垃圾收集上

我已经读到StackOverflow上的另一个线程谈论同样的事情,“解决方案”是使用约束库,这是我不能使用的


我用尽了我的技巧,坦白地说,我不是一个经常使用Prolog的程序员。你知道我怎样才能在速度方面做得更好吗

Prolog并不完全以速度著称;你能用另一种语言吗?如果我用C或类似的语言实现完全相同的算法,我会得到什么样的速度,从而建立我应该期望的某种基线,这会很有趣。我想我可以忍受10倍的减速,但100倍的减速可能意味着某些事情确实出了问题。然而,为了这篇文章,我们可以继续使用Prolog吗?假设时间主要是回溯,也许有更好的启发式方法(我没有)。但是,是什么让你认为性能“糟糕”呢?这对我来说似乎很好,找到解决方案的最短路径非常重要。在我的电脑上,你的代码解决了一个难题,需要在不到一秒钟的时间内进行25次移动,这似乎是可以接受的——比它快得多,可能需要调用C或C++,或者更聪明的Prolog技巧,而不是我知道怎么做。我的C++解析器需要大约20秒来解决一个需要33个动作的难题,而且可能在几分钟内也不会运行在“硬”实例上。我不知道25步拼图的程序很快。我正在尝试这个链接中给出的谜题:,这肯定是一个很难的谜题,因为它需要53个步骤,在我的电脑上,程序永远不会停止。想知道它是如何在C++的求解器上实现的?
:- set_prolog_stack(global, limit(100 000 000 000)).
:- set_prolog_stack(trail,  limit(20 000 000 000)).
:- set_prolog_stack(local,  limit(2 000 000 000)).

:- use_module(myheap).
:- use_module(myhash).

hash([], 0) :- !.
hash([H|T], Hash) :- hash(T, R), Hash is (H + 23 * R) mod 1073741824, !.

flatten([[A,B,C,D],[E,F,G,H],[I,J,K,L],[M,N,O,P]],[A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P]) :- !.

moves([0,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([X10,X01,X02,X03,0,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X10),move([X01,0,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X01)]) :- !.
moves([X00,0,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([0,X00,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X00),move([X00,X11,X02,X03,X10,0,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X11),move([X00,X02,0,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X02)]) :- !.
moves([X00,X01,0,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([X00,0,X01,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X01),move([X00,X01,X12,X03,X10,X11,0,X13,X20,X21,X22,X23,X30,X31,X32,X33],X12),move([X00,X01,X03,0,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X03)]):- !.
moves([X00,X01,X02,0,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([X00,X01,0,X02,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X02),move([X00,X01,X02,X13,X10,X11,X12,0,X20,X21,X22,X23,X30,X31,X32,X33],X13)]):- !.
moves([X00,X01,X02,X03,0,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([0,X01,X02,X03,X00,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X00),move([X00,X01,X02,X03,X20,X11,X12,X13,0,X21,X22,X23,X30,X31,X32,X33],X20),move([X00,X01,X02,X03,X11,0,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X11)]):- !.
moves([X00,X01,X02,X03,X10,0,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([X00,0,X02,X03,X10,X01,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X01),move([X00,X01,X02,X03,0,X10,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33],X10),move([X00,X01,X02,X03,X10,X21,X12,X13,X20,0,X22,X23,X30,X31,X32,X33],X21),move([X00,X01,X02,X03,X10,X12,0,X13,X20,X21,X22,X23,X30,X31,X32,X33],X12)]):- !.
moves([X00,X01,X02,X03,X10,X11,0,X13,X20,X21,X22,X23,X30,X31,X32,X33],[move([X00,X01,0,X03,X10,X11,X02,X13,X20,X21,X22,X23,X30,X31,X32,X33],X02),move([X00,X01,X02,X03,X10,0,X11,X13,X20,X21,X22,X23,X30,X31,X32,X33],X11),move([X00,X01,X02,X03,X10,X11,X22,X13,X20,X21,0,X23,X30,X31,X32,X33],X22),move([X00,X01,X02,X03,X10,X11,X13,0,X20,X21,X22,X23,X30,X31,X32,X33],X13)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,0,X20,X21,X22,X23,X30,X31,X32,X33],[move([X00,X01,X02,0,X10,X11,X12,X03,X20,X21,X22,X23,X30,X31,X32,X33],X03),move([X00,X01,X02,X03,X10,X11,0,X12,X20,X21,X22,X23,X30,X31,X32,X33],X12),move([X00,X01,X02,X03,X10,X11,X12,X23,X20,X21,X22,0,X30,X31,X32,X33],X23)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,0,X21,X22,X23,X30,X31,X32,X33],[move([X00,X01,X02,X03,0,X11,X12,X13,X10,X21,X22,X23,X30,X31,X32,X33],X10),move([X00,X01,X02,X03,X10,X11,X12,X13,X30,X21,X22,X23,0,X31,X32,X33],X30),move([X00,X01,X02,X03,X10,X11,X12,X13,X21,0,X22,X23,X30,X31,X32,X33],X21)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,0,X22,X23,X30,X31,X32,X33],[move([X00,X01,X02,X03,X10,0,X12,X13,X20,X11,X22,X23,X30,X31,X32,X33],X11),move([X00,X01,X02,X03,X10,X11,X12,X13,0,X20,X22,X23,X30,X31,X32,X33],X20),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X31,X22,X23,X30,0,X32,X33],X31),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X22,0,X23,X30,X31,X32,X33],X22)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,0,X23,X30,X31,X32,X33],[move([X00,X01,X02,X03,X10,X11,0,X13,X20,X21,X12,X23,X30,X31,X32,X33],X12),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,0,X21,X23,X30,X31,X32,X33],X21),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X32,X23,X30,X31,0,X33],X32),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X23,0,X30,X31,X32,X33],X23)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,0,X30,X31,X32,X33],[move([X00,X01,X02,X03,X10,X11,X12,0,X20,X21,X22,X13,X30,X31,X32,X33],X13),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,0,X22,X30,X31,X32,X33],X22),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X33,X30,X31,X32,0],X33)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,0,X31,X32,X33],[move([X00,X01,X02,X03,X10,X11,X12,X13,0,X21,X22,X23,X20,X31,X32,X33],X20),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X31,0,X32,X33],X31)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,0,X32,X33],[move([X00,X01,X02,X03,X10,X11,X12,X13,X20,0,X22,X23,X30,X21,X32,X33],X21),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,0,X30,X32,X33],X30),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X32,0,X33],X32)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,0,X33],[move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,0,X23,X30,X31,X22,X33],X22),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,0,X31,X33],X31),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X33,0],X33)]):- !.
moves([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,0],[move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,0,X30,X31,X32,X23],X23),move([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,0,X32],X32)]):- !.
debug([X00,X01,X02,X03,X10,X11,X12,X13,X20,X21,X22,X23,X30,X31,X32,X33]) :- true,write(X00),write('\t'),write(X01),write('\t'),write(X02),write('\t'),write(X03),write('\t'),write('\n'),write(X10),write('\t'),write(X11),write('\t'),write(X12),write('\t'),write(X13),write('\t'),write('\n'),write(X20),write('\t'),write(X21),write('\t'),write(X22),write('\t'),write(X23),write('\t'),write('\n'),write(X30),write('\t'),write(X31),write('\t'),write(X32),write('\t'),write(X33),write('\t'),write('\n'),write('\n').

easy([[1, 2, 3, 4],[5, 6, 0, 8],[9, 10, 7,  11],[13, 14, 15, 12]]).
hard([[15, 14,  1,  6],[ 9, 11,  4, 12],[ 0, 10,  7,  3],[13,  8,  5,  2]]).

dist(A, B, C) :- C is A - B, C >= 0, !.
dist(A, B, C) :- C is B - A, C >= 0, !.

manhattan(L, D) :- manhattan(L, 0, D), !.
manhattan([], _, 0) :- !.
manhattan([0|T], L, D) :- 
  NextL is L + 1, 
  manhattan(T, NextL, D),
  !.
manhattan([H|T], L, D) :- 
  H > 0, 
  CellCol is L mod 4, 
  CellRow is (L - CellCol) / 4, 
  DataCol is (H - 1) mod 4, 
  DataRow is (H - 1- DataCol) / 4, 
  dist(CellCol, DataCol, CD), 
  dist(CellRow, DataRow, RD), 
  NextL is L + 1, 
  manhattan(T, NextL, TD), 
  D is CD + RD + TD,
  !.

compress([], 0) :- !.
compress([H|T], L) :- compress(T, I), L is I * 16 + H, !.

uncompress(L, R) :- uncompress(L, 16, R), !.
uncompress(_, 0, []) :- !.
uncompress(L, C, [H|T]) :- C > 0, D is C - 1, H is L mod 16, R is (L - H) / 16, uncompress(R, D, T), !.

search(CurrentBoard) :-
  myHashEmpty(EmptyHash),
  hash(CurrentBoard, CurrentBoardHash),  
  manhattan(CurrentBoard, CurrentBoardPriority),
  compress(CurrentBoard, CurrentBoardCompressed),
  myHashPut(EmptyHash, CurrentBoardCompressed, CurrentBoardHash, _, Enqueued),  
  search(CurrentBoard, CurrentBoardPriority, 0, Enqueued, []), !.

search(CurrentBoard, _, _, _, _) :-
  manhattan(CurrentBoard, 0),
  !.

search(CurrentBoard, CurrentBoardPriority, CurrentStep, Enqueued, Queue) :-
  CurrentBoardPriority > 0,
  moves(CurrentBoard, NextMoves),
  update_enqueued_queue_all(Enqueued, Queue, NextMoves, CurrentStep, NextEnqueued, ImmediateQueue),
  myHeapDeleteMin(ImmediateQueue, NextState, NextBoardPriority, NextQueue),
  state(NextBoardCompressed, _, NextStep) = NextState,
  uncompress(NextBoardCompressed, NextBoard),
  search(NextBoard, NextBoardPriority, NextStep, NextEnqueued, NextQueue),
  !.

update_enqueued_queue_all(Enqueued, Queue, [], _, Enqueued, Queue) :- !.
update_enqueued_queue_all(Enqueued, Queue, [Head|Tail], CurrentStep, NextEnqueued, NextQueue) :-
  move(HeadBoard, HeadMove) = Head,
  hash(HeadBoard, HeadHash),
  compress(HeadBoard, HeadBoardCompressed),
  update_enqueued_queue(Enqueued, Queue, HeadBoard, HeadBoardCompressed, HeadHash, HeadMove, CurrentStep, ImmediateEnqueued, ImmediateQueue),
  update_enqueued_queue_all(ImmediateEnqueued, ImmediateQueue, Tail, CurrentStep, NextEnqueued, NextQueue),
  !.

update_enqueued_queue(Enqueued, Queue, Board, BoardCompressed, BoardHash, Move, CurrentStep, NextEnqueued, NextQueue) :-
  myHashGet(Enqueued, BoardCompressed, BoardHash, _), NextEnqueued = Enqueued, NextQueue = Queue, !;  
  NextStep is CurrentStep + 1, manhattan(Board, BoardHeuristic), BoardPriority is NextStep + BoardHeuristic, myHashPut(Enqueued, BoardCompressed, BoardHash, _, NextEnqueued), myHeapInsert(Queue, state(BoardCompressed, Move, NextStep), BoardPriority, NextQueue), !.

solve(X) :- flatten(X, Y), search(Y).

run :- hard(X), solve(X).