Search code examples
prolog

Solving a logical puzzle with Prolog gives a wrong answer


I've got a simple puzzle: We've got 2 people, Zod and Jenk. Each of them is either Truthful and always making true statements, or Lying and always making false statements. Zod says: "We are both truthful". Jenk says: "At lease one of us is lying".

The solution is that Zod is lying and Jenk is truthful.

I'm trying to solve it using Prolog but somehow it comes to a conclusion that both are Truthful. Here's the program:

% Define the possible states for each person
state(zod, truthful).
state(zod, lying).
state(jenk, truthful).
state(jenk, lying).

% Zod says: "We are both truthful".
zod_statement :- state(zod, truthful), state(jenk, truthful).

% Jenk says: "At least one of us is lying".
jenk_statement :- state(zod, lying); state(jenk, lying).

% Check if a person's statement is true based on their state
statement(zod) :- 
    state(zod, truthful), zod_statement;
    state(zod, lying), \+ zod_statement.

statement(jenk) :- 
    state(jenk, truthful), jenk_statement;
    state(jenk, lying), \+ jenk_statement.

% The solution must satisfy both statements
solution(ZodState, JenkState) :-
    state(zod, ZodState),
    state(jenk, JenkState),
    statement(zod),
    statement(jenk).

If I comment out state(zod, truthful) and leave the only possibility for Zod as lying it works, but if it's got both options for Zod it comes back with JenkState = ZodState, ZodState = truthful which is incorrect.

What's wrong with my code?


Solution

  • Can reify this, so that the person's variable is whether they are telling the truth.

    truthful(true).
    truthful(false).
    
    zod_jenk(Z, J) :-
        person_truthful(zod, Z, J),
        % Only this Jenk rule is actually needed
        person_truthful(jenk, J, Z),
        % Ensuring that Z and J are ground
        truthful(Z),
        truthful(J).
    
    % Zod is telling the truth if Jenk is telling the truth
    person_truthful(zod, true, true).
    % If Zod is lying, don't know about Jenk
    person_truthful(zod, false, _J).
    
    % If Jenk is truthful then Zod must be lying (to be the "at least 1")
    person_truthful(jenk, true, false).
    % If Jenk is lying, then at least one of them is lying - contradiction
    

    Result in swi-prolog:

    ?- zod_jenk(Z, J).
    Z = false,
    J = true.
    

    The Zod rule isn't even needed.