How to list all the trips discarding one airline - prolog

This is my flights data
#flight(city1, city2,airline,distance,time,price).
flight(dublin, london,ab,8000,4,1000).
flight(moscow,london,ab,9000,5,2000).
flight(dublin,moscow,bc,1000,6,3000).
flight(cork, moscow,ca,2000,7,4000).
flight(chicago, dublin,ab,6000,8,4500).
flight(berlin, moscow,bc,3000,9,4600).
flight(cork, newyork,ca,4000,10,4700).
flight(paris, hongkong,bc,11000,11,4890).
connected(X,Y,_,_,_,_) :- flight(X,Y,_,_,_,_) ; flight(Y,X,_,_,_,_).
I have to get all the trips between city1 and city2 discarding one airline
I have calculated the trip something like
trips(A,B,Path) :-
traverse(A,B,[A],Q),
reverse(Q,Path).
traverse(A,B,P,[B|P]) :-
connected(A,B,_,_,_,_).
traverse(A,B,Visited,Path) :-
connected(A,C,_,_,_,_),
C \== B,
\+member(C,Visited),
traverse(C,B,[C|Visited],Path).
This is the all_trip
Alltrip(C,L,T):-
findall(Ci, trips(C,L,Ci), T).
I have to compute this
Alltrip_noairline(X,Y,T,A):- where X and Y are city ,T is the list of all trips and DISCARD all the trip containing a flight with airline A
I am stuck here ,don't know how to start ,any help would be appreciated .Thanks

I believe connected/6 is in fact flight/6
You may add a Filter goal to your procedures:
trips(A,B,Filter, Path) :-
traverse(A,B,Filter, [A],Q),
reverse(Q,Path).
traverse(A,B, Filter, P,[B|P]) :-
flight(A,B,Airline,_,_,_),
call(Filter, Airline).
traverse(A,B, Filter, Visited,Path) :-
flight(A,C,Airline,_,_,_),
C \== B,
call(Filter, Airline),
\+ member(C,Visited),
traverse(C,B, Filter, [C|Visited],Path).
Then in alltrip/3 use a filter that accepts any airline:
alltrip(C,L,T):-
findall(Ci, trips(C,L, any, Ci), T).
any(_).
and in alltrip_noairline/4 one that forbids certain airline:
alltrip_noairline(C,L,T, A):-
findall(Ci, trips(C,L, except(A), Ci), T).
except(X, Y):- X\=Y.

Related

Apply a predicate to a list of inputs Prolog

What i'm trying to do is:
Given a list of characters to know which list best contradicts it, so image I put on the list [kraken,scorpia,zulrah] so it will check the type of attack of each and would see would be the most effective type of attack for each and with that, I would receive a list of 3 bosses.
% boss(Name, Type) Name = Name of boss, Type = Attack type
boss(kraken,magic).
boss(scorpia,melee).
boss(zulrah,ranged).
boss(cerberus,melee).
boss(abyssal_sire,melee).
boss(obor,melee).
boss(sarachnis,ranged).
boss(skotizo,melee).
boss(venenatis,magic).
superEffective(magic,melee). %magic is more effective against melee
superEffective(ranged,magic). %ranged is more effective against magic
superEffective(melee,ranged). %melee is more effective against ranged
First, create a predicate to verify the counter of a single Boss:
verify_con(Boss, ConBoss) :- boss(Boss,MainSkill),
superEffective(MainSkill,ConSkill),
boss(ConBoss,ConSkill), !.
Notice that this predicate will get always the first best counter to the input boss. If you want all the possible combinations, just delete the , ! at the end.
Second, use recursion to iterate over the input list and build the output list. You can use append/3 to append the output array.
verify_con_list([],[]).
verify_con_list([H|T], LIST) :- verify_con(H, ConBoss),
verify_con_list(T, L1),
append([ConBoss],L1, LIST).
If necessary, you can define the append/3 function at the top of your code like this:
append([], X, X).
append([H|T], X, [H|S]) :- append(T, X, S).
Examples
Single output:
?- verify_con(kraken, A).
A = scorpia
List input:
?- verify_con_list([kraken, scorpia, zulrah], Con).
Con = [scorpia, zulrah, kraken]
Full code:
append( [], X, X).
append( [X | Y], Z, [X | W]) :- append( Y, Z, W).
% boss(Name, Type) Name = Name of boss, Type = Attack type
boss(kraken,magic).
boss(scorpia,melee).
boss(zulrah,ranged).
boss(cerberus,melee).
boss(abyssal_sire,melee).
boss(obor,melee).
boss(sarachnis,ranged).
boss(skotizo,melee).
boss(venenatis,magic).
superEffective(magic,melee). %magic is more effective against melee
superEffective(ranged,magic). %ranged is more effective against magic
superEffective(melee,ranged). %melee is more effective against ranged
verify_con(Boss, ConBoss) :- boss(Boss,MainSkill),
superEffective(MainSkill,ConSkill),
boss(ConBoss,ConSkill), !.
verify_con_list([],[]).
verify_con_list([H|T], LIST) :- verify_con(H, ConBoss),
verify_con_list(T, L1),
append([ConBoss],L1, LIST).
%verify_con(kraken, A).
%verify_con_list([kraken, scorpia, zulrah], Con).

Prolog - Path finding and length given Relation

I just began learning Prolog and I wanted to understand Pathfinding better. I have a few examples of relationships, however, I don't know how to find the path and length of a relationships when the relationships are cyclical. I've been trying to create a list that documents visited nodes, but I keep receiving errors.
Below are a few examples as well as my attempt to find path given the relationship, source, target, pathlist, and length):
is_a(parallelogram, quadrilateral).
is_a(trapezoid, quadrilateral).
is_a(rhombus, parallelogram).
is_a(rectangle, parallelogram).
is_a(square, rhombus).
is_a(square, rectangle).
edge(a, b).
edge(b, c).
edge(c, d).
edge(d, a).
friend(alice, bob).
friend(bob, carol).
friend(carol, daniel).
friend(carol, eve).
friends(A,B) :-
friend(A,B);
friend(B,A).
transit(Rel, S, T) :-
call(Rel, S, X),
(X = T; transit(Rel, X, T)).
path_(Rel,Source,Target,Path,Len) :-
path_(Rel,Source,Target,Path,Len,[]).
path_(Rel,Source,Target,Path,Len,Visited) :-
transit(Rel,Source,Target),
transit(Rel,Source,Mid),
Mid == Target, !,
append(Visited,[Source,Target],Path),
length(Path,L),
Len is L+1.
path_(Rel,Source,Target,Path,Len,Visited) :-
transit(Rel,Source,Target),
transit(Rel,Source,Mid),
not(member(Mid,Visited)),
path_(Rel,Mid,Target,Path,Len,[Source|Visited]).
The above is my second attempt, but I receive errors on everything. My first attempt only worked with non-cyclical paths, such as for the is_a relationships, which is noted below:
path0(Rel,From,To,[From,To],2) :-
transit(Rel,From,To),
call(Rel, From, To).
path0(Rel,From,To,[From|Xs],Len) :-
transit(Rel,From,X),
call(Rel,From,X),
path0(Rel,X,To,Xs,_),
length(Xs, L),
Len is L+1.

Making a STRIPS Planner using BFS in Prolog

I have the following generic Breadth-first search code for Prolog and I would like to take the simple node representation s(a,b,go(a,b)) and change it to a predicate so that go(a,b) will represent a STRIPS operator say stack(A,B) so that I might have two predicates: s(S0,S,stack(A,B)) and s(S0,S,unstack(B,A)) (classic blocks world problem) which can be used by the breadth-first search below. I'm not sure if this is possible or how I would go about doing it. My first idea was to have a predicate as follows:
% S0 is the given state and S is the successor for the 'stack(A,B)' predicate if S0
% A and B are not the same blocks, and we want the next state S to contain the
% same state/preconditions information except we want to add 'on(A,B)'
% to S and we want to remove 'clear(B)' from S0
s(S0,S,stack(A,B)) :-
A \== B,
% other predicates etc
The breadth-first search is given below.
:- use_module(library(lists)).
% bfs(?initial_state, ?goal_state, ?solution)
% does not include cycle detection
bfs(X,Y,P) :-
bfs_a(Y,[n(X,[])],R),
reverse(R,P).
bfs_a(Y,[n(Y,P)|_],P).
bfs_a(Y,[n(S,P1)|Ns],P) :-
findall(n(S1,[A|P1]),s(S,S1,A),Es),
append(Ns,Es,O),
bfs_a(Y,O,P).
% s(?state, ?next_state, ?operator).
s(a,b,go(a,b)).
s(a,c,go(a,c)).
bfs(S0,Goal,Plan) :-
bfs_a(Goal1,[n(S0,[])],R),
subset(Goal,Goal1),
reverse(R,Plan).
bfs_a(Y,[n(Y,P)|_],P).
bfs_a(Y,[n(S,P1)|Ns],P) :-
findall(n(S1,[A|P1]), s(S,S1,A), Es),
append(Ns,Es,O),
bfs_a(Y,O,P).
s(State,NextState,Operation) :-
opn(Operation, PreList), subset(PreList, State),
deletes(Operation, DelList), subtract(State, DelList, TmpState),
adds(Operation, AddList), union(AddList, TmpState, NextState).
subset([ ],_).
subset([H|T],List) :-
member(H,List),
subset(T,List).
opn(move(Block,X1,X2),[clear(Block),clear(X2),on(Block,X1)]) :-
block(Block),object(X1),object(X2),
Block \== X1, X2 \== Block, X1 \== X2.
adds(move(Block,X1,X2),[on(Block,X2),clear(X1)]).
deletes(move(Block,X1,X2),[on(Block,X1),clear(X2)]).
object(X) :- place(X) ; block(X).
block(a).
block(b).
block(c).
block(d).
place(x1).
place(x2).
place(x3).

Match database items exactly once in Prolog?

Let's say there is a simple database of people in Prolog
person(john).
person(mary).
person(john).
person(susan).
I need to match the entires exactly once:
john-mary, john-john, john-susan, mary-john, mary-susan, john-susan
I tried coming up with something like this:
match:- person(X),!,person(Y), write(X),write(-), write(Y),nl.
run:- person(X), match(X), fail.
But it's matching many times, and matches a person to him/herself, which shouldn't be.
Basically, what I need is to iterate over all Xs and make Prolog to look strictly "below" for Ys.
A quick solution would be to number your people:
person(1, john).
person(2, mary).
person(3, john).
person(4, susan).
Then you could match people like this:
match(X-Y) :-
person(I, X), person(J, Y), I < J.
Since you have two john entries, I'm not sure any other solution is going to work. Normally you could fake an ordering using #>/2 but that would require your atoms to be unique, and since they aren't, it would prevent the john-john solution.
Edit: Since we're willing to use findall/3 to materialize the database of people, we can treat this as a list problem and find a functional solution. Let's get all the combinations in a list:
combinations([X|Rest], X, Y) :- member(Y, Rest).
combinations([_|Rest], X, Y) :- combinations(Rest, X, Y).
With this predicate in hand, we can find the solution:
combined_folks(People) :-
findall(P, person(P), Persons),
findall(X-Y, combinations(Persons, X, Y), People).
?- combined_folks(X).
X = [john-mary, john-john, john-susan, mary-john, mary-susan, john-susan].
That actually turned out to be pretty clean!
person(john).
person(mary).
person(john).
person(susan).
match :- findall(P,person(P),People), match_all(People).
match_all([_]) :- !.
match_all([P|People]) :- match_2(P,People), match_all(People).
match_2(_,[]) :- !.
match_2(P1,[P2|People]) :- format('~a-~a~n',[P1,P2]), match_2(P1,People).
?- match.

How can you add elements to a list or compute them from the knowledge base without using findall or assert/retract in Prolog?

I have a knowledge base that consists of students database in a file 'students.pl' like this:
% student(Name,Percent,List_of_Marks_in_3_subjects).
student('abc',83,[80,80,90]).
student('pqr',70,[70,60,80]).
student('xyz',76,[80,70,80]).
I want to access each student predicate from the knowledge base and calculate the average marks in each subject or average percentage, without using 'findall' or assert/retract.
I may want to use backtracking like this:
find_score_all(X) :- student(Name,Percent,L),
write(Percent),nl,
fail.
find_score_all(_).
With this approach I can access each element and write it, but if I want to add each 'Percent' value as an element to a list or just use a predicate like 'Percent1 is Total + Percent' to total the percent values and then find its average, how can I do so?
Note that I dont want to use findall or retract/assert and preferably find the average in one pass through the knowledge base since the knowledge base is very large.
Any help is appreciated.
%solution for sum of percents, you can replace with any other calculation sum_percent predicate.
listing(student/3, convert_to_list/2, sum_percent, sum_percent/2).
% student(Name,Percent,List_of_Marks_in_3_subjects).
student('abc',83,[80,80,90]).
student('pqr',70,[70,60,80]).
student('xyz',76,[80,70,80]).
convert_to_list(X, R):-
student(N, P, LM),
not(member(st(N, P, LM), X)),
convert_to_list([st(N, P, LM)|X], R).
convert_to_list(X, X).
sum_percent:-
convert_to_list([], X),
sum_percent(X, S),
write(S).
sum_percent([], 0).
sum_percent([st(_,E,_)|T], S):-
sum_percent(T, S2),
S is E+S2.
if you want to add to a list then you should use findall, or better, library(aggregate). But if you fear about efficiency, you could use something like this
integrate(ave, Goal, Ave) :-
State = state(0, 0, _),
repeat,
( call(Goal, V),
arg(1, State, C), U is C+1, nb_setarg(1, State, U),
arg(2, State, S), T is S+V, nb_setarg(2, State, T),
fail
; arg(1, State, C), arg(2, State, S), Ave is S/C
).
:- meta_predicate integrate(+, :, ?).
test:
members(X) :- member(X, [1,2,3,4]).
?- integrate(ave, members, R).
R = 2.5 .
Of course, you'll need to add error handling (at least, when counter C == 0).

Resources