Replies: 13 comments 36 replies
-
Beta Was this translation helpful? Give feedback.
-
grumble ok it runs but it is not correct. There certainly exist infinite plans but the current code does not terminate for plans greater than length 2. ?- state1(Start), plan(Start, [on(a,b)], Plan).
%@ Start = [clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)], Plan = [move(c,a,2),move(a,1,b)]
%@ ; Start = [clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)], Plan = [move(c,a,2),move(a,1,b)]
%@ ; Start = [clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)], Plan = [move(c,a,2),move(a,1,b)]
%@ ; Start = [clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)], Plan = [move(c,a,2),move(a,1,b)]
%@ ; Start = [clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)], Plan = [move(c,a,2),move(a,1,b)]
%@ ; Start = [clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)], Plan = [move(c,a,2),move(a,1,b)], dif:dif(clear(2),_A), dif:dif(clear(a),_A), dif:dif(clear(b),_A), dif:dif(on(a,1),_A), dif:dif(on(c,a),_A)
%@ ; Start = [clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)], Plan = [move(c,a,2),move(a,1,b)], dif:dif(clear(2),_A), dif:dif(clear(a),_A), dif:dif(clear(b),_A), dif:dif(on(a,1),_A), dif:dif(on(c,a),_A)
... snip ...
%@ ; Start = [clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)], Plan = [move(c,a,b),move(a,1,b)], dif:dif(clear(b),_A), dif:dif(on(a,1),_A), dif:dif(on(a,b),_A)
%@ ; Start = [clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)], Plan = [move(c,a,b),move(a,1,b)], dif:dif(clear(b),_A), dif:dif(on(a,1),_A), dif:dif(on(a,b),_A)
%@ ; Start = [clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)], Plan = [move(c,a,b),move(a,1,b)], dif:dif(clear(b),_A), dif:dif(on(a,1),_A), dif:dif(on(a,b),_A), dif:dif(clear(a),_B), dif:dif(clear(b),_B), dif:dif(on(a,1),_B), dif:dif(on(c,a),_B)
%@ ; Start = [clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)], Plan = [move(c,a,b),move(a,1,b)], dif:dif(clear(b),_A), dif:dif(on(a,1),_A), dif:dif(on(a,b),_A), dif:dif(clear(a),_B), dif:dif(clear(b),_B), dif:dif(on(a,1),_B), dif:dif(on(c,a),_B)
%@ ; nontermination |
Beta Was this translation helpful? Give feedback.
-
I feel like xs_ys_setdiff(Xs, Ys, Diff) :- tpartition(i_memberd_t(Ys), Xs, _, Diff). I haven’t tested it, though. |
Beta Was this translation helpful? Give feedback.
-
You can simplify |
Beta Was this translation helpful? Give feedback.
-
I got so frustrated with the book version that I ended up writing a DCG version that is iterative DFS. It's much faster. Now I'm working on a version that uses the regression method. :- use_module(library(dif)).
:- use_module(library(reif)).
:- use_module(library(lists)).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
xs_ys_setdiff(Xs, Ys, Diff): Diff is the set difference of Xs and Ys.
https://github.com/mthom/scryer-prolog/discussions/2507#discussioncomment-10471435
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
xs_ys_setdiff([], _, []).
xs_ys_setdiff([X|Xs], Ys, Diff0) :-
if_(memberd_t(X, Ys),
Diff0=Diff,
Diff0=[X|Diff]
),
xs_ys_setdiff(Xs, Ys, Diff).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
xs_ys_setunion(Xs, Ys, Union): Union is the set union of Xs and Ys.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
xs_ys_setunion([], L, L).
xs_ys_setunion([X|Xs], Ys, Union0) :-
if_(memberd_t(X, Ys),
Union0=Union,
Union0=[X|Union]
),
xs_ys_setunion(Xs, Ys, Union).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
object(X): X is a place or a block.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
object(X) :-
( place(X)
; block(X)
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
State Change relations.
clear(Block): denotes a block that hgas nothing above it
on(Block,Object): denotes a block being on either another block or place
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
%% can(Action, PreConditions): Preconditions are required to be in present for this move
%% to be admissible.
can(move(Block, From, To), [clear(Block), clear(To), on(Block,From)]) :-
block(Block),
object(To),
dif(To, Block),
object(From),
dif(Block, From).
%% adds(Action, Effects): These are the effects added to the State when this move happens
adds(move(X, From, To), [on(X,To), clear(From)]).
%% deletes(Action, Undoes): The Undoes are removed from the world state when this move happens
deletes(move(X,From,To), [on(X,From), clear(To)]).
%% supported(WorldState, Conditions): the world state contains the supporting conditions
supported(WorldState, Conditions) :-
xs_ys_setdiff(Conditions, WorldState, []).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
results_in(IntitialWorldState, Undoes, Effects, WorldState):
The new world state after removing the undoes and apply the effects
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
results_in(WorldState0, Undoes, Effects, WorldState) :-
xs_ys_setdiff(WorldState0, Undoes, WorldState1),
xs_ys_setunion(WorldState1, Effects, WorldState).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
goal(WorldState, Goals): The goal is achieved if all Goals are
present in the WorldState
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
goal(WorldState, Goals) :-
xs_ys_setdiff(Goals, WorldState, []).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
goal_t(WorldState,Goals,Truth): Truth=true if goal(WorldState, Goals)
else Truth=false.
It turns out I didn't need this, but I'm so excited to discover
I can make a reif predicate using (=)/3 that I'm leaving it in
so I remember.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
goal_t(WorldState,Goals,T) :-
xs_ys_setdiff(Goals, WorldState, Result),
=(Result, [], T).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
moves(state(WorldState,Goals)):
Possible moves carried out by the planner.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
moves(state(WorldState, Goals)) -->
{ goal(WorldState, Goals) }.
moves(state(WorldState0, Goals)) --> [Move],
{ can(Move, Conditions), % action precondtions
supported(WorldState0, Conditions), % determine if action can happen
adds(Move, Effects), % determine effects
deletes(Move, Undoes), % determine undoes
results_in(WorldState0, Undoes, Effects, WorldState) % determine WorldState'
},
moves(state(WorldState, Goals)).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Starting World State.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
block(a).
block(b).
block(c).
place(1).
place(2).
place(3).
place(4).
% A state in the blocks world
%
% c
% a b
% = = = =
% place 1 2 3 4
start([clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)]).
solve(Goal, Moves) :-
start(Start),
length(Moves, _),
phrase(moves(state(Start, Goal)), Moves).
?- solve([on(a,b),on(c,a)], Moves).
%@ Moves = [move(c,a,2),move(a,1,b),move(c,2,a)]
%@ ; Moves = [move(c,a,4),move(a,1,b),move(c,4,a)]
%@ ; Moves = [move(b,3,2),move(c,a,3),move(a,1,b),move(c,3,a)]
%@ ; Moves = [move(b,3,2),move(c,a,4),move(a,1,b),move(c,4,a)]
%@ ; Moves = [move(b,3,4),move(c,a,2),move(a,1,b),move(c,2,a)]
%@ ; Moves = [move(b,3,4),move(c,a,3),move(a,1,b),move(c,3,a)]
%@ ; Moves = [move(c,a,2),move(a,1,4),move(a,4,b),move(c,2,a)]
%@ ; Moves = [move(c,a,2),move(a,1,b),move(c,2,1),move(c,1,a)]
%@ ; Moves = [move(c,a,2),move(a,1,b),move(c,2,4),move(c,4,a)]
%@ ; Moves = [move(c,a,2),move(a,1,c),move(a,c,b),move(c,2,a)]
%@ ; Moves = [move(c,a,2),move(b,3,4),move(a,1,b),move(c,2,a)]
%@ ; Moves = [move(c,a,2),move(c,2,4),move(a,1,b),move(c,4,a)]
%@ ; Moves = [move(c,a,4),move(a,1,2),move(a,2,b),move(c,4,a)]
%@ ; Moves = [move(c,a,4),move(a,1,b),move(c,4,1),move(c,1,a)]
%@ ; Moves = [move(c,a,4),move(a,1,b),move(c,4,2),move(c,2,a)]
%@ ; Moves = [move(c,a,4),move(a,1,c),move(a,c,b),move(c,4,a)]
%@ ; Moves = [move(c,a,4),move(b,3,2),move(a,1,b),move(c,4,a)]
%@ ; Moves = [move(c,a,4),move(c,4,2),move(a,1,b),move(c,2,a)]
%@ ; Moves = [move(c,a,b),move(c,b,2),move(a,1,b),move(c,2,a)]
%@ ; Moves = [move(c,a,b),move(c,b,4),move(a,1,b),move(c,4,a)]
%@ ; Moves = [move(b,3,2),move(b,2,3),move(c,a,2),move(a,1,b),move(c,2,a)]
%@ ; Moves = [move(b,3,2),move(b,2,3),move(c,a,4),move(a,1,b),move(c,4,a)]
%@ ; Moves = [move(b,3,2),move(b,2,4),move(c,a,2),move(a,1,b),move(c,2,a)]
%@ ; Moves = [move(b,3,2),move(b,2,4),move(c,a,3),move(a,1,b),move(c,3,a)]
%@ ; Moves = [move(b,3,2),move(c,a,3),move(a,1,4),move(a,4,b),move(c,3,a)]
%@ ; Moves = [move(b,3,2),move(c,a,3),move(a,1,b),move(c,3,1),move(c,1,a)]
%@ ; Moves = [move(b,3,2),move(c,a,3),move(a,1,b),move(c,3,4),move(c,4,a)]
%@ ; Moves = [move(b,3,2),move(c,a,3),move(a,1,c),move(a,c,b),move(c,3,a)]
%@ ; Moves = [move(b,3,2),move(c,a,3),move(b,2,4),move(a,1,b),move(c,3,a)]
%@ ; Moves = [move(b,3,2),move(c,a,3),move(c,3,4),move(a,1,b),move(c,4,a)]
%@ ; Moves = [move(b,3,2),move(c,a,4),move(a,1,3),move(a,3,b),move(c,4,a)]
%@ ; Moves = [move(b,3,2),move(c,a,4),move(a,1,b),move(c,4,1),move(c,1,a)]
%@ ; Moves = [move(b,3,2),move(c,a,4),move(a,1,b),move(c,4,3),move(c,3,a)]
%@ ; Moves = [move(b,3,2),move(c,a,4),move(a,1,c),move(a,c,b),move(c,4,a)]
%@ ; Moves = [move(b,3,2),move(c,a,4),move(b,2,3),move(a,1,b),move(c,4,a)]
%@ ; error('$interrupt_thrown',repl/0). |
Beta Was this translation helpful? Give feedback.
-
This is overthinking it to the point of uselessness, but you could write xs_ys_setunion(Xs, Ys, Union) :-
xs_ys_setdiff(Xs, Ys, UniqXs),
append(UniqXs, Ys, Union). |
Beta Was this translation helpful? Give feedback.
-
So the regression case is turning out to be much harder. Here's the basic algorithm: moves(state(WorldState,Goals)) -->
{
format("WorldState/Goals: ~t~w~t~w~t~250|~n", [WorldState, Goals]),
xs_ys_setdiff(WorldState, Goals, [])
}.
moves(state(WorldState, Goals0)) -->
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Goal regression version
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
{
xs_ys_setdiff(WorldState, Goals0, Result), % sanity check to make sure you aren't solving a solved problem
=(Result, [], false),
adds(Move, Effects), % find a move with some effects
member(Effect, Effects), % where there is at least one effect
member(Effect, Goals0), % that is also a goal
deletes(Move, Undoes), % get the undoes of the action
member(Undo, Undoes), % and make sure there is no undo
memberd_t(Undo, Goals0, false), % that is also a goal (or it would create an inconsistent state)
can(Move, Conditions), % take the preconditions of the action
xs_ys_setdiff(Conditions, Goals0, NewGoals), % check out any new goals
any_impossible_t(NewGoals, Goals0, false), % and make sure they do not create an impossible state
xs_ys_setunion(NewGoals, Goals0, Goals), % and union them with the goals, as if the action happened
format("New Goal/Goals: ~t~t~w~t~w~250|~n", [NewGoals, Goals0])
},
[Move],
moves(state(WorldState, Goals)). from some (shameful) trace statements (because I don't know a better way yet), I have determined that the most likely point of failure is my ?- any_impossible_t([on(b,4)], [clear(1),on(b,a),clear(b),clear(c),on(b,1),on(b,c)], true).
%@ true
%@ ; true
%@ ; true
%@ ; false.
?- any_impossible_t([on(b,4)], [clear(1),on(b,a),clear(b),clear(c),on(b,1),on(b,c)], false).
%@ true. Ouch. Block Here is the i_impossible_t(on(X,X), _, true).
i_impossible_t(on(X,Y), Goals, true) :-
( memberd_t(clear(Y), Goals, true)
; memberd_t(on(X,Y1), Goals,true), dif(Y1, Y)
; memberd_t(on(X1,Y), Goals,true), dif(X1, X)
).
i_impossible_t(clear(X), Goals, true) :-
memberd_t(on(_,X), Goals, true).
i_impossible_t(_, _, false).
impossible_t(Goal, Goals, T) :-
length(Goals, _),
i_impossible_t(Goal, Goals, T).
any_impossible_t([], _, false).
any_impossible_t([Goal|Goals0], Goals, T0) :-
if_(impossible_t(Goal, Goals),
T0=true,
(
T0=T,
any_impossible_t(Goals0, Goals, T)
)
). |
Beta Was this translation helpful? Give feedback.
-
Note: I wrote this before seeing @jjtolton was having issues with A change to i_impossible_t(on(X,Y), Goals, T) :-
if_(X = Y, T = true, tmember_t(conflicts_on_t(X, Y), Goals, T)). % could be further esoteric-icized as ';'(X = Y, tmember_t(conflicts_on_t(X, Y), Goals), T). lol
i_impossible_t(clear(Y), Goals, T) :- tmember_t(conflicts_clear_t(Y), Goals, T).
conflicts_on_t(X, Y, Goal, T) :- conflicts_on_t_(Goal, X, Y, T).
conflicts_on_t_(on(X1, Y1), X, Y, T) :-
','(X1 = X, Y1 = 1, T0),
if_(T0 = true, T = false, T = true). % not entirely sure on this logic tbh
conflicts_on_t_(clear(Y1), _, Y, T) :- =(Y1, Y, T).
conflicts_clear_t(Y, Goal, T) :- conflicts_clear_t_(Goal, Y, T).
conflicts_clear_t_(on(_, Y1), Y, T) :- =(Y1, Y, T).
conflicts_clear_t_(clear(_), _, false). |
Beta Was this translation helpful? Give feedback.
-
A second attempt at rewriting %% true iff any of NewGoals are impossible in the context of Goals
any_impossible_t(NewGoals, Goals, T) :- tmember_t(impossible_t(Goals), NewGoals, T).
%% true iff NewGoal is impossible in the context of Goals (either it is impossible intrinsically, or contextually)
impossible_t(Goals, NewGoal, T) :- ';'(impossible_t(NewGoal), tmember_t(conflicts_t(NewGoal), Goals), T).
%% true iff the goal is impossible intrinsically
impossible_t(on(X, Y), T) :- =(X, Y, T).
impossible_t(clear(_), false).
%% true iff the two goals are contradictory
conflicts_t(on(X, Y), Goal, T) :- conflicts_on_t(Goal, X, Y, T).
conflicts_t(clear(Y), Goal, T) :- conflicts_clear_t(Goal, Y, T).
%% true iff Goal conflicts on(X, Y)
conflicts_on_t(on(X1, Y1), X, Y, T) :- ';'((X1 = X, dif(Y1, Y)), (dif(X1, X), Y1 = Y), T).
conflicts_on_t(clear(Y1), _, Y, T) :- =(Y1, Y, T).
%% true iff Goal conflicts clear(Y)
conflicts_clear_t(on(_, Y1), Y, T) :- =(Y1, Y, T).
conflicts_clear_t(clear(_), _, false). |
Beta Was this translation helpful? Give feedback.
-
Ok, regression working: moves(state(WorldState,Goals)) -->
{
% format("WorldState/Goals: ~t~w~t~w~t~250|~n", [WorldState, Goals]),
xs_ys_setdiff(WorldState, Goals, [])
}.
moves(state(WorldState, Goals0)) --> [Action],
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Goal regression version
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
{
xs_ys_setdiff(WorldState, Goals0, Result), % sanity check to make sure you aren't solving a solved problem
=(Result, [], false),
can(Action, Conditions), % take the preconditions of the action.
adds(Action, Effects), % the effects of the action,
deletes(Action, Undoes), % the "undoes" of the action,
member(Effect, Goals0), % if there is an effect
member(Effect, Effects), % that is also a goal
member(Undo, Undoes), % but no undo
memberd_t(Undo, Goals0, false), % in goals
xs_ys_setdiff(Goals0, Effects, Goals1), % as long as for the combined effects+goals
any_impossible_t(Conditions, Goals1, false), % there is no invalid state
xs_ys_setunion(Goals1, Conditions, Goals) % then union the conditions as if the event happened
% start(Start),
% xs_ys_setdiff(Start, Goals, Left)
% format("New Goal/Goals: ~t~w~t~20| ~t~w~t~80|-~t~w~t~80| + ~t~w~t~80|= ~w~80|(~w)~30|~n", [Action, Goals0, Effects, Conditions, Goals, Left])
},
moves(state(WorldState, Goals)).
It's not pretty: ?- solve(State, [on(a,b)], Moves).
%@ State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,a),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,2)] There seem to be 4 redundant choice points. Not great, but the IDFS version works better anyway, so I'm ok with that. However, it is REALLY cool to see that you can DCGs to generate a list of actions via a DFS traversal and backwards via goal regression! For the sake of completeness: :- use_module(library(dif)).
:- use_module(library(reif)).
:- use_module(library(lists)).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
xs_ys_setdiff(Xs, Ys, Diff): Diff is the set difference of Xs and Ys.
https://github.com/mthom/scryer-prolog/discussions/2507#discussioncomment-10471435
Courtesy of Ulrich: https://github.com/UWN
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
xs_ys_setdiff([], _, []).
xs_ys_setdiff([X|Xs], Ys, Diff0) :-
if_(memberd_t(X, Ys),
Diff0=Diff,
Diff0=[X|Diff]
),
xs_ys_setdiff(Xs, Ys, Diff).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
xs_ys_setunion(Xs, Ys, Union): Union is the set union of Xs and Ys.
Courtesy of Rayh ttps://github.com/librarianmage
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
xs_ys_setunion(Xs, Ys, Union) :-
xs_ys_setdiff(Xs, Ys, UniqXs),
append(UniqXs, Ys, Union).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
object(X): X is a place or a block.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
object(X) :-
( place(X)
; block(X)
).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
State Change relations.
clear(Block): denotes a block that hgas nothing above it
on(Block,Object): denotes a block being on either another block or place
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
%% can(Action, PreConditions): Preconditions are required to be in present for this move
%% to be admissible.
can(move(Block, From, To), [clear(Block), clear(To), on(Block,From)]) :-
block(Block),
object(To),
dif(To, Block),
object(From),
dif(Block, From).
%% adds(Action, Effects): These are the effects added to the State when this move happens
adds(move(X, From, To), [on(X,To), clear(From)]).
%% deletes(Action, Undoes): The Undoes are removed from the world state when this move happens
deletes(move(X,From,To), [on(X,From), clear(To)]).
%% supported(WorldState, Conditions): the world state contains the supporting conditions
supported(WorldState, Conditions) :-
xs_ys_setdiff(Conditions, WorldState, []).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
results_in(IntitialWorldState, Undoes, Effects, WorldState):
The new world state after removing the undoes and apply the effects
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
results_in(WorldState0, Undoes, Effects, WorldState) :-
xs_ys_setdiff(WorldState0, Undoes, WorldState1),
xs_ys_setunion(WorldState1, Effects, WorldState).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
goal(WorldState, Goals): The goal is achieved if all Goals are
present in the WorldState
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
goal(WorldState, Goals) :-
xs_ys_setdiff(Goals, WorldState, []).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
goal_t(WorldState,Goals,Truth): Truth=true if goal(WorldState, Goals)
else Truth=false.
It turns out I didn't need this, but I'm so excited to discover
I can make a reif predicate using (=)/3 that I'm leaving it in
so I remember.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
goal_t(WorldState,Goals,T) :-
xs_ys_setdiff(Goals, WorldState, Result),
=(Result, [], T).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
https://github.com/mthom/scryer-prolog/discussions/2513#discussioncomment-10493094
courtesy of Ray https://github.com/librarianmage
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
%% true iff any of NewGoals are impossible in the context of Goals
any_impossible_t(NewGoals, Goals, T) :- tmember_t(impossible_t(Goals), NewGoals, T).
%% true iff NewGoal is impossible in the context of Goals (either it is impossible intrinsically, or contextually)
impossible_t(Goals, NewGoal, T) :- ';'(impossible_t(NewGoal), tmember_t(conflicts_t(NewGoal), Goals), T).
%% true iff the goal is impossible intrinsically
impossible_t(on(X, Y), T) :- =(X, Y, T).
impossible_t(clear(_), false).
%% true iff the two goals are contradictory
conflicts_t(on(X, Y), Goal, T) :- conflicts_on_t(Goal, X, Y, T).
conflicts_t(clear(Y), Goal, T) :- conflicts_clear_t(Goal, Y, T).
%% true iff Goal conflicts on(X, Y)
conflicts_on_t(on(X1, Y1), X, Y, T) :- ';'((X1 = X, dif(Y1, Y)), (dif(X1, X), Y1 = Y), T).
conflicts_on_t(clear(Y1), _, Y, T) :- =(Y1, Y, T).
%% true iff Goal conflicts clear(Y)
conflicts_clear_t(on(_, Y1), Y, T) :- =(Y1, Y, T).
conflicts_clear_t(clear(_), _, false).
?- any_impossible_t([on(b,4)], [clear(1),on(b,a),clear(b),clear(c),on(b,1),on(b,c)], true).
?- any_impossible_t([on(b,4)], [clear(1),on(b,a),clear(b),clear(c),on(b,1),on(b,c)], false).
moves(state(WorldState,Goals)) -->
{
% format("WorldState/Goals: ~t~w~t~w~t~250|~n", [WorldState, Goals]),
xs_ys_setdiff(WorldState, Goals, [])
}.
moves(state(WorldState, Goals0)) --> [Action],
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Goal regression version
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
{
xs_ys_setdiff(WorldState, Goals0, Result), % sanity check to make sure you aren't solving a solved problem
=(Result, [], false),
can(Action, Conditions), % take the preconditions of the action.
adds(Action, Effects), % the effects of the action,
deletes(Action, Undoes), % the "undoes" of the action,
member(Effect, Goals0), % if there is an effect
member(Effect, Effects), % that is also a goal
member(Undo, Undoes), % but no undo
memberd_t(Undo, Goals0, false), % in goals
xs_ys_setdiff(Goals0, Effects, Goals1), % as long as for the combined effects+goals
any_impossible_t(Conditions, Goals1, false), % there is no invalid state
xs_ys_setunion(Goals1, Conditions, Goals) % then union the conditions as if the event happened
% start(Start),
% xs_ys_setdiff(Start, Goals, Left)
% format("New Goal/Goals: ~t~w~t~20| ~t~w~t~80|-~t~w~t~80| + ~t~w~t~80|= ~w~80|(~w)~30|~n", [Action, Goals0, Effects, Conditions, Goals, Left])
},
moves(state(WorldState, Goals)).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Starting World State.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
block(a).
block(b).
block(c).
place(1).
place(2).
place(3).
place(4).
% A state in the blocks world
%
% c
% a b
% = = = =
% place 1 2 3 4
%start([clear(2),clear(4),clear(b),clear(c),on(a,1),on(b,3),on(c,a)]).
start([on(a,1),on(b,3)]).
solve(Start, Goal, Moves) :-
start(Start),
length(Moves, _),
phrase(moves(state(Start, Goal)), Moves).
?- solve(State, [on(a,b)], Moves).
%@ State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,a),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,3)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,3)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,2),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,4),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,a),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(b,a,c),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,3)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,3)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(c,a,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,3)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,3)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,3)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,3)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,4)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,a)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,3,b),move(a,1,3),move(b,3,c)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(a,1,1),move(a,1,3),move(b,3,2)]
%@ ; State = [on(a,1),on(b,3)], Moves = [move(a,1,b),move(a,1,1),move(a,1,3),move(b,3,2)]
%@ ; ... .
%@ error('$interrupt_thrown',repl/0). |
Beta Was this translation helpful? Give feedback.
-
Ok let's begin the roast proper! I'll be focusing primarily on the IDFS DCG version because it works the best and it's the easiest to understand, though I would love to know why goal regression version has so many redundant choice points. The IDFS DCG version is pretty good, but it's a lot slower than I would like it to be. I believe the way I wrote it (trying to stay close to the book logic) is generating too many alternatives at each step. The bulk of the work is done in moves(state(WorldState0, Goals)) --> [Move],
{ can(Move, Conditions), % action precondtions
supported(WorldState0, Conditions), % determine if action can happen
adds(Move, Effects), % determine effects
deletes(Move, Undoes), % determine undoes
results_in(WorldState0, Undoes, Effects, WorldState) % determine WorldState'
},
moves(state(WorldState, Goals)). and using %% all_actions(Action): like `can/2` without filtering out any to actions so as to count all computations)
all_actions(action(Block, From, To)) :-
block(Block),
object(To),
object(From).
action_count(Count) :-
findall(Action, all_actions(Action), Actions),
length(Actions, Count).
?- action_count(Count).
%@ Count = 147. |
Beta Was this translation helpful? Give feedback.
-
You should probably learn about failure slices and declarative debugging. It isn't applicable for every property (it doesn't help with determinism for example) but it does help with non-termination and making sure your predicate is actually the relation you meant. Those are techniques that are only really applicable for monotonic Prolog, and they are really powerful. |
Beta Was this translation helpful? Give feedback.
-
Thanks everyone for your wonderful suggestions! Next up: partial order planning! |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
As referenced in a previous discussion: I rewrote the algorithm from Prolog Programming for Artificial Intelligence (source at bottom).
The results seem to be valid, but there are a LOT of inefficiences and redundant choice points:Edit:
fixed a few things, updated output below. There are still duplicates and it's taking a surprising amount of time to find|Plan|>2
!Edit2:
now the results are just incorrect,[move(c,a,b),move(b,3,a), ...]
is not a valid move sequence, as you cannot move block C onto block B and then move block B! >.<Edit3:
it wascorrectrunning originally, it's not nonterminating, it's just slow and inefficient!Edit4: Wrote two DCG versions (forwards and backwards). There is plenty to be discussed about the various approaches below.
Output of monotic rewrite of the book version of the code:
Output of iterative DFS DCG version of the algo:
Output of the goal regression DCG version of the algo:
I'd love it if anyone would be willing to brutally critique this code so that we can make it into a shining example of what good Scryer code should look like!
Goals:
Original discussion of research direction
On the last point, comparing to @triska's [Zurg](https://www.metalevel.at/zurg/) and [Jug Pouring](https://www.metalevel.at/tist/jugs.pl) problem, which seem to work from beginning to end (which is how I'm used to approaching search problems), _this_ approach (means-end) starts from the end and works to be beginning, working backwards through non-conflicting goals. Part of this research is to determine if it's possible to write goal regression via DCG notation -- my guess is that it is, and it would be much cleaner to do it that way.But I wanted to get a decent reference implementation setup first!
I put the code in a gist so we can track changes and refer to it more easily:Edit: moved to summary abovehttps://gist.github.com/jjtolton/f6416b8ed2942437d8894fa3fdfe99c3
Beta Was this translation helpful? Give feedback.
All reactions