How to solve this puzzle in Prolog? (robbery) - prolog

Three suspects are involved in a robbery, Alice, Bob, Carl. At least one of them are guilty.
Here are the conditions:
If A is guilty, he has exactly 1 accomplice.
If B is guilty, he has exactly 2 accomplices.
Who are guilty?
How can I write a Prolog script to solve this problem which guilty(X) gives the gangs?

Here is a solution using clpb :
:- use_module(library(clpb)).
solve(A,B,C) :-
% there is a least one guilty
sat(A + B + C),
% If A is guilty, he has exactly 1 accomplice.
sat(A =< B # C),
% if B is guilty, he has exactly 2 accomplices.
sat(B =< A * C),
% Assigns truth values to the variables such that all constraints are satisfied.
labeling([A,B,C]).
Now we get :
?- solve(A,B,C).
A = B, B = 0,
C = 1 ;
A = C, C = 1,
B = 0.
The answer A = B, B = 0, C = 1 means that C is guilty the other one that A and C are guilties.

Let's encode the state of our world as three numbers, A, B, and C.
Each number will be either 1 (guilty) or 0 (innocent).
The conditions are:
at_least_one(A,B,C):- 0 < A+B+C.
one_accomplice(A,B,C):- A == 1 -> 1 is ....... ; true.
two_accomplices(A,B,C):- B == 1 -> ....... ; true.
The three rules holding together is
ok(A,B,C):- at_least_one(A,B,C),
one_accomplice(A,B,C),
...... .
Now we can find out the gangs, as
the_guilty([A,B,C]):-
( A = 0 ; A = 1 ),
....
....
ok( ..... ).
The last thing is to report the three given numbers as names of people. We know that the first number is for "Alice", the second is for "Bob", etc.
Prolog is easy.

Related

How do I understand the logic behind this Prolog problem?

I am trying to understand how to solve the following problem in Prolog. Given the following Prolog code, list all solutions in order for the query c(X,Y,Z).
a(1).
a(2).
b(a).
c(A,B,C) :- a(A),d(B,C).
c(A,B,C) :- b(A),d(B,C).
d(B,C) :- a(B),!,a(C).
d(B,_) :- b(B).
I loaded up SWI Prolog to try it out myself, but I am unsure how it reaches the conclusion:
X = Y, Y = Z, Z = 1 ;
X = Y, Y = 1,
Z = 2 ;
X = 2,
Y = Z, Z = 1 ;
X = Z, Z = 2,
Y = 1 ;
X = a,
Y = Z, Z = 1 ;
X = a,
Y = 1,
Z = 2.
Starting small, I tried to query just d(B,C). with the same code, leading to the following conclusion:
Y = Z, Z = 1 ;
Y = 1,
Z = 2.
I understand how to get the Y=1 and Z=1,2, but I am unsure how the code leads to Y=Z. If anyone could help me with the logic to reach these conclusions, that would be well appreciated!
Let's start by looking at the predicates a/1 and b/1: We expect them to produce two and one answer(s) respectively and indeed that is the case:
?- a(X).
X = 1 ? ;
X = 2
?- b(X).
X = a
Next let's look at the predicate d/2 but without the cut, that is:
d(B,C) :- a(B),a(C).
d(B,_) :- b(B).
If we query this predicate:
?- d(B,C).
Prolog will start with the first rule of the predicate d/2, that is d(B,C) :- a(B),a(C). and tries to prove the first goal a(B). That succeeds with the substitution B = 1. So Prolog goes on to prove the goal a(C). Again that succeeds with the substitution C = 1. Prolog then reports the first solution to the query:
?- d(B,C).
B = C = 1 ?
So we ask if there are any more solutions by pressing ;:
?- d(B,C).
B = C = 1 ?;
Now Prolog tries to find another solution for a(C) and finds the substitution C = 2:
?- d(B,C).
B = C = 1 ?;
B = 1,
C = 2 ?
Again we ask if there's more:
?- d(B,C).
B = C = 1 ?;
B = 1,
C = 2 ? ;
Now Prolog fails to find another solution for a(C), so it backtracks to the first goal and tries to find an alternative solution for a(B) and succeeds with the substitution B = 2. So it goes on to prove the second goal a(C) to which again the substitution C = 1 is found. So Prolog reports this new solution.
?- d(B,C).
B = C = 1 ?;
B = 1,
C = 2 ? ;
B = 2,
C = 1 ?
We ask for yet more solutions:
?- d(B,C).
B = C = 1 ?;
B = 1,
C = 2 ? ;
B = 2,
C = 1 ? ;
So Prolog looks for another solution for a(C) and finds C = 2. So it answers:
?- d(B,C).
B = C = 1 ?;
B = 1,
C = 2 ? ;
B = 2,
C = 1 ? ;
B = C = 2 ?
We press ; again
?- d(B,C).
B = C = 1 ?;
B = 1,
C = 2 ? ;
B = 2,
C = 1 ? ;
B = C = 2 ? ;
And Prolog looks for other solutions for a(C) but can't find any. So it backtracks to the goal a(B) and again fails to produce new answers here. So those are all solutions for the first rule of d/2. Prolog now goes on and tries to find a solution for the second rule d(B,_) :- b(B).. This rule only contains one goal: b(B) and Prolog finds a substitution: B = a, so it answers:
?- d(B,C).
B = C = 1 ?;
B = 1,
C = 2 ? ;
B = 2,
C = 1 ? ;
B = C = 2 ? ;
B = a
?-
And there are no more solutions. Let's observe that while Prolog traverses the proof tree as seen above, every time alternative substitutions can't be ruled out a choice point is created and in the course of the search for answers Prolog can backtrack to these choicepoints to look for alternative solutions. Every time an answer in the above query ended with ? Prolog still had some choice points left to explore and by pressing ; we asked it to explore them.
Now let's move our attention back to your posted version of d/2 with the cut in the first rule:
d(B,C) :- a(B),!,a(C).
d(B,_) :- b(B).
What a cut does is to prune away choice points back to where it was when the predicate that contains it was called. So it is essentially throwing away untried alternatives, thereby committing Prolog to the first substitution(s) it finds. So if we issue the query again with this version:
?- d(B,C).
Prolog again starts with the first rule, looking for a solution for a(B), successfully substituting B = 1 encountering the cut, therefore committing to this solution and then searching for a proof for a(C), again successfully substituting C = 1. Prolog then answers:
?- d(B,C).
B = C = 1 ?
indicating that there are open choice points. Again we want more:
?- d(B,C).
B = C = 1 ? ;
Prolog finds another substitution C=2 for the goal a(C) and reports:
?- d(B,C).
B = C = 1 ? ;
B = 1,
C = 2
?-
But this time the query terminates. That is because the choicepoint for an alternative solution for the goal a(B) was pruned away thereby leaving us without the solutions B = 2, C = 1 and B = C = 2. Furthermore the choicepoint that would have led to the second rule being explored was also pruned away thereby robbing us of the solution B = a. Cuts that prune away correct solutions as seen above are often referred to as red cuts in the literature.
Note that by cutting away actual solutions this program is not logically correct anymore. With the above query you asked for all solutions of the predicate d/2 but got only two of them. However, if you ask for the other solutions specifically, the respective queries still succeed:
?- d(2,1).
yes
?- d(2,2).
yes
?- d(a,_).
yes
?-
Returning to your original question, let's compare the query ?- c(X,Y,Z). with both versions of d/2. To make comparisons easier, the variables X, Y and Z are replaced with A, B and C just like they occur in the predicate definitions:
% cut-free version % version with the cut:
?- c(A,B,C). ?- c(A,B,C).
A = B = C = 1 ? ; A = B = C = 1 ? ;
A = B = 1, A = B = 1,
C = 2 ? ; C = 2 ? ;
A = C = 1,
B = 2 ? ;
A = 1,
B = C = 2 ? ;
A = 1,
B = a ? ;
A = 2, A = 2,
B = C = 1 ? ; B = C = 1 ? ;
A = C = 2, A = C = 2,
B = 1 ? ; B = 1 ? ;
A = B = 2,
C = 1 ? ;
A = B = C = 2 ? ;
A = 2,
B = a ? ;
A = a, A = a,
B = C = 1 ? ; B = C = 1 ? ;
A = a, A = a,
B = 1, B = 1,
C = 2 ? ; C = 2
A = a,
B = 2,
C = 1 ? ;
A = a,
B = C = 2 ? ;
A = B = a
For the sake of brevity I'm not going to iterate through the steps of the proof, but let's observe a few key things: Firstly, in both rules of c/3 the predicate d/2 is called by the same goal d(B,C) and we have already looked at that goal in detail for both versions. Secondly, we can observe that both rules of c/3 are taken into regard in the search for answers with both versions of the predicate by seeing all three substitutions, A = 1, A = 2 and A = a appearing in both answer-sets. So the cut doesn't prune away the choice point here. Thirdly, the three solutions of d/2 that are pruned away by the cut: B = 2, C = 1, B = 2, C = 2 and B = a are missing in the answers for all three substitutions of A in the version with the cut. And finally, the version of c/3 with the cut is also not logically correct, as the most general query does not give you all the answers but you can query for the thrown away solutions directly and Prolog will find them, e.g.:
?- c(1,2,1).
yes
?- % etc.
Try the query trace, c(X,Y,Z).

Prolog predicate - Same functionality, different results

I tried to implement a predicate count/5 that given a List, an element E and an initial count of 0 it is supposed to return the total number of time E shows up consecutively and the remaining list.
So by count([1,1,1,2,3,4,1],1,0,COUNT,REMAINING_LIST) the following result is expected :
COUNT = 3 , REMAINING_LIST = [2,3,4,1]
I implemented the following 2 versions of it :
count1([],_,C,C,[]).
count1([E|T],E,C,EC,L) :-
!,
C1 is C + 1,
count1(T,E,C1,EC,L).
count1([H|T],E,C,C,[H|T]) :- H \== E.
count2([],_,C,C,[]).
count2([H|T],E,C,EC,L) :-
H == E,
!,
C1 is C + 1,
count2(T,E,C1,EC,L).
count2([H|T],E,C,C,[H|T]) :- H \== E.
Both seem to work just fine for regular lists - I am aware that some edge cases are not taken into account - but when I decided to run the following query out of curiosity I got 2 completely different results :
count1([1,1,X,X,2],1,0,S,Z).
S = 4,
X = 1,
Z = [2]
count2([1,1,X,X,2],1,0,S,Z).
S = 2,
Z = [X, X, 2]
Any explanation on why these 2 queries differ when it comes to their result would be appreciated.
Edit :
One thing I thought of is that probably in the following call of count1 :
count1([X|T],1,2,EC,L)
X turns out to be 1 so that the call complies with the defined rule that the head element of the list should be equal to the element we are looking for / that is being counted.

Contradiction in Prolog

I'm testing Prolog ability to test contradiction. To test this, I came up with the following scenario:
There are three suspects: a, b, and c, who are on trial, and one of the suspect is guilty and the rest are innocent.
The facts of the case are,
(Fact 1) if a is innocent then c must be guilty, and
(Fact 2) if a is innocent then c must be innocent.
The answer to who is guilty is suspect 'a' because suspect 'c' cannot be both guilty and innocent. The following code is my implementation:
who_guilty(Suspects) :-
% Knowledge Base
Suspects=[[Name1,Sentence1],
[Name2, Sentence2],
[Name3,Sentence3]],
% Suspects
Names=[a,b,c],
Names=[Name1,Name2,Name3],
% One Is Guilty
Sentences=[innocent,innocent,guilty],
permutation(Sentences,[Sentence1,Sentence2,Sentence3]),
% If A Innocent Then C Is Guilty
(member([a,innocent],Suspects) -> member([c,guilty], Suspects) ; true),
% If A Innocent Then C Is Innocent
(member([a,innocent],Suspects) -> member([c,innocent], Suspects) ; true).
To get Prolog's answer, the query that needs to be run is who_guilty(S). Prolog will then output two identical answers:
S = [[a, guilty], [b, innocent], [c, innocent]] ?
S = [[a, guilty], [b, innocent], [c, innocent]]
My central question is how can I get only one answer instead of two?
Using clpfd library, you can solve this problem easily:
solve(L):-
L = [A,B,C], %0 innocent, 1 guilty
L ins 0..1,
A + B + C #= 1, %one is guilty
A #= 0 #==> C #= 1, %if a is innocent then c must be guilty
A #= 0 #==> C #= 0, %if a is innocent then c must be innocent
label(L).
?- solve(L).
L = [1, 0, 0]
Using clpb :
:- use_module(library(clpb)).
% 0 means guilty
% 1 means innocent
guilty(A,B,C) :-
% only one is guilty
sat(~A * B * C + A * ~B * C + A * B * ~C),
% Fact 1
sat(A =< ~C),
% Fact 2
sat(A =< C).
?- guilty(A,B,C).
A = 0,
B = C, C = 1.
A compact solution, that follows your intuition about expressing the facts.
who_guilty(L) :-
select(guilty,L,[innocent,innocent]),
( L = [innocent,_,_] -> L = [_,_,guilty] ; true ),
( L = [innocent,_,_] -> L = [_,_,innocent] ; true ).
yields:
?- who_guilty(L).
L = [guilty, innocent, innocent] ;
false.
thanks to joel76 (+1), here is a more synthetic solution based on library(clpb)
?- sat(card([1],[A,B,C])*(~A =< ~C)*(~A =< C)).
A = 1,
B = C, C = 0.
1 means guilty...
You should add a new predicate which checks whether someone is both innocent and guilty, which then answers yes to whether there are contradictory outcomes. You are giving Prolog two facts, meaning two correct conclusions to the query. Your real question is "do I have facts contradicting each other?", which means A and NOT A are both true at the same time. contradiction(A, not(A)).
All truths are universal and you are giving two truths that are contradictory to Prolog, so both are true to Prolog.

Prolog - summing up predicate results

Let's say i have the following predicate:
func1(X,B,R)
I provide it with a certain X and B and in return it gives me 5 different results for R.
EDIT:
The X and B do not specify a range. rather, X specify an integer (say 120) and B specifies all integers (starting from 1) whose cubic is less than X.
What func1 does is calculating R as the result the remainder.
In this case where X=120:
B = 1, R = 119 (120-1^3)
B = 2, R = 112 (120-2^3)
B = 3, R = 93 (120-3^3)
B = 4, R = 56 (120-4^3)
It would not calculate B=5 since 5^3 = 125 which is greater than 120, so it stops here.
How can i make a predicate such as:
func2(R,S)
That would accept all of the results given by R, sum them up and store them in S?
Thanks!
To start with, since the values of B are totally derivable from the value of X, I wouldn't include both as arguments in func1. I'll introduce a predicate func3/2 which is true if the second argument is derivable from the first (i.e., func3(X, B) is true if B is derivable from X):
func1(X, R) :-
func3(X, B),
...
What will happen if you query func1(120, R) is you'd get one or more results for R. Then you can use findall/3 as I indicated in my comment:
func2(X, S) :-
findall(R, func1(X, R), Rs),
sumlist(Rs, S).
To define func3/2 the cleanest approach would be to use CLP(FD):
:- use_module(library(clpfd)).
func3(X, B) :-
B #>= 0,
(X - B^3) #>= 0,
label([B]).
Here's an example of what func3 does:
?- func3(120, B).
B = 1 ;
B = 2 ;
B = 3 ;
B = 4.
A much less desirable way to do this if you can't use CLP(FD) would be to use between and define the upper limit of B to be the greatest integer not exceeding the cube root of X:
func3(X, B) :-
Limit is floor(exp(log(X) / 3)),
between(1, Limit, B).
Which yields the same result as above.

Use if else in Prolog

I want to implement something of this sort using if else in Prolog.Being from C++ background I am finding it hard to implement.How can I do it???
if(X!=4 || Y!=3)
printf("1");
else if(A!=4 || Y=3 && Z==2)
printf("2");
If-else in Prolog is simply
Condition → Then ; Else
resp.
Condition1 → Then1
; Condition2 → Then2
; …
; Else
Your C code
if(X!=4 || Y!=3)
printf("1");
else if(A!=4 || Y==3 && Z==2)
printf("2");
would translate to
(X \= 4; Y \= 3) -> write('1')
; (A \= 4; Y = 3, Z = 2) -> write('2')
; true % or drop this line, then it will raise a unification error
But be aware that you should use write/1 only in the outer loops, because the function is impure (the order of execution matters).
Probably you should write something like:
( (X \= 4; Y \= 3) -> Message = '1'
; (A \= 4; Y = 3, Z = 2) -> Message = '2'),
write(Message).
Be aware that Prolog is a logical programming language. Ofttimes you will find that a verbatim translation from an imperative programming language is not the best solution.
As an example see a question I answered a few days ago: "Calculating whether number is prime in Prolog":
Imperative:
is_prime(A) :-
A > 1, % Negative numbers, 0 and 1 are not prime.
is_prime(A, 2). % Begin iteration:
is_prime(A, B) :- % Test if A divides by B without remainder
B >= A % The limit was reached?
-> true % Then it's prime.
; 0 is A mod B % B divides A without a remainder?
-> false % Then it's not prime.
; C is B + 1, % Otherwise: C is B + 1
is_prime(A, C). % Test if C divides A.
Logical:
is_prime(A) :-
L is A - 1, % L is floor(sqrt(A)) ← optimized upper bound
\+ (between(2, L, X), % Is there a number X between 2 and L
0 is A mod X). % that divides A without a remainder?
Which is easier to read?
Try this bit of code:
main:- (X\=4 ; Y\=3) -> write('1') ;
(A \=3 ; Y is 3 , Z is 2) -> write('2').
\= equals !=
; equals ||
, equals &&
is equals =, but you can also use = for this.
-> equals then
I added four read/1 to test the code:
main:- read(X),read(Y),read(A),read(Z),
(X\=4 ; Y\=3) -> write('1') ;
(A \=3 ; Y is 3 , Z is 2) -> write('2').
and I get:
3 ?- main.
|: 4.
|: 3.
|: 2.
|: 2.
2
true
Second if works
6 ?- main.
|: 3.
|: 6.
1
true.
First if works

Resources