1

我在 Prolog 中定义了一个 STRIPS Planner 来解决逻辑问题。在对其他更简单的问题进行了几次尝试后,我开始看看它是否可以解决更复杂的问题。我给了他一个 STRIPS 对 peg solitaire 的定义,英文版,考虑到我们不能做对角线移动,最后一个球会落在棋盘的中央,然后尝试了一下,程序进入了循环。这是问题所在: https ://en.wikipedia.org/wiki/Peg_solitaire

这是我的解决方案:

%%%%%%%%%%%%%%%%%%%%%% PLAN %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

accao(nome : move(Xi,Yi,Xf,Yf),
condicoes : [empty(Xf,Yf),ball(Xi,Yi), ball(Xm,Ym)],
efeitos : [ball(Xf,Yf), -ball(Xm,Ym),-ball(Xi,Yi), empty(Xi,Yi), empty(Xm,Ym), -empty(Xf,Yf)],
restricoes : [abs(Xf-Xi)+abs(Yf-Yi)=:=2, abs(Xf-Xi)*abs(Yf-Yi)=:=0, Xi=<Xm, Xm=<Xf, Yi=<Ym, Ym=<Yf]).

inicial([empty(5,5), ball(1,4), ball(1,5), ball(1,6), 
        ball(2,4), ball(2,5), ball(2,6),
        ball(3,4), ball(3,5), ball(3,6),
 ball(4,1), ball(4,2), ball(4,3),ball(4,4), ball(4,5),              ball(4,6),ball(4,7), ball(4,8), ball(4,9),
 ball(5,1), ball(5,2), ball(5,3),ball(5,4),            ball(5,6),ball(5,7), ball(5,8), ball(5,9),
 ball(6,1), ball(6,2), ball(6,3),ball(6,4), ball(6,5), ball(6,6),ball(6,7), ball(6,8), ball(6,9),
        ball(7,4), ball(7,5), ball(7,6), 
        ball(8,4), ball(8,5), ball(8,6),
        ball(9,4), ball(9,5), ball(9,6)]).

objectivos([ball(5,5), empty(1,4), empty(1,5), empty(1,6), 
                    empty(2,4), empty(2,5), empty(2,6),
                    empty(3,4), empty(3,5), empty(3,6),
empty(4,1), empty(4,2), empty(4,3),empty(4,4), empty(4,5), empty(4,6),empty(4,7), empty(4,8), empty(4,9),
empty(5,1), empty(5,2), empty(5,3),empty(5,4),            empty(5,6),empty(5,7), empty(5,8), empty(5,9),
empty(6,1), empty(6,2), empty(6,3),empty(6,4), empty(6,5), empty(6,6),empty(6,7), empty(6,8), empty(6,9),
                    empty(7,4), empty(7,5), empty(7,6), 
                    empty(8,4), empty(8,5), empty(8,6),
                    empty(9,4), empty(9,5), empty(9,6)]).



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%




%%%%%%%%%%%%%%%%%%%%%% PRINT FUNCTION %%%%%%%%%%%%%%%%%%%%%%%%%%%
printExec([]).
printExec([A,E|T]) :- write("Action performed: "),
                  write(A),nl,
                  write("Situation: "),
                  write(E),nl,
                  printExec(T).

 writeExec([I|T]):- write("Initial Situation"),
               write(I),nl,
               printExec(T),
               write("Goal: "),
               objectivos(G),
               write(G),
               write(" satisfied."),nl.
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%




 %%%%%%%%%%%%%%%%%%%% AUXILIAR FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%
 member(E,[E|_]).
 member(E,[_|T]):-member(E,T).

 sub([],_).
 sub([H|T],L):- member(H,L),
           sub(T,L).

 remove(_,[],[]):-!.
 remove(E1, [E2|T], T):- E1 == E2, !. 
 remove(E,[H|T1],[H|T2]):- remove(E,T1,T2).

 add(E,[],[E]):-!.
 add(E1,[E2|T],[E1,E2|T]):- E1 \== E2, !. 
 add(E,[H|T1],[H|T2]):-add(E,T1,T2).

 effects([],S,S).
 effects([-H|Fx],S,N) :-!, 
                   remove(H,S,NS), 
                   effects(Fx,NS,N).
 effects([H|Fx],S,N) :- !, 
                   add(H,S,NS), 
                   effects(Fx,NS,N).

 restriction([]).                                              
 restriction([R|T]) :- R,
                  restriction(T).            
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%




 %%%%%%%%%%%%%%%%%%%%%%% PLAN EXECUTE %%%%%%%%%%%%%%%%%%%%%%%%%%%
 planExecute(P):-testPlan(P,E),writeExec(E),!.

 satisfiedGoal(E):- objectivos(Fn),!,
               sub(Fn,E).

 testPlan(Plan,[I|Exec]) :- inicial(I),              
                       testPlan(Plan,I,Exec,Fn),
                       satisfiedGoal(Fn).   

 testPlan([],Fn,[],Fn).
 testPlan([H|T],S,[H,N|Exec],Fn) :- accao(nome:H, condicoes:C,efeitos:E, restricoes:R), 
                               sub(C,S), 
                               effects(E,S,N), 
                               restriction(R), 
                               testPlan(T,N,Exec,Fn).
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%




 %%%%%%%%%%%%%%%%%%%%%%% FIND PLAN %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 plano(P) :- progressivePlan(P, 0).

 progressivePlan(P, N) :- createPlan(P,_,0,N).
 progressivePlan(P, N) :- \+ createPlan(P,_,0,N),
                     NewN is N + 1, 
                     progressivePlan(P, NewN).

 createPlan(Plan,[I|Exec],N,Max) :- inicial(I),       
                               createPlan(Plan,I,Exec,Fn,N,Max),
                               satisfiedGoal(Fn).       

 createPlan([],Fn,[],Fn,Max,Max):- !.
 createPlan([H|T],S,[H,N|Exec],Fn,Acc, Max) :- accao(nome:H, condicoes:C, efeitos:E, restricoes:R), 
                                          sub(C,S), 
                                          effects(E,S,N),
                                          restriction(R),
                                          NewAcc is Acc+1, 
                                          createPlan(T,N,Exec,Fn,NewAcc, Max). 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%`

我尝试通过只做一两个动作来简化目标,这适用于一个动作,并且当两个动作不相互矛盾时,例如将弹珠移过已经移动的弹珠,通过两个动作进入循环当他们这样做时,就像所说的目标一样:

objectivos([球(4,5),空(3,5),空(5,5),空(6,5)])。

我已经尝试过跟踪和调试,但我似乎找不到问题,尽管我相信它位于问题的制定中,而不是 Planner 本身。有任何想法吗?

4

1 回答 1

0

您的代码中至少存在一个逻辑错误,并且可以进行一些简单的性能调整。这为您的问题提供了部分解决方案。

首先,对于逻辑错误:目标的预期解决方案objectivos([ball(4,5), empty(3,5), empty(5,5), empty(6,5)])似乎是计划P = [move(3, 5, 5, 5), move(6, 5, 4, 5)]。但是这些移动中的第二个对于你的定义是不合法的restricoes:对于这个移动,你有Xi = 6,Xf = 4和条件要求6 =< XmXm <= 4,但这是不可能的。这些约束的想法是确保ball(Xm,Ym)在移动中的其他两个球之间。这是确保这一点的替代公式:

restricoes : [abs(Xf-Xi)+abs(Yf-Yi) =:= 2,
              abs(Xf-Xi)*abs(Yf-Yi) =:= 0,
              abs(Xf-Xm)+abs(Yf-Ym) =:= 1,
              abs(Xi-Xm)+abs(Yi-Ym) =:= 1]

这也排除了之前在跟踪代码时让我感到困惑的情况:以前拥有ball(Xi,Yi) = ball(Xm,Ym).

第二,提高绩效,交换目标effects(E,S,N)restriction(R)在定义上createPlan/6。以前,您在检查其合法性之前计算了移动的效果!因为规划者提出的大多数举动都是非法的,这会浪费很多时间。

然后,为了使整个东西更好用,您可以将plano/1和的定义更改createPlan/4为:

 plano(P) :-
     length(P, PlanLength),
     createPlan(P, _, 0, PlanLength).

 createPlan(Plan,[I|Exec],N,Max) :- inicial(I),
                               N =< Max,
                               createPlan(Plan,I,Exec,Fn,N,Max),
                               satisfiedGoal(Fn).

这比您之前的定义更简单,而且表现也更好。我们可以传入一个完整的计划来检查它是否合法,或者只是传入一个固定长度的列表来询问存在哪些该长度的计划:

?- P = [_,_], plano(P).
P = [move(3, 5, 5, 5), move(6, 5, 4, 5)] ;
false.  % no more solutions

根据您的定义,这将继续循环并计数Max计数器,寻找不存在的进一步解决方案。

使用这个公式,我们可以切换到您的大目标并尝试寻找解决方案(这部分特定于 SWI-Prolog):

?- length(P, N), format('searching for solutions of length ~w~n', [N]), time(plano(P)).
searching for solutions of length 0
% 58 inferences, 0.000 CPU in 0.000 seconds (71% CPU, 2171959 Lips)
searching for solutions of length 1
% 9,709 inferences, 0.001 CPU in 0.001 seconds (100% CPU, 9123980 Lips)
searching for solutions of length 2
% 79,789 inferences, 0.009 CPU in 0.009 seconds (100% CPU, 8778416 Lips)
searching for solutions of length 3
% 477,230 inferences, 0.051 CPU in 0.051 seconds (100% CPU, 9409315 Lips)
searching for solutions of length 4
% 3,412,088 inferences, 0.361 CPU in 0.361 seconds (100% CPU, 9453315 Lips)
searching for solutions of length 5
% 30,967,699 inferences, 3.503 CPU in 3.503 seconds (100% CPU, 8840598 Lips)
searching for solutions of length 6

此时我不得不中断搜索,它变得太慢了。更多的调整当然是可能的,我可能会继续关注这个。

于 2016-12-27T11:39:22.243 回答