rec.puzzles Archive

How can I solve logic puzzles (e.g., as published by Dell) automatically?

logic/dell

Problem

How can I solve logic puzzles (e.g., as published by Dell) automatically?
Show solution
/*
Who Owns the Zerba?

On a city block there are five houses in a row, numbered from left
to right, each of a different color and inhabited by men of different
nationalities, with different pets, drinks and cigarettes. You are given
the following clues:

1.  The Englishman lives in the red house. 
2.  The Spaniard owns the dog. 
3.  Coffee is drunk in the green house. 
4.  The Ukrainian drinks tea. 
5.  The green house is immediately to the right of the ivory house. 
6.  The Old Gold smoker owns snails. 
7.  Kools are smoked in the yellow house. 
8.  Milk is drunk in house #3. 
9.  The Norwegian lives in house #1. 
10. The man who smokes Chesterfields lives in the house next to the man
    with the fox.
11. Kools are smoked in the house next to the house where the horse is kept. 
12. The Lucky Strike smoker drinks orange juice. 
13. The Japanese smokes Parliaments. 
14. The Norwegian lives next to the blue house. 

Who drinks water? And who owns the zebra? 

This puzzle was in the International Edition of Life Magazine in 1962. 

*/
#include        <setjmp.h>

#define        EITHER                if (S[1] = S[0], ! setjmp((S++)->jb)) {
#define        OR                } else EITHER
#define        REJECT                longjmp((--S)->jb, 1)
#define        END_EITHER        } else REJECT;

/* values in tmat: */
#define        T_UNK        0
#define        T_YES        1
#define        T_NO        2

#define        Val(t1,t2)        (S->tmat[t1][t2])
#define        CLASS(x)                \
                (((x) / NUM_ITEM) * NUM_ITEM)
#define        EVERY_TOKEN(x)                \
                (x = 0; x < TOT_TOKEN; x++)
#define        EVERY_ITEM(x, class)        \
                (x = CLASS(class); x < CLASS(class) + NUM_ITEM; x++)

#define        BEGIN                                                \
struct state {                                                \
        char        tmat[TOT_TOKEN][TOT_TOKEN];                \
        jmp_buf jb;                                        \
} States[100], *S = States;                                \
                                                        \
main()                                                        \
{                                                        \
        int        token;                                        \
                                                        \
        for EVERY_TOKEN(token)                                \
                yes(token, token);                        \
        EITHER

/* Here is the problem-specific data */
#define        NUM_ITEM        5
#define        NUM_CLASS        6
#define        TOT_TOKEN        (NUM_ITEM * NUM_CLASS)

#define        HOUSE_0                0
#define        HOUSE_1                1
#define        HOUSE_2                2
#define        HOUSE_3                3
#define        HOUSE_4                4

#define        ENGLISH                5
#define        SPANISH                6
#define        NORWEG                7
#define        UKRAIN                8
#define        JAPAN                9

#define        GREEN                10
#define        RED                11
#define        IVORY                12
#define        YELLOW                13
#define        BLUE                14

#define        COFFEE                15
#define        TEA                16
#define        MILK                17
#define        OJUICE                18
#define        WATER                19

#define        DOG                20
#define        SNAIL                21
#define        FOX                22
#define        HORSE                23
#define        ZEBRA                24

#define        OGOLD                25
#define        PLAYER                26
#define        CHESTER                27
#define        LSTRIKE                28
#define        PARLIA                29

char *names[] = {
        "HOUSE_0", "HOUSE_1", "HOUSE_2", "HOUSE_3", "HOUSE_4",
        "ENGLISH", "SPANISH", "NORWEG", "UKRAIN", "JAPAN",
        "GREEN", "RED", "IVORY", "YELLOW", "BLUE",
        "COFFEE", "TEA", "MILK", "OJUICE", "WATER",
        "DOG", "SNAIL", "FOX", "HORSE", "ZEBRA",
        "OGOLD", "PLAYER", "CHESTER", "LSTRIKE", "PARLIA",
};

        BEGIN

        yes(ENGLISH, RED);        /* Clue 1 */
        yes(SPANISH, DOG);        /* Clue 2 */
        yes(COFFEE, GREEN);        /* Clue 3 */
        yes(UKRAIN, TEA);        /* Clue 4 */

        EITHER                        /* Clue 5 */
                yes(IVORY, HOUSE_0);
                yes(GREEN, HOUSE_1);
        OR
                yes(IVORY, HOUSE_1);
                yes(GREEN, HOUSE_2);
        OR
                yes(IVORY, HOUSE_2);
                yes(GREEN, HOUSE_3);
        OR
                yes(IVORY, HOUSE_3);
                yes(GREEN, HOUSE_4);
        END_EITHER

        yes(OGOLD, SNAIL);        /* Clue 6 */
        yes(PLAYER, YELLOW);        /* Clue 7 */
        yes(MILK, HOUSE_2);        /* Clue 8 */
        yes(NORWEG, HOUSE_0);        /* Clue 9 */

        EITHER                        /* Clue 10 */
                yes(CHESTER, HOUSE_0);
                yes(FOX, HOUSE_1);
        OR
                yes(CHESTER, HOUSE_4);
                yes(FOX, HOUSE_3);
        OR
                yes(CHESTER, HOUSE_1);
                EITHER        yes(FOX, HOUSE_0);
                OR        yes(FOX, HOUSE_2);
                END_EITHER
        OR
                yes(CHESTER, HOUSE_2);
                EITHER        yes(FOX, HOUSE_1);
                OR        yes(FOX, HOUSE_3);
                END_EITHER
        OR
                yes(CHESTER, HOUSE_3);
                EITHER        yes(FOX, HOUSE_2);
                OR        yes(FOX, HOUSE_4);
                END_EITHER
        END_EITHER

        EITHER                        /* Clue 11 */
                yes(PLAYER, HOUSE_0);
                yes(HORSE, HOUSE_1);
        OR
                yes(PLAYER, HOUSE_4);
                yes(HORSE, HOUSE_3);
        OR
                yes(PLAYER, HOUSE_1);
                EITHER        yes(HORSE, HOUSE_0);
                OR        yes(HORSE, HOUSE_2);
                END_EITHER
        OR
                yes(PLAYER, HOUSE_2);
                EITHER        yes(HORSE, HOUSE_1);
                OR        yes(HORSE, HOUSE_3);
                END_EITHER
        OR
                yes(PLAYER, HOUSE_3);
                EITHER        yes(HORSE, HOUSE_2);
                OR        yes(HORSE, HOUSE_4);
                END_EITHER
        END_EITHER

        yes(LSTRIKE, OJUICE);        /* Clue 12 */
        yes(JAPAN, PARLIA);        /* Clue 13 */

        EITHER                        /* Clue 14 */
                yes(NORWEG, HOUSE_0);
                yes(BLUE, HOUSE_1);
        OR
                yes(NORWEG, HOUSE_4);
                yes(BLUE, HOUSE_3);
        OR
                yes(NORWEG, HOUSE_1);
                EITHER        yes(BLUE, HOUSE_0);
                OR        yes(BLUE, HOUSE_2);
                END_EITHER
        OR
                yes(NORWEG, HOUSE_2);
                EITHER        yes(BLUE, HOUSE_1);
                OR        yes(BLUE, HOUSE_3);
                END_EITHER
        OR
                yes(NORWEG, HOUSE_3);
                EITHER        yes(BLUE, HOUSE_2);
                OR        yes(BLUE, HOUSE_4);
                END_EITHER
        END_EITHER

/* End of problem-specific data */

                solveit();
        OR
                printf("All solutions found\n");
                exit(0);
        END_EITHER
}

no(a1, a2)
{
        int        non1, non2, token;

        if (Val(a1, a2) == T_YES)
                REJECT;
        else if (Val(a1, a2) == T_UNK) {
                Val(a1, a2) = T_NO;
                no(a2, a1);
                non1 = non2 = -1;

                for EVERY_ITEM(token, a1)
                        if (Val(token, a2) != T_NO)
                                if (non1 == -1)
                                        non1 = token;
                                else
                                        break;
                if (non1 == -1)
                        REJECT;
                else if (token == CLASS(a1) + NUM_ITEM)
                        yes(non1, a2);

                for EVERY_TOKEN(token)
                        if (Val(token, a1) == T_YES)
                                no(a2, token);
        }
}

yes(a1, a2)
{
        int        token;

        if (Val(a1, a2) == T_NO)
                REJECT;
        else if (Val(a1, a2) == T_UNK) {
                Val(a1, a2) = T_YES;
                yes(a2, a1);
                for EVERY_ITEM(token, a1)
                        if (token != a1)
                                no(token, a2);
                for EVERY_TOKEN(token)
                        if (Val(token, a1) == T_YES)
                                yes(a2, token);
                        else if (Val(token, a1) == T_NO)
                                no(a2, token);
        }
}

solveit()
{
        int        token, tok2;

        for EVERY_TOKEN(token)
                for (tok2 = token; tok2 < TOT_TOKEN; tok2++)
                        if (Val(token, tok2) == T_UNK) {
                                EITHER
                                        yes(token, tok2);
                                OR
                                        no(token, tok2);
                                END_EITHER;
                                return solveit();
                        }
        printf("Solution:\n");
        for EVERY_ITEM(token, 0) {
                for (tok2 = NUM_ITEM; tok2 < TOT_TOKEN; tok2++)
                        if (Val(token, tok2) == T_YES)
                                printf("\t%s %s\n",names[token],names[tok2]);
                printf("\n");
        }
        REJECT;
}

---
james@crc.ricoh.com (James Allen)