Replies: 13 comments 36 replies
-
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.
-
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