Search code examples
prologzebra-puzzle

Prolog puzzle baker, meatman and carpenter


This is a very simple puzzle I found on the web, I don't find any solution on internet. The rules are simples:

  • There are 6 artisans, M.Baker and his son, M.Carpenter and his son and M.Meatman and his son
  • Each artisan can be baker, carpenter or meatman
  • Son and father cannot do the same job
  • The lastname couldn't be the job name (M.Baker and his son cannot be baker)

We know that: - M.Meatman's son is baker - M.Baker do the same job than M.Carpenter's son

I implemented this predicates:

% swipl prolog
% M.Meatman's son is baker
% M. Baker do job of M. Carpenter's son

% jobs
job(baker).
job(meatman).
job(carpenter).

% fathers
artisan(fatherbaker).
artisan(fathercarpenter).
artisan(fathermeatman).

% sons
artisan(sonbaker).
artisan(soncarpenter).
artisan(sonmeatman).

% some links
father(fatherbaker, sonbaker).
father(fathermeatman, sonmeatman).
father(fathercarpenter, soncarpenter).

son(S, F) :- father(F, S).

same_name(fathercarpenter, soncarpenter,  carpenter).
same_name(fathermeatman, sonmeatman, meatman).
same_name(fatherbaker, sonbaker, baker).

% rules:
do_job(Artisan, Job) :-
    Artisan==sonmeatman,!, 
    artisan(Artisan),
    job(Job),
    Job=baker. % M.Meatman's son is baker (rule 1)

do_job(Artisan, Job) :- 
    Artisan==fatherbaker,!, 
    artisan(Artisan),
    job(Job),
    do_job(soncarpenter, Job). % M.Baker do M.Carpenter's son job

% not relevant...
%do_job(Artisan, Job) :-
%        Artisan == soncarpenter,!,
%   job(Job),
%   artisan(Artisan),
%        do_job(fatherbaker, Job). % rule 2 inverted

% checking if father job is not the same and name are not forgotten
do_job(Artisan, Job) :-
    artisan(Artisan),
    job(Job),
    father(Father, Artisan),
    do_job(Father, JobFather),
    Job \== JobFather,
    not(same_name(Artisan,_,Job)).

% checking if son job is not the same and name are not forgotten
do_job(Artisan, Job) :-
    artisan(Artisan),
    job(Job),
    son(Artisan, Son),
    do_job(Son, JobSon),
    Job \== JobSon,
    not(same_name(_, Artisan, Job)).

Then I try:

swipl
?- do_job(sonmeatman, X).
X = baker ;
false.
?- do_job(fatherbaker, X).
false.

Can you please tell me where I'm wrong.

  • Note: I'm a newbie in Prolog. I never used this language before (I'm Golang, Python, C programmer...).
  • Note 2: Excuse my english, I've just translated my example that was in french, maybe jobnames or verbs are not corrects...
  • Note 3: I've already tried to implement zebra puzzle and I realize that was simpler to resolve than this one... strange ?

Solution

  • You're overengineering, and reifying too much of the problem (a typical Prolog beginner's error). What you need to write is a six-argument predicate

    jobs(BakerSrJob, BakerJrJob,
         CarpenterSrJob, CarpenterJrJob,
         MeatmanSrJob, MeatmanJrJob) :-
        ...
    

    and in the body, constraints on those variables. E.g.

    member(BakerJrJob, [carpenter, meatman])
    

    expresses that Baker jr. is either a carpenter or a meatman, and

    BakerJrJob \= BakerSrJob
    

    expresses that father and son Baker have different jobs. A couple of these member calls and \= constraints should be enough to encode all the knowledge necessary. (Just hardcode the identity of the persons in the variable name, instead of representing and inspecting the names as atoms. Don't try to write a generic puzzle solver if you're just starting out.)