Task: Zebra puzzle



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();
    }
}






VB: Option Base 1
Public Enum attr
    Colour = 1
    Nationality
    Beverage
    Smoke
    Pet
End Enum
Public Enum Drinks_
    Beer = 1
    Coffee
    Milk
    Tea
    Water
End Enum
Public Enum nations
    Danish = 1
    English
    German
    Norwegian
    Swedish
End Enum
Public Enum colors
    Blue = 1
    Green
    Red
    White
    Yellow
End Enum
Public Enum tobaccos
    Blend = 1
    BlueMaster
    Dunhill
    PallMall
    Prince
End Enum
Public Enum animals
    Bird = 1
    Cat
    Dog
    Horse
    Zebra
End Enum
Public permutation As New Collection
Public perm(5) As Variant
Const factorial5 = 120
Public Colours As Variant, Nationalities As Variant, Drinks As Variant, Smokes As Variant, Pets As Variant

Private Sub generate(n As Integer, A As Variant)
    If n = 1 Then
        permutation.Add A
    Else
        For i = 1 To n
            generate n - 1, A
            If n Mod 2 = 0 Then
                tmp = A(i)
                A(i) = A(n)
                A(n) = tmp
            Else
                tmp = A(1)
                A(1) = A(n)
                A(n) = tmp
            End If
        Next i
    End If
End Sub

Function house(i As Integer, name As Variant) As Integer
    Dim x As Integer
    For x = 1 To 5
        If perm(i)(x) = name Then
            house = x
            Exit For
        End If
    Next x
End Function
 
Function left_of(h1 As Integer, h2 As Integer) As Boolean
    left_of = (h1 - h2) = -1
End Function
 
Function next_to(h1 As Integer, h2 As Integer) As Boolean
    next_to = Abs(h1 - h2) = 1
End Function
 
Private Sub print_house(i As Integer)
    Debug.Print i & ": "; Colours(perm(Colour)(i)), Nationalities(perm(Nationality)(i)), _
        Drinks(perm(Beverage)(i)), Smokes(perm(Smoke)(i)), Pets(perm(Pet)(i))
End Sub
Public Sub Zebra_puzzle()
    Colours = [{"blue","green","red","white","yellow"}]
    Nationalities = [{"Dane","English","German","Norwegian","Swede"}]
    Drinks = [{"beer","coffee","milk","tea","water"}]
    Smokes = [{"Blend","Blue Master","Dunhill","Pall Mall","Prince"}]
    Pets = [{"birds","cats","dog","horse","zebra"}]
    Dim solperms As New Collection
    Dim solutions As Integer
    Dim b(5) As Integer, i As Integer
    For i = 1 To 5: b(i) = i: Next i
    
    generate 5, b
    For c = 1 To factorial5
        perm(Colour) = permutation(c)
        
        If left_of(house(Colour, Green), house(Colour, White)) Then
            For n = 1 To factorial5
                perm(Nationality) = permutation(n)
                
                
                
                If house(Nationality, Norwegian) = 1 _
                    And house(Nationality, English) = house(Colour, Red) _
                    And next_to(house(Nationality, Norwegian), house(Colour, Blue)) Then
                    For d = 1 To factorial5
                        perm(Beverage) = permutation(d)
                        
                        
                        
                        If house(Nationality, Danish) = house(Beverage, Tea) _
                            And house(Beverage, Coffee) = house(Colour, Green) _
                            And house(Beverage, Milk) = 3 Then
                            For s = 1 To factorial5
                                perm(Smoke) = permutation(s)
                                
                                
                                
                                
                                If house(Colour, Yellow) = house(Smoke, Dunhill) _
                                    And house(Nationality, German) = house(Smoke, Prince) _
                                    And house(Smoke, BlueMaster) = house(Beverage, Beer) _
                                    And next_to(house(Beverage, Water), house(Smoke, Blend)) Then
                                    For p = 1 To factorial5
                                        perm(Pet) = permutation(p)
                                        
                                        
                                        
                                        
                                        If house(Nationality, Swedish) = house(Pet, Dog) _
                                            And house(Smoke, PallMall) = house(Pet, Bird) _
                                            And next_to(house(Smoke, Blend), house(Pet, Cat)) _
                                            And next_to(house(Pet, Horse), house(Smoke, Dunhill)) Then
                                            For i = 1 To 5
                                                print_house i
                                            Next i
                                            Debug.Print
                                            solutions = solutions + 1
                                            solperms.Add perm
                                        End If
                                    Next p
                                End If
                            Next s
                        End If
                    Next d
                End If
            Next n
        End If
    Next c
    Debug.Print Format(solutions, "@"); " solution" & IIf(solutions > 1, "s", "") & " found"
    For i = 1 To solperms.Count
        For j = 1 To 5
            perm(j) = solperms(i)(j)
        Next j
        Debug.Print "The " & Nationalities(perm(Nationality)(house(Pet, Zebra))) & " owns the Zebra"
    Next i
End Sub