Task: Zebra puzzle


C++: #include <stdio.h>
#include <string.h>

#define defenum(name, val0, val1, val2, val3, val4) \
    enum name { val0, val1, val2, val3, val4 }; \
    const char *name ## _str[] = { # val0, # val1, # val2, # val3, # val4 }

defenum( Attrib,    Color, Man, Drink, Animal, Smoke );
defenum( Colors,    Red, Green, White, Yellow, Blue );
defenum( Mans,      English, Swede, Dane, German, Norwegian );
defenum( Drinks,    Tea, Coffee, Milk, Beer, Water );
defenum( Animals,   Dog, Birds, Cats, Horse, Zebra );
defenum( Smokes,    PallMall, Dunhill, Blend, BlueMaster, Prince );

void printHouses(int ha[5][5]) {
    const char **attr_names[5] = {Colors_str, Mans_str, Drinks_str, Animals_str, Smokes_str};

    printf("%-10s", "House");
    for (const char *name : Attrib_str) printf("%-10s", name);
    printf("\n");

    for (int i = 0; i < 5; i++) {
        printf("%-10d", i);
        for (int j = 0; j < 5; j++) printf("%-10s", attr_names[j][ha[i][j]]);
        printf("\n");
    }
}

struct HouseNoRule {
    int houseno;
    Attrib a; int v;
} housenos[] = {
    {2, Drink, Milk},     
    {0, Man, Norwegian}   
};

struct AttrPairRule {
    Attrib a1; int v1;
    Attrib a2; int v2;

    bool invalid(int ha[5][5], int i) {
        return (ha[i][a1] >= 0 && ha[i][a2] >= 0) &&
               ((ha[i][a1] == v1 && ha[i][a2] != v2) ||
                (ha[i][a1] != v1 && ha[i][a2] == v2));
    }
} pairs[] = {
    {Man, English,      Color, Red},     
    {Man, Swede,        Animal, Dog},    
    {Man, Dane,         Drink, Tea},     
    {Color, Green,      Drink, Coffee},  
    {Smoke, PallMall,   Animal, Birds},  
    {Smoke, Dunhill,    Color, Yellow},  
    {Smoke, BlueMaster, Drink, Beer},    
    {Man, German,       Smoke, Prince}    
};

struct NextToRule {
    Attrib a1; int v1;
    Attrib a2; int v2;

    bool invalid(int ha[5][5], int i) {
        return (ha[i][a1] == v1) &&
               ((i == 0 && ha[i + 1][a2] >= 0 && ha[i + 1][a2] != v2) ||
                (i == 4 && ha[i - 1][a2] != v2) ||
                (ha[i + 1][a2] >= 0 && ha[i + 1][a2] != v2 && ha[i - 1][a2] != v2));
    }
} nexttos[] = {
    {Smoke, Blend,      Animal, Cats},    
    {Smoke, Dunhill,    Animal, Horse},   
    {Man, Norwegian,    Color, Blue},     
    {Smoke, Blend,      Drink, Water}     
};

struct LeftOfRule {
    Attrib a1; int v1;
    Attrib a2; int v2;

    bool invalid(int ha[5][5]) {
        return (ha[0][a2] == v2) || (ha[4][a1] == v1);
    }

    bool invalid(int ha[5][5], int i) {
        return ((i > 0 && ha[i][a1] >= 0) &&
                ((ha[i - 1][a1] == v1 && ha[i][a2] != v2) ||
                 (ha[i - 1][a1] != v1 && ha[i][a2] == v2)));
    }
} leftofs[] = {
    {Color, Green,  Color, White}     
};

bool invalid(int ha[5][5]) {
    for (auto &rule : leftofs) if (rule.invalid(ha)) return true;

    for (int i = 0; i < 5; i++) {
#define eval_rules(rules) for (auto &rule : rules) if (rule.invalid(ha, i)) return true;
        eval_rules(pairs);
        eval_rules(nexttos);
        eval_rules(leftofs);
    }
    return false;
}

void search(bool used[5][5], int ha[5][5], const int hno, const int attr) {
    int nexthno, nextattr;
    if (attr < 4) {
        nextattr = attr + 1;
        nexthno = hno;
    } else {
        nextattr = 0;
        nexthno = hno + 1;
    }

    if (ha[hno][attr] != -1) {
        search(used, ha, nexthno, nextattr);
    } else {
        for (int i = 0; i < 5; i++) {
            if (used[attr][i]) continue;
            used[attr][i] = true;
            ha[hno][attr] = i;

            if (!invalid(ha)) {
                if ((hno == 4) && (attr == 4)) {
                    printHouses(ha);
                } else {
                    search(used, ha, nexthno, nextattr);
                }
            }

            used[attr][i] = false;
        }
        ha[hno][attr] = -1;
    }
}

int main() {
    bool used[5][5] = {};
    int ha[5][5]; memset(ha, -1, sizeof(ha));

    for (auto &rule : housenos) {
        ha[rule.houseno][rule.a] = rule.v;
        used[rule.a][rule.v] = true;
    }

    search(used, ha, 0, 0);

    return 0;
}


C#: using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static System.Console;

public enum Colour { Red, Green, White, Yellow, Blue }
public enum Nationality { Englishman, Swede, Dane, Norwegian,German }
public enum Pet { Dog, Birds, Cats, Horse, Zebra }
public enum Drink { Coffee, Tea, Milk, Beer, Water }
public enum Smoke { PallMall, Dunhill, Blend, BlueMaster, Prince}

public static class ZebraPuzzle
{
    private static (Colour[] colours, Drink[] drinks, Smoke[] smokes, Pet[] pets, Nationality[] nations) _solved;

    static ZebraPuzzle()
    {
        var solve = from colours in Permute<Colour>()  
                    where (colours,Colour.White).IsRightOf(colours, Colour.Green) 
                    from nations in Permute<Nationality>()
                    where nations[0] == Nationality.Norwegian 
                    where (nations, Nationality.Englishman).IsSameIndex(colours, Colour.Red) 
                    where (nations,Nationality.Norwegian).IsNextTo(colours,Colour.Blue) 
                    from drinks in Permute<Drink>()
                    where drinks[2] == Drink.Milk 
                    where (drinks, Drink.Coffee).IsSameIndex(colours, Colour.Green) 
                    where (drinks, Drink.Tea).IsSameIndex(nations, Nationality.Dane) 
                    from pets in Permute<Pet>()
                    where (pets, Pet.Dog).IsSameIndex(nations, Nationality.Swede) 
                    from smokes in Permute<Smoke>()
                    where (smokes, Smoke.PallMall).IsSameIndex(pets, Pet.Birds) 
                    where (smokes, Smoke.Dunhill).IsSameIndex(colours, Colour.Yellow) 
                    where (smokes, Smoke.Blend).IsNextTo(pets, Pet.Cats) 
                    where (smokes, Smoke.Dunhill).IsNextTo(pets, Pet.Horse) 
                    where (smokes, Smoke.BlueMaster).IsSameIndex(drinks, Drink.Beer) 
                    where (smokes, Smoke.Prince).IsSameIndex(nations, Nationality.German) 
                    where (drinks,Drink.Water).IsNextTo(smokes,Smoke.Blend) 
                    select (colours, drinks, smokes, pets, nations);

        _solved = solve.First();
    }
    
    private static int IndexOf<T>(this T[] arr, T obj) => Array.IndexOf(arr, obj);

    private static bool IsRightOf<T, U>(this (T[] a, T v) right, U[] a, U v) => right.a.IndexOf(right.v) == a.IndexOf(v) + 1;

    private static bool IsSameIndex<T, U>(this (T[] a, T v)x, U[] a, U v) => x.a.IndexOf(x.v) == a.IndexOf(v);

    private static bool IsNextTo<T, U>(this (T[] a, T v)x, U[] a,  U v) => (x.a,x.v).IsRightOf(a, v) || (a,v).IsRightOf(x.a,x.v);

    
    public static IEnumerable<IEnumerable<T>> Permutations<T>(this IEnumerable<T> values)
    {
        if (values.Count() == 1)
            return values.ToSingleton();

        return values.SelectMany(v => Permutations(values.Except(v.ToSingleton())),(v, p) => p.Prepend(v));
    }

    public static IEnumerable<T[]> Permute<T>() => ToEnumerable<T>().Permutations().Select(p=>p.ToArray());

    private static IEnumerable<T> ToSingleton<T>(this T item){ yield return item; }

    private static IEnumerable<T> ToEnumerable<T>() => Enum.GetValues(typeof(T)).Cast<T>();

    public static new String ToString()
    {
        var sb = new StringBuilder();
        sb.AppendLine("House Colour Drink    Nationality Smokes     Pet");
        sb.AppendLine("───── ────── ──────── ─────────── ────────── ─────");
        var (colours, drinks, smokes, pets, nations) = _solved;
        for (var i = 0; i < 5; i++)
            sb.AppendLine($"{i+1,5} {colours[i],-6} {drinks[i],-8} {nations[i],-11} {smokes[i],-10} {pets[i],-10}");
        return sb.ToString();
    }

    public static void Main(string[] arguments)
    {
        var owner = _solved.nations[_solved.pets.IndexOf(Pet.Zebra)];
        WriteLine($"The zebra owner is {owner}");
        Write(ToString());
        Read();
    }
}