Skip to content

Instantly share code, notes, and snippets.

@atifaziz
Created April 16, 2023 15:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atifaziz/71ee1a21da7259be24c019c45a82525e to your computer and use it in GitHub Desktop.
Save atifaziz/71ee1a21da7259be24c019c45a82525e to your computer and use it in GitHub Desktop.
Zebra Puzzle solution using LINQ
#nullable enable
/* Zebra Puzzle: https://en.wikipedia.org/wiki/Zebra_Puzzle
*
> The following version of the puzzle appeared in Life International in 1962:
>
> 1. There are five houses.
> 2. The Englishman lives in the red house.
> 3. The Spaniard owns the dog.
> 4. Coffee is drunk in the green house.
> 5. The Ukrainian drinks tea.
> 6. The green house is immediately to the right of the ivory house.
> 7. The Old Gold smoker owns snails.
> 8. Kools are smoked in the yellow house.
> 9. Milk is drunk in the middle house.
> 10. The Norwegian lives in the first house.
> 11. The man who smokes Chesterfields lives in the house next to the man with the fox.
> 12. Kools are smoked in the house next to the house where the horse is kept.
> 13. The Lucky Strike smoker drinks orange juice.
> 14. The Japanese smokes Parliaments.
> 15. The Norwegian lives next to the blue house.
>
> Now, who drinks water? Who owns the zebra?
*/
using System;
using System.Collections.Generic;
using System.Linq;
using static Color;
using static Nationality;
using static Pet;
using static Drink;
using static Smoke;
var houses =
// Inspiration & credit:
// https://rosettacode.org/wiki/Zebra_puzzle#%22Manual%22_solution_(Combining_Houses)
from hs in new[]
{
// 1. There are five houses.
from c in Enum.GetValues<Color>()
from n in Enum.GetValues<Nationality>()
from d in Enum.GetValues<Drink>()
from s in Enum.GetValues<Smoke>()
from p in Enum.GetValues<Pet>()
select new House
{
Color = c,
Nationality = n,
Drink = d,
Smoke = s,
Pet = p
}
into e
// 2. The Englishman lives in the red house.
where e.Color is Red == e.Nationality is Englishman
// 3. The Spaniard owns the dog.
where e.Nationality is Spaniard == e.Pet is Dog
// 4. Coffee is drunk in the green house.
where e.Drink is Coffee == e.Color is Green
// 5. The Ukrainian drinks tea.
where e.Nationality is Ukranian == e.Drink is Tea
// 7. The Old Gold smoker owns snails.
where e.Smoke is OldGold == e.Pet is Snails
// 8. Kools are smoked in the yellow house.
where e.Smoke is Kools == e.Color is Yellow
// 13. The Lucky Strike smoker drinks orange juice.
where e.Smoke is LuckyStrike == e.Drink is OrangeJuice
// 14. The Japanese smokes Parliaments.
where e.Nationality is Japanese == e.Smoke is Parliaments
select e
}
// Materialize before proceeding since the above sequence will be
// reiterated multiple times.
select hs.ToArray() into hs
from h1 in hs
// 10. The Norwegian lives in the first house.
where h1 is { Nationality: Norwegian }
from h3 in hs
// 9. Milk is drunk in the middle house.
where h3 is { Drink: Milk } && h3 ^ h1
from h2 in hs
// 15. The Norwegian lives next to the blue house.
//
// By logical deduction, since the Norwegian lives in the first
// house, the second house must be blue.
where h2 is { Color: Blue } && h2 ^ h1 && h2 ^ h3
from h4 in hs
where h4 ^ h1 && h4 ^ h2 && h4 ^ h3
from h5 in hs
where h5 ^ h1 && h5 ^ h2 && h5 ^ h3 && h5 ^ h4
select new[] { h1, h2, h3, h4, h5 }
into hs
// 6. The green house is immediately to the right of the ivory house.
where hs.Pairwise()
.Any(pair => pair is ({ Color: Ivory }, { Color: Green }))
// 11. The man who smokes Chesterfields lives in the house next to the man with the fox.
where hs.Pairwise()
.Any(pair => pair is ({ Smoke: Chesterfields }, { Pet: Fox })
or ({ Pet: Fox }, { Smoke: Chesterfields }))
// 12. Kools are smoked in the house next to the house where the horse is kept.
where hs.Pairwise()
.Any(pair => pair is ({ Smoke: Kools }, { Pet: Horse })
or ({ Pet: Horse }, { Smoke: Kools }))
from h in hs
select h;
houses = houses.ToArray();
Console.WriteLine("| # | Color | Nationality | Drink | Smoke | Pet |");
Console.WriteLine("|---|--------|-------------|-------------|---------------|--------|");
foreach (var (h, i) in houses.Select((h, i) => (h, i + 1)))
Console.WriteLine($"| {i} | {h.Color,-6} | {h.Nationality,-11} | {h.Drink,-11} | {h.Smoke,-13} | {h.Pet,-6} |");
Console.WriteLine();
Console.WriteLine($"Who drinks water? {houses.Single(h => h.Drink is Water).Nationality}");
Console.WriteLine($"Who owns the zebra? {houses.Single(h => h.Pet is Zebra).Nationality}");
public enum Color { Red, Green, Ivory, Yellow, Blue }
public enum Nationality { Englishman, Spaniard, Ukranian, Japanese, Norwegian }
public enum Pet { Dog, Snails, Fox, Horse, Zebra }
public enum Drink { Coffee, Tea, Milk, OrangeJuice, Water }
public enum Smoke { OldGold, Kools, Chesterfields, LuckyStrike, Parliaments }
sealed record House
{
public required Color Color { get; init; }
public required Nationality Nationality { get; init; }
public required Drink Drink { get; init; }
public required Smoke Smoke { get; init; }
public required Pet Pet { get; init; }
public static bool operator^(House a, House b)
=> a.Color != b.Color
&& a.Nationality != b.Nationality
&& a.Drink != b.Drink
&& a.Smoke != b.Smoke
&& a.Pet != b.Pet;
}
static class Extensions
{
public static IEnumerable<(T, T)> Pairwise<T>(this IEnumerable<T> source)
{
using var e = source.GetEnumerator();
if (!e.MoveNext())
yield break;
T b;
for (var a = e.Current; e.MoveNext(); a = b)
yield return (a, b = e.Current);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment