Search code examples
prologzebra-puzzle

Solving a logic task using Prolog


I started to learn Prolog and I can't solve a difficult task for me.

On Halloween, three friends - Bob, Mark, Alex, chose the costumes of three ghosts: a ghost, a zombie, a werewolf. It is known that:

  • Bob is the tallest.
  • The one who chose the werewolf costume is shorter than the one who chose the ghost costume.
  • Alex didn't fit the werewolf costume.
  • None of the friends have the same name as the Halloween character from the selected costumes.

Which costume did each of the friends choose?

I solved the problem mathematically, but it is impossible to describe its solution in Prolog.


Solution

  • (the previous versions of this answer suffered from the "premature implementation syndrome". Here's another take on it)

    I started to learn Prolog and I can't solve a difficult task for me. [...] it is impossible to describe its solution in Prolog.

    Nah! :) If we can say it in English we can say it in Prolog:

    task( Sol) :-
    

    On Halloween, three friends - Bob, Mark, Alex, chose the costumes of three ghosts: a ghost, a zombie, a werewolf.

        Sol = [bob-C1, mark-C2, alex-C3],
        permutation( [ghost, zombie, werewolf], [C1,C2,C3]),
    

    It is known that:

    • Bob is the tallest.
        select( bob-_, Sol, ShorterOnes),
    
    • The one who chose the werewolf costume is shorter than the one who chose the ghost costume.
        member( _-werewolf, ShorterOnes),
    
    • Alex didn't fit the werewolf costume.
        C3 \= werewolf,
    
    • None of the friends have the same name as the Halloween character from the selected costumes.

      Apparently, there is one Bob the Ghost, so

        C1 \= ghost,
    

    Which costume did each of the friends choose?

        true.  % nothing more to say
    

    So we were able to describe the problem in Prolog, after all.

    Having described the problem properly, we already have our program to find the solution.

    Testing:

    13 ?- task(X).
    X = [bob-zombie, mark-werewolf, alex-ghost] ;
    false.
    

    This can be written a tiny bit shorter if we realize that selecting bob as the tallest

        select( bob-_, Sol, ShorterOnes),
        member( _-werewolf, ShorterOnes),
    

    leaves us with mark and alex as the shorter ones:

    task( [bob-C1, mark-C2, alex-C3]) :-
        permutation( [ghost, zombie, werewolf], [C1,C2,C3]),
        member( werewolf, [C2, C3]),
        C3 \= werewolf,
        C1 \= ghost.
    

    Glancing at this short code for a minute, we realize that it can be further simplified to

    task( [bob-C1, mark-C2, alex-C3]) :-
        permutation( [ghost, zombie, werewolf], [C1,C2,C3]),
        ( werewolf = C2 ; werewolf = C3 ),
        C3 \= werewolf,
        C1 \= ghost.
    

    and this to

    task( [bob-C1, mark-C2, alex-C3]) :-
        permutation( [ghost, zombie, werewolf], [C1,C2,C3]),
        werewolf = C2,
        C1 \= ghost.
    

    and this to

    task( [bob-C1, mark-werewolf, alex-C3]) :-
        permutation( [ghost, zombie], [C1,C3]),
        C1 \= ghost.
    

    and this to

    task( [bob-zombie, mark-werewolf, alex-ghost]).
    

    And this version we don't even need to run.