% Solution to the Imaginary Friends logic puzzle.

% Facts
animal(grizzly).
animal(moose).
animal(seal).
animal(zebra).

child(joanne).
child(lou).
child(ralph).
child(winnie).

adventure(circus).
adventure(rockband).
adventure(spaceship).
adventure(train).

solve :-
    animal(JoanneAnimal), animal(LouAnimal), animal(RalphAnimal),
    animal(WinnieAnimal),
    all_different([JoanneAnimal, LouAnimal, RalphAnimal, WinnieAnimal]),
    
    adventure(JoanneAdventure), adventure(LouAdventure), adventure(RalphAdventure), adventure(WinnieAdventure),
    all_different([JoanneAdventure, LouAdventure, RalphAdventure, WinnieAdventure]),
    
    
            
    Stories = [ story(joanne, JoanneAnimal, JoanneAdventure),
              story(lou, LouAnimal, LouAdventure),
              story(ralph, RalphAnimal, RalphAdventure),
              story(winnie, WinnieAnimal, WinnieAdventure) ],

% 1. The seal (who isn't the creation of either Joanne or Lou ) neither rode to the moon in a spaceship, 
%    nor took a trip around the world on a magic train.
\+ member(story(_, seal, spaceship), Stories),
\+ member(story(_, seal, train), Stories),

% 2. Joanne's imaginary friend ( who isn't the grizzly bear) went to the circus.
   member(story(joanne, _, circus), Stories),
\+ member(story(joanne, grizzly, circus), Stories),

% 3. Winnie's imaginary friend is a zebra.
  member(story(winnie, zebra, _), Stories),

% 4. The grizzly bear didn't board the spaceship to the moon.
\+ member(story(_, grizzly, spaceship), Stories), 


tell(joanne, JoanneAnimal, JoanneAdventure),
    tell(lou, LouAnimal, LouAdventure),
    tell(ralph, RalphAnimal, RalphAdventure),
    tell(winnie, WinnieAnimal, WinnieAdventure).
    
tell(Child, Animal, Adventure) :- write(Child), write('s '), write('imaginary friend was a '), write(Animal),
    write(', and they went on a '), write(Adventure), write(' adventure'), write('.'), nl.
    
% If the head of the list is in the tail, fail.
all_different([Head|Tail]) :- member(Head, Tail), !, fail.
% Otherwise, check the tail of the list.
all_different([_|Tail]) :- all_different(Tail).
% If there's only one thing in the list, it's fine.
all_different([_]).