% pail pazzle 27-29 Oct 2001 % 水差し問題 pail('4Lj','3Lj'). % modified: 23 Dec 2002. % modified: 2008.1.17 unicode(utf-8) % test command go1, go2, and plan have added. go1:-test(state(2,0),1). go2:-test(state(2,0),2). plan(A,B,L):-pail(state(2,0),A,B,L). %%%%%%% ルールベース %%%%%%%% % 水槽から桶に汲む。水槽から桶に吐き出す。 rule(1,state(X,0)->state(X,3)). rule(2,state(0,Y)->state(4,Y)). rule(3,state(X,Z)->state(X,0)):- Z > 0, Z =< 3, X > 0. rule(4,state(Z,Y)->state(0,Y)):- Z > 0, Z =< 4, Y > 0. % 桶の水が空になるまで、他方の桶へ注ぎ込む。 rule(5,state(Z,Y)->state(X,0)):- Z < 4, Y > 0, (X is Y + Z, X < 4; X is 4). rule(6,state(X,Z)->state(0,Y)):- Z < 3, X > 0, (Y is X + Z, Y < 3; Y is 3). % 他方が一杯になるまで、他方へ注ぐ。 rule(7,state(Z,Y)->state(X,3)):- Z > 0, Z >= 3 - Y, X is Z - (3 - Y). rule(8,state(X,Z)->state(4,Y)):- Z > 0, Z >= 4 - X, Y is Z - (4 - X). % ルールの説明文(日本語) explaining_rule(1,'水槽から3リットルの桶一杯に水を汲む。'). explaining_rule(2,'水槽から4リットルの桶一杯に水を汲む。'). explaining_rule(3,'水槽へ3リットルの桶の水をすべて捨てる。'). explaining_rule(4,'水槽へ4リットルの桶の水をすべて捨てる。'). explaining_rule(5,'3L桶の水を、空になるまで、4L桶に注ぎ込む。'). explaining_rule(6,'4L桶の水を、空になるまで、3L桶に注ぎ込む。'). explaining_rule(7,'3L桶がちょうど一杯になるまで、4L桶の水を注ぐ。'). explaining_rule(8,'4L桶がちょうど一杯になるまで、3L桶の水を注ぐ。'). %%%%%%% 推論エンジン %%%%%%%% % プランニング(計画立案)。 pail(state(0,0),[],[],0). pail(state(4,3),[],[],0). pail(Goal,[G1|H],[N|Hn],L):- % リストから項(Term)を作る。 G1 =.. [state,X1,Y1], % G1 = state(X1,Y1) でも可。 Goal =.. [state,X,Y], % Goal = state(X,Y) でも可。 /* あるいはルールの頭で次のように直接指定すると、後戻りを省略できる。 pail(state(X,Y),[state(X1,Y1)|H],L):- 一方、変数GoalやG1を使うと、本体の後ろの部分で短く書ける。 */ length([G1|H],L), % リストの長さをコントロールする。 % (L > 6 -> !,write('end')); L1 is L - 1, % リカージョン(再帰) pail(G1,H,Hn,L1), rule(N,G1->Goal), Goal \= G1, % 以下は数が非束縛(unbound)になるのを防止する。 Z4 = [0,1,2,3,4], Z3 = [0,1,2,3], member(X,Z4), member(Y,Z3), member(X1,Z4), member(Y1,Z3), % 巡回(循環)を避ける。 \+ member(Goal,H), write(N). %%%%%%% 対話インタフェース %%%%%%%% % といっても対話的でないユーザーインタフェース用ルーチンにすぎません。 test(Goal,2):- Goal=.. [state,_X,_Y], pail(Goal,H,Hn,_L),!, nl,nl,write('手順に沿って桶の水量を絵にする:'),nl, reverse(H,H1),reverse(Hn,Hn1), graphic_plan(H1,Hn1), graphic_plan([Goal],[stop]), write(' end'),nl. test(Goal,Eflag):- Goal=.. [state,_X,_Y], pail(Goal,H,Hn,L),!, nl,nl,write('目盛りのない4リットルと2リットルの2つの桝で'), nl,write('2リットル汲むための手順:'),nl, reverse(H,H1),reverse(Hn,Hn1), nl,(Eflag = 1 -> explain_plan(H1,Hn1); true), write(' end'),nl,%write(H1), nl,write(' 以上のように '),write(Goal),write(' に達するまで '), write('合計 '),write(L),write(' 回汲みます。'),nl. % 求めた行動プランの中のルール適用手順を説明する。 explain_plan([],[]). explain_plan([S1|RemainPlan],[N|RemainRules]):- %rule(N,S2->S1), explaining_rule(N,E), tab(1),write(S1),write(' >'),write('rule '), write(N),write(': '),write(E), write('>'),nl, explain_plan(RemainPlan,RemainRules). graphic_plan([],[]). graphic_plan([S1|RemainPlan],[N|RemainRules]):- %rule(N,S2->S1), S1 = state(X,Y), graph_pail(4,X), graph_pail(3,Y), tab(1),write(S1),write(' >'),write('rule '), write(N),write('>'),nl, graphic_plan(RemainPlan,RemainRules). % 絵を出す。 graph_pail(4,0):- tab(5),write('4L:'),write('|----|'),tab(5). graph_pail(4,1):- tab(5),write('4L:'),write('|#---|'),tab(5). graph_pail(4,2):- tab(5),write('4L:'),write('|##--|'),tab(5). graph_pail(4,3):- tab(5),write('4L:'),write('|###-|'),tab(5). graph_pail(4,4):- tab(5),write('4L:'),write('|####|'),tab(5). graph_pail(3,0):- tab(5),write('3L:'),write('|---|'),tab(5). graph_pail(3,1):- tab(5),write('3L:'),write('|#--|'),tab(5). graph_pail(3,2):- tab(5),write('3L:'),write('|##-|'),tab(5). graph_pail(3,3):- tab(5),write('3L:'),write('|###|'),tab(5). %%%%%% おまけ(屑籠) %%%%% % 以下は最初に考えたプログラム:スタック溢れする。 % 他方が空になるまで、他方から注ぎ込む。 pail1(4,Z):- pail(3,X), pail(4,Y), Z is X + Y, X >= 0, Y >= 0, Z > 0, Z < 4, X =< 3, Y =< 4, Z < 4, Z > Y, write_pail(4,Z). pail1(3,Z):- pail(4,X), pail(3,Y), Z is X + Y, X >= 0, Y >= 0, Z > 0, Z < 3, X =< 4, Y =< 3, Z < 3, Z > Y, write_pail(3,Z). % 他方が一杯になるまで、他方へ注ぐ。 pail1(4,Z):- pail(3,X), pail(4,Y), Z is Y - (3 - X), %or W is 3 - X, W is Y - Z, X >= 0, Y >= 0, Z > 0, X =< 3, Y =< 4, Z < 4, Z < Y, write_pail(4,Z). pail1(3,Z):- pail(4,X), pail(3,Y), Z is Y - (4 - X), %or W is 4 - X, W is Y - Z, X >= 0, Y >= 0, Z > 0, X =< 4, Y =< 3, Z < 3, Z < Y, write_pail(3,Z).