:- module puzzle.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
:- implementation.
:- import_module list, string, solutions.

:- type races ---> english; japanese; spanish.
:- type colors ---> red; blue; green.
:- type pets ---> jaguar; snail; zebra.
:- type house
    --->    house(
                race :: races,
                color :: colors,
                pet :: pets
            ).

:- pred distinct(house::in, house::in) is semidet.
distinct(house(R1, C1, P1), house(R2, C2, P2)) :-
    not (R1 = R2 ; C1 = C2 ; P1 = P2).

:- pred row(list(house)::out) is nondet.
row([A, B, C]) :-
    house(A), house(B), house(C),

    A^pet = snail       <=> B^race = japanese,
    B^pet = snail       <=> C^race = japanese,

    C^color = blue      <=> B^pet = snail,
    B^color = blue      <=> A^pet = snail,

    not A^race = japanese,
    not C^pet = snail,

    distinct(A, B), distinct(B, C), distinct(A, C).

:- pred house(house::out) is nondet.
house(house(R, H, P)) :-
    race(R), color(H), pet(P),

    R = english <=> H = red,
    R = spanish <=> P = jaguar,

    not (R = japanese, P = snail),
    not (P = snail, H = blue).

main(!IO) :-
    solutions(row, Solutions),
    ( if Solutions = [] then
        io.write_string("Impossible puzzle.\n", !IO)
    else
        foldl((pred(L::in, !.IO::di, !:IO::uo) is det :-
            io.print(L, !IO),
            io.nl(!IO)), Solutions, !IO)
    ).

:- pred race(races::out) is multi.
race(english).
race(japanese).
race(spanish).

:- pred color(colors).
:- mode color(out) is multi.
:- mode color(in) is det.
color(red).
color(blue).
color(green).

:- pred pet(pets::out) is multi.
pet(jaguar).
pet(snail).
pet(zebra).