% Figure 12.3 A best-first search program.
:- op( 900, fy, not).
% not Goal): negation as failure;
% Note: This is often available as a built-in predicate,
% often written as prefix operator "\+", e.g. \+ likes(mary,snakes)
not Goal :-
Goal, !, fail
;
true.
% bestfirst( Start, Solution): Solution is a path from Start to a goal
bestfirst( Start, Solution) :-
expand( [], l( Start, 0/0), 9999, _, yes, Solution).
% Assume 9999 is greater than any f-value
% expand( Path, Tree, Bound, Tree1, Solved, Solution):
% Path is path between start node of search and subtree Tree,
% Tree1 is Tree expanded within Bound,
% if goal found then Solution is solution path and Solved = yes
% Case 1: goal leaf-node, construct a solution path
expand( P, l( N, _), _, _, yes, [N|P]) :-
goal(N).
% Case 2: leaf-node, f-value less than Bound
% Generate successors and expand them within Bound.
expand( P, l(N,F/G), Bound, Tree1, Solved, Sol) :-
F =< Bound,
( bagof( M/C, ( s(N,M,C), not member(M,P) ), Succ),
!, % Node N has successors
succlist( G, Succ, Ts), % Make subtrees Ts
bestf( Ts, F1), % f-value of best successor
expand( P, t(N,F1/G,Ts), Bound, Tree1, Solved, Sol)
;
Solved = never % N has no successors - dead end
) .
% Case 3: non-leaf, f-value less than Bound
% Expand the most promising subtree; depending on
% results, procedure continue will decide how to proceed
expand( P, t(N,F/G,[T|Ts]), Bound, Tree1, Solved, Sol) :-
F =< Bound,
bestf( Ts, BF), min( Bound, BF, Bound1), % Bound1 = min(Bound,BF)
expand( [N|P], T, Bound1, T1, Solved1, Sol),
continue( P, t(N,F/G,[T1|Ts]), Bound, Tree1, Solved1, Solved, Sol).
% Case 4: non-leaf with empty subtrees
% This is a dead end which will never be solved
expand( _, t(_,_,[]), _, _, never, _) :- !.
% Case 5: f-value greater than Bound
% Tree may not grow.
expand( _, Tree, Bound, Tree, no, _) :-
f( Tree, F), F > Bound.
% continue( Path, Tree, Bound, NewTree, SubtreeSolved, TreeSolved, Solution)
continue( _, _, _, _, yes, yes, Sol).
continue( P, t(N,F/G,[T1|Ts]), Bound, Tree1, no, Solved, Sol) :-
insert( T1, Ts, NTs),
bestf( NTs, F1),
expand( P, t(N,F1/G,NTs), Bound, Tree1, Solved, Sol).
continue( P, t(N,F/G,[_|Ts]), Bound, Tree1, never, Solved, Sol) :-
bestf( Ts, F1),
expand( P, t(N,F1/G,Ts), Bound, Tree1, Solved, Sol).
% succlist( G0, [ Node1/Cost1, ...], [ l(BestNode,BestF/G), ...]):
% make list of search leaves ordered by their F-values
succlist( _, [], []).
succlist( G0, [N/C | NCs], Ts) :-
G is G0 + C,
h( N, H), % Heuristic term h(N)
F is G + H,
succlist( G0, NCs, Ts1),
insert( l(N,F/G), Ts1, Ts).
% Insert T into list of trees Ts preserving order w.r.t. f-values
insert( T, Ts, [T | Ts]) :-
f( T, F), bestf( Ts, F1),
F =< F1, !.
insert( T, [T1 | Ts], [T1 | Ts1]) :-
insert( T, Ts, Ts1).
% Extract f-value
f( l(_,F/_), F). % f-value of a leaf
f( t(_,F/_,_), F). % f-value of a tree
bestf( [T|_], F) :- % Best f-value of a list of trees
f( T, F).
bestf( [], 9999). % No trees: bad f-value
min( X, Y, X) :-
X =< Y, !.
min( X, Y, Y).