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