The problem is
Brown, Clark, Jones and Smith are four substantial citizens who serve the community as architect, banker, doctor and lawyer, though not necessarily respectively. Brown who is more conservative than Jones but more liberal than Smith, is a better golfer than the men who are older than he is and has a larger income than the men who are younger than Clark
The banker who earns more than the architect, is neither the youngest or the oldest.
The doctor, who is a poorer golfer than the lawyer, is less conservative than the architect
As might be expected, the oldest man is the most conservative and has the largest income, and the youngest man is the best golfer
what is each man's profession?
I've written
jobs(L) :- L = [[brown,_,_,_,_,_],
[clark,_,_,_,_,_],
[jones,_,_,_,_,_],
[smith,_,_,_,_,_]],
% [name,job,conservative,golf,income,age]
% conserative: 1 = least conservative, 4 = most conservative
% golf: 1 = worst golfer, 4 = best golfer
% income: 1 = least income, 4 = highest income
% age: 1 = youngest, 4 = oldest
% Brown is more conservative than Jones. Brown is less conservative than Smith.
member([brown,_,C1,_,_,_],L),
member([jones,_,C2,_,_,_],L),
C1 > C2,
member([smith,_,C3,_,_,_],L),
C1 < C3,
% Brown is a better golfer than those older than him.
member([brown,_,_,G1,_,A1],L),
member([_,_,_,G2,_,A2],L),
G1 > G2,
A2 > A1,
% Brown has a higher income than those younger than Clark.
member([brown,_,_,_,I1,_],L),
member([clark,_,_,_,_,A3],L),
member([_,_,_,_,I2,A4],L),
I1 > I2,
A3 > A4,
% Banker has a higher income than architect. Banker is neither youngest nor oldest.
member([_,banker_,_,I3,A5],L),
member([_,architect,_,_,I4,_],L),
I3 > I4,
(A5 = 2;A5 = 3),
% Doctor is a worse golfer than lawyer. Doctor is less conservative than architect.
member([_,doctor,C4,G3,_,_],L),
member([_,lawyer,_,G4,_,_],L),
member([_,architect,C5,_,_,_],L),
G3 < G4,
C4 < C5,
% Oldest is most conservative and has highest income.
member([_,_,4,_,4,4],L),
% Youngest is the best golfer.
member([_,_,_,4,_,1],L).
When I ask it
?- jobs(L).
I get
ERROR: >/2: Arguments are not sufficiently instantiated
I'm not sure what the error means, I believe I've translated all the clues.
You code works exactly as expected if you just use finite domain constraints instead of lower-level arithmetic. For example, use
(#>)/2
instead of(>)/2
.After you get it to run beyond this instantiation error by using constraints, you will then notice that among other things, your code has a typo:
banker_
. Also, you are not formulating the implication correctly, and your predicate will therefore yieldfalse
.Here is a slightly modified version of your code, changed to use finite domain constraints and correcting the two mentioned mistakes:
You can use
label/1
to search for concrete solutions. As you can see with the following query, there is a unique solution with respect to professions:which yields:
And that's without even requiring that all income levels etc. be different. If you want, you can express this constraint easily by adding:
to this formulation.
(continuing the trail blazed by CapelliC...) Selecting from domains and (better yet, while) applying the rules is usually the way to go in such puzzles. Carefully testing as soon as possible, to eliminate wrong choices as soon as possible - but not sooner.
We can't arithmetically compare unknown values, this is what the error means:
>
compares two known arithmetical values to which its arguments are instantiated. But if a Prolog logical variable is not yet instantiated it means that its value is still unknown.In constraint logical programming (CLP) we can register such constraints upfront, - but not in vanilla Prolog. Though many a modern Prolog has CLP packages or predicates available in them. SWI Prolog has it too. But in vanilla Prolog code, we must be careful.
Testing:
([_,_,Conserv,Golf,Income,Age])
This is actually one solution, according to the way the puzzle question is asked.
you need to bind your variables to a domain before to use them, the easiest way is permutation/2:
now the rules can be used
efficiency wise, when you select (via member) a named member, 'fetch' all related variables at once (brown attributes' are used later). Also beware that referencing in different selection variables J1,C1, etc could lead to unwanted binding.
A rule difficult to express is
where vardiff/3 is a simple convenience:
Of course, if your Prolog has available, CLP(FD) is a much better choice.
Here's my answer to the problem:
It needs these supporting predicates:
Now, the only issue I had is that this gives me more than one answer. Unless I've got the logic wrong this is what I got:
I can constrain it to a single solution if I also say that Clark's income is greater than Brown's.
Can anyone confirm if my answer is correct or not and if there should be more constraints?
My solution based off what CapelliC said
I get
Multiple answers that repeat so I deleted some of them.
All the clues are satisfied except when Clark is the second oldest for example
This clue is violated
And for all of my answers Brown is the youngest so clues like
seem a bit pointless...