Solving a puzzle in Prolog - prolog

I am new to prolog and I am trying to solve this puzzle problem. I did a couple tutorials on youtube on the basics of prolog, but I need some help solving the puzzle below.
Two weeks ago, four enthusiasts made sightings of objects in the sky in their neighborhood. Each of the four reported his or her sightings on a different day. The FBI came and was able to give each person a different explanation of what he or she had "really" seen. Can you determine the day ( Tuesday through Friday ) each person sighted the object, as well as the object that it turned out to be?
Mr. K made his sighting at some point earlier in the week than the one who saw the balloon, but at some point later in the week, than the one who spotted the Kite ( who isn't Ms. G ).
Friday's sighting was made by either Ms. Barn or the one who saw a plane ( or both ).
Mr. Nik did not make his sighting on Tuesday.
Mr. K isn't the one whose object turned out to be a telephone pole.
I have my set my rules up correctly, but I can't seem to get the logic down pack. I am looking for guidance not direct answers. On the far right, I have listed the number to each question i am attempting to answer
enthu(mr_k).
enthu(ms_barn).
enthu(ms_g).
enthu(mr_nik).
object(ballon).
object(kite).
object(plane).
object(tele_pole).
day(tuesday).
day(wednesday).
day(thursday).
day(friday).
sight(X,ballon).
sighting(mr_k):- 1
day(X),
sight(X,Y),
didntc_kite(ms_g).
friday_sight:- enthu(ms_barn); 2
saw(X,plane);
both(ms_barn,X).
nosight_tuesday(mr_nik,X). 3
no_telepole(mr_k,Y). 4

I know you didn't ask for a solution, but I find it hard to describe what to do without a working solution. I apologise for that.
Here's what I would do:
/*
1. Mr. K made his sighting at some point earlier in the week than the one who saw the balloon, but at some point later in the week, than the one who spotted the Kite ( who isn't Ms. G ).
2. Friday's sighting was made by either Ms. Barn or the one who saw a plane ( or both ).
3. Mr. Nik did not make his sighting on Tuesday.
4. Mr. K isn't the one whose object turned out to be a telephone pole.
*/
?-
% Set up a list of lists to be the final solution
Days = [[tuesday,_,_],[wednesday,_,_],[thursday,_,_],[friday,_,_]],
/* 1 */ before([_,mr_k,_],[_,_,balloon],Days),
/* 1 */ before([_,_,kite],[_,mr_k,_],Days),
/* 2 */ (member([friday,ms_barn,_],Days);
member([friday,_,plane],Days);
member([friday,ms_barn,plane],Days)),
% Fill in the rest of the people
members([[_,mr_k,_],[_,ms_barn,_],[_,ms_g,_],[_,mr_nik,_]],Days),
% Fill in the rest of the objects
members([[_,_,balloon],[_,_,kite],[_,_,plane],[_,_,tele_pole]],Days),
% Negations should be done after the solution is populated
/* 1 */ member([_,NOT_ms_g,kite],Days), NOT_ms_g \= ms_g,
/* 3 */ member([tuesday,NOT_mr_nik,_],Days), NOT_mr_nik \= mr_nik,
/* 4 */ member([_,NOT_mr_k,tele_pole],Days), NOT_mr_k \= mr_k,
write(Days),
nl,
fail.
% Checks that `X` comes before `Y`
% in the list `Ds`
before(X,Y,Ds) :-
remainder(X,Ds,Rs),
member(Y,Rs).
% Finds a member of a list and
% unifies the third parameter such
% that it is the remaining elements in
% the list after the found member
remainder(X,[X|Ds],Ds).
remainder(X,[_|Ds],Rs) :- remainder(X,Ds,Rs).
% An extended version of `member` that
% checks if the members of the first list
% are all members of the second
members([],_).
members([X|Xs],Ds) :-
member(X,Ds),
members(Xs,Ds).
That gives me:
[[tuesday, ms_g, tele_pole],
[wednesday, mr_nik, kite],
[thursday, mr_k, plane],
[friday, ms_barn, balloon]]

Related

Prolog endless loop with Intersect predicate.(Scheduling)

I have a base file which i include which contains many attends/2 predicates like the following.
attends(student_ID,course_iD). (Example: attends(2222,CSD101).)
The predicate schedule_e(W,IN,E) :
-W is a List that contains 3 courses and represents a week of examination. For instance W = [CSD101,CSD102,CSD103].
What it basically does is that for a student takes all the courses that he has and intersects them with the W list and then Returns this List IN of the intersected along with the number of courses this IN list contains. IN list basically contains all the courses that the student will be examined on and E is the number of the courses this student has on the exams.
The predicate count_num just returns how many numbers in a list are greater than 2.
The last predicate:
W1,W2,W3 are week1 week2 week3 exactly like the above one.
Each findall finds all the courses for each student and for each week and then intersects them with the week itself so we can know for each student how many courses he has on the examination for each week. Then count_num counts how many students have more than 2 courses for per week and returns it.
If i run ?- schedule_errors([CSD101,CSD102,CSD103],[CSD104,CSD105,CSD106],[CSD107,CSD108],E). everything's great.
But if i run schedule_errors(W1,W2,W3,0) (so that it will give the schedule or else the 3 weeks for which the number of students that have more that 2 courses to examined on is 0), in this case it says out of global stack for which i raised the global stack and it seems that it enters in an endless recursion. It also notes that this happens in Intersect. How else can i implement this? What exactly is the problem.
(The first 2 weeks have always 3 courses each and the 3rd has 2 courses.)
intersect([],_,[]).
intersect([H1|T1],L2,[H1|Res]):-
member(H1,L2),
intersect(T1,L2,Res).
intersect([_|T1],L2,Res):-
intersect(T1,L2,Res).
%1
schedule_e(W1,IN,E):-
setof(C,attends(Y,C), L),intersect(L,W1,IN),length(IN,E).
%2
count_num(_,[],0).
count_num(A,[H|L],N):-A<H,count_num(A,L,N1),N is N1+1.
count_num(A,[H|L],N):-A>=H,count_num(A,L,N).
%3
schedule_errors(W1,W2,W3,E):-
findall(E1, schedule_e(W1, _, E1), Esa),
findall(E2, schedule_e(W2, _, E2), Esb),
findall(E3, schedule_e(W3, _, E3), Esc),
append(Esa,Esb,L),append(L,Esc,L1),count_num(2,L1,E).

Prolog: Difference relation between two objects

I will be honest and preface this by saying it is homework, but I desperately need help.
I am new to Prolog, coming from C++ and Javascript and no matter how hard I try, I'm just not getting it.
What I am supposed to do is to be able to find the difference between two dates (assuming non leap year).
Basically I have a knowledge base with information laid out like:
object(A, B).
object(A, B).
...
where A represents month and B represents total days of the month.
(eg. object(1, 31).)
The expression I am supposed to use for input is this:
Difference(Object(A,B), object(A,B), N).
(N = difference between the objects).
I have tried a few different things (and I know this isn't on the right track) and haven't been able to get further then:
difference(object(A,B), object(A,B),N):-
days(M,D), days(M,D),
Y = 365-D,
N is Y.
I don't understand how to make the two objects register as individual things that I can manipulate and therefore continue with the problem.
Can someone please please please point me in the right direction?
Thanks
Homework Question (AS REQUESTED).
Assume the presence of the days relation that describes how many days are in
each calendar month of a non-leap year.
days(1,31).
days(2,28).
(and so on).
The structure dateObject(M, D) describes a date. For example dateObject(6,5) would denote the 5th of June.
Write the relation difference(From, To, N), where N is the number of days between starting
date From and finishing date To. The From date is not included in the count. For
example:
?- difference(dateobject(2,1),dateobject(2,2),N).
N = 1.
If the day or month values in a date are invalid (e.g. dateobject(4,31), dateobject(13,1) then the
value of N returned should be -1. If the From date is later than To then the -1 error value should also be returned for N.
Here's a really bad solution - I'm sure someone smarter than me will come up with something better.
?- difference(dateobject(1,28),dateobject(6,1),N),write(N),nl.
days(1,31).
days(2,28).
days(3,31).
days(4,30).
days(5,31).
days(6,30).
days(7,31).
days(8,31).
days(9,30).
days(10,31).
days(11,30).
days(12,31).
daysbetween(M,FD,M,TD,N) :- !, N is TD - FD.
daysbetween(FM,FD,TM,TD,N) :-
days(FM,D),
FM2 is FM + 1,
daysbetween(FM2,FD,TM,TD,N2),
N is D + N2.
difference(dateobject(FM,FD),dateobject(TM,TD),N) :-
TM >= FM,
FD >= 1,
days(FM,FDM),
FD =< FDM,
TD >= 1,
days(TM,TDM),
TD =< TDM,
daysbetween(FM,FD,TM,TD,N),
N > 0,
!.
difference(_,_,-1).

Is Prolog suitable for this kind of project?

I am working on a project where I have to build an engine that is able to generate a bunch of premises and, given a true fact, tell what else can be deduced. Example given:
If John plays football then Mary makes cookies.
If Eric listens to rock then john doesn't play Football.
If Eric doesn't listen to rock then john plays Football.
Either Eric listens to rock or Luiza listens to rock.
When the given true fact is "Luiza listens to rock", then the solution should be:
Eric listens to rock = false
John plays football = true
Mary makes cookies = true
When the true fact is "Eric listens to rock", then the solution should be:
Luiza listens to rock = false
Jonh plays football = false
Mary makes cookies = (can't solve)
When the true fact is "John plays football", then the solution should be:
Mary makes cookies = true
Luiza listens to rock = (can't solve)
Jonh plays football = (can't solve)
Question: is Prolog the correct tool for solving this kind of problem? Code examples are appreciated.
Yes, Prolog would be awesome for this.
Here's an example of a similar problem:
/*
1. Mr. K made his sighting at some point earlier in the week than the one who saw the balloon, but at some point later in the week, than the one who spotted the Kite ( who isn't Ms. G ).
2. Friday's sighting was made by either Ms. Barn or the one who saw a plane ( or both ).
3. Mr. Nik did not make his sighting on Tuesday.
4. Mr. K isn't the one whose object turned out to be a telephone pole.
*/
?-
% Set up a list of lists to be the final solution
Days = [[tuesday,_,_],[wednesday,_,_],[thursday,_,_],[friday,_,_]],
/* 1 */ before([_,mr_k,_],[_,_,balloon],Days),
/* 1 */ before([_,_,kite],[_,mr_k,_],Days),
/* 2 */ (member([friday,ms_barn,_],Days);
member([friday,_,plane],Days);
member([friday,ms_barn,plane],Days)),
% Fill in the rest of the people
members([[_,mr_k,_],[_,ms_barn,_],[_,ms_g,_],[_,mr_nik,_]],Days),
% Fill in the rest of the objects
members([[_,_,balloon],[_,_,kite],[_,_,plane],[_,_,tele_pole]],Days),
% Negations should be done after the solution is populated
/* 1 */ member([_,NOT_ms_g,kite],Days), NOT_ms_g \= ms_g,
/* 3 */ member([tuesday,NOT_mr_nik,_],Days), NOT_mr_nik \= mr_nik,
/* 4 */ member([_,NOT_mr_k,tele_pole],Days), NOT_mr_k \= mr_k,
write(Days),
nl,
fail.
% Checks that `X` comes before `Y`
% in the list `Ds`
before(X,Y,Ds) :-
remainder(X,Ds,Rs),
member(Y,Rs).
% Finds a member of a list and
% unifies the third parameter such
% that it is the remaining elements in
% the list after the found member
remainder(X,[X|Ds],Ds).
remainder(X,[_|Ds],Rs) :- remainder(X,Ds,Rs).
% An extended version of `member` that
% checks if the members of the first list
% are all members of the second
members([],_).
members([X|Xs],Ds) :-
member(X,Ds),
members(Xs,Ds).
It produces the following result:
[[tuesday, ms_g, tele_pole], [wednesday, mr_nik, kite], [thursday, mr_k, plane], [friday, ms_barn, balloon]]

Seating chart starts to output wrong permutations in Prolog

I have a homework assignment where I must write a predicate seatingChart(X):- which will have 8 seats. The rules are:
Adjacent seating partners must be of the opposite gender.
Adjacent seating partners must share at least one of the same hobby.
I thought I wrote the code below to create the correct case.
person(jim,m).
person(tom,m).
person(joe,m).
person(bob,m).
person(fay,f).
person(beth,f).
person(sue,f).
person(cami,f).
% Database of hobbies
% hobbies(name,hobby). -> People can have multiple hobbies)
hobbies(jim, sup).
hobbies(jim, fish).
hobbies(jim, kayak).
hobbies(tom, hike).
hobbies(tom, fish).
hobbies(tom, ski).
hobbies(joe, gamer).
hobbies(joe, chess).
hobbies(joe, climb).
hobbies(bob, paint).
hobbies(bob, yoga).
hobbies(bob, run).
hobbies(fay, sup).
hobbies(fay, dance).
hobbies(fay, run).
hobbies(beth, climb).
hobbies(beth, cycle).
hobbies(beth, fish).
hobbies(sue, yoga).
hobbies(sue, skate).
hobbies(sue, ski).
hobbies(cami, run).
hobbies(cami, kayak).
hobbies(cami, gamer).
%% ANSWER %%
% return a pair of opposite gender people
gender(PersonX, PersonY):-
person(PersonX,GenderX),
person(PersonY,GenderY),
GenderX \= GenderY.
% return the pair of similar interests.
similarHobbies(PersonX, PersonY):-
hobbies(PersonX, HobbyX),
hobbies(PersonY, HobbyY),
HobbyX == HobbyY.
% Create the rules for our seating chart list
seatingRules([P1,P2,P3,P4,P5,P6,P7,P8|_]):-
% Have each adjacent person be of the opposite gender
gender(P1,P2),
gender(P3,P4),
gender(P5,P6),
gender(P7,P8),
gender(P8,P1),
% Have each adjacent person have at least one of the same hobby
similarHobbies(P1,P2),
similarHobbies(P3,P4),
similarHobbies(P5,P6),
similarHobbies(P7,P8).
% Generate a list of all the names from person(...)
people(P):-
findall(X, person(X,_), P).
% Generate a list of permutations of people
permPeople([P1,P2,P3,P4,P5,P6,P7,P8]):-
permutation([P1,P2,P3,P4,P5,P6,P7,P8],
[jim,tom,joe,bob,fay,beth,sue,cami]),
\+error([P1,P2,P3,P4,P5,P6,P7,P8]).
error([P1,P2,P3,P4,P5,P6,P7,P8]):-
\+seatingRules([P1,P2,P3,P4,P5,P6,P7,P8]).
seatingChart(X):-
permPeople(X).
When I run this using seatingChart(X). in SWI-Prolog I get the following answer first:
X = [jim, fay, tom, beth, joe, cami, bob, sue] ;
However, my subsequent permutations seem to be flat out wrong.. after hitting ; a few more times this says it's a valid answer:
X = [jim, beth, sue, tom, joe, cami, bob, fay] .
What am I doing wrong? Or what is causing my permutations to start not following the seating chart rules?
Shouldn't the seating rule predicate contain all pairs?
% Create the rules for our seating chart list
seatingRules([P1,P2,P3,P4,P5,P6,P7,P8|_]):-
% Have each adjacent person be of the opposite gender
gender(P1,P2),
gender(P2,P3),
gender(P3,P4),
gender(P4,P5),
gender(P5,P6),
gender(P6,P7),
gender(P7,P8),
gender(P8,P1),
% Have each adjacent person have at least one of the same hobby
similarHobbies(P1,P2),
similarHobbies(P2,P3),
similarHobbies(P3,P4),
similarHobbies(P4,P5),
similarHobbies(P5,P6),
similarHobbies(P6,P7),
similarHobbies(P7,P8),
similarHobbies(P8,P1).

Prolog riddle solving

The statement :
Four couples in all
Attended a costume ball.
2
The lady dressed as a cat
Arrived with her husband Matt.
3
Two couples were already there,
One man dressed like a bear.
4
First to arrive wasn't Vince,
But he got there before the Prince.
5
The witch (not Sue) is married to Chuck,
Who was dressed as Donald Duck.
6
Mary came in after Lou,
Both were there before Sue.
7
The Gipsy arrived before Ann,
Neither is wed to Batman.
8
If Snow White arrived after Tess,
Then how was each couple dressed?
My code is here , but it returns false :
sol(S):-
S=[[1,L1,M1,LD1,MD1],
[2,L2,M2,LD2,MD2],
[3,L3,M3,LD3,MD3],
[4,L4,M4,LD4,MD4]],
member([_,_,matt,cat,_],S),
member([ALR,_,_,_,bear],S),
(ALR =:= 1 ; ALR =:= 2),
not(member([1,_,vince,_,_],S)),
member([VN,_,vince,_,_],S),
member([PS,_,_,_,prince],S),
VN < PS ,
member([_,_,chuck,witch,donald],S),
not(member([_,sue,_,witch,_],S)),
member([MRY,mary,_,_,_],S),
member([LOU,_,lou,_,_],S),
member([SUE,sue,_,_,_],S),
MRY > LOU,
MRY < SUE,
member([GPS,_,_,gipsy,_],S),
member([ANN,ann,_,_,_],S),
GPS < ANN ,
not(member([_,_,_,gipsy,batman],S)),
not(member([_,ann,_,_,batman],S)),
member([SW,_,_,snowwhite,_],S),
member([TS,tess,_,_,_],S),
SW > TS ,
perm([sue,mary,ann,tess],[L1,L2,L3,L4]),
perm([matt,lou,vince,chuck],[M1,M2,M3,M4]),
perm([cat,witch,gipsy,snowwhite],[LD1,LD2,LD3,LD4]),
perm([donald,prince,batman,bear],[MD1,MD2,MD3,MD4]).
takeout(X,[X|R],R).
takeout(X,[F|R],[F|S]) :- takeout(X,R,S).
perm([],[]).
perm([X|Y],Z) :- perm(Y,W), takeout(X,Z,W).
Any solution ?
You should move all your not(...) goals to the very end of the predicate.
not(G) means, "G is impossible to satisfy right now". When tried too early, with many still non-instantiated variables in the lists, it is in fact very often possible to satisfy a goal, and the whole not(...) call will fail right away.
Alternatively, delay the checking of the inequality on a variable until it is instantiated, e.g. in SWI Prolog with freeze/2 (as seen e.g. in this answer).

Resources