Task: Zebra puzzle




Go: package main

import (
        "fmt"
        "log"
        "strings"
)



type HouseSet [5]*House
type House struct {
        n Nationality
        c Colour
        a Animal
        d Drink
        s Smoke
}
type Nationality int8
type Colour int8
type Animal int8
type Drink int8
type Smoke int8



const (
        English Nationality = iota
        Swede
        Dane
        Norwegian
        German
)
const (
        Red Colour = iota
        Green
        White
        Yellow
        Blue
)
const (
        Dog Animal = iota
        Birds
        Cats
        Horse
        Zebra
)
const (
        Tea Drink = iota
        Coffee
        Milk
        Beer
        Water
)
const (
        PallMall Smoke = iota
        Dunhill
        Blend
        BlueMaster
        Prince
)



var nationalities = [...]string{"English", "Swede", "Dane", "Norwegian", "German"}
var colours = [...]string{"red", "green", "white", "yellow", "blue"}
var animals = [...]string{"dog", "birds", "cats", "horse", "zebra"}
var drinks = [...]string{"tea", "coffee", "milk", "beer", "water"}
var smokes = [...]string{"Pall Mall", "Dunhill", "Blend", "Blue Master", "Prince"}

func (n Nationality) String() string { return nationalities[n] }
func (c Colour) String() string      { return colours[c] }
func (a Animal) String() string      { return animals[a] }
func (d Drink) String() string       { return drinks[d] }
func (s Smoke) String() string       { return smokes[s] }
func (h House) String() string {
        return fmt.Sprintf("%-9s  %-6s  %-5s  %-6s  %s", h.n, h.c, h.a, h.d, h.s)
}
func (hs HouseSet) String() string {
        lines := make([]string, 0, len(hs))
        for i, h := range hs {
                s := fmt.Sprintf("%d  %s", i, h)
                lines = append(lines, s)
        }
        return strings.Join(lines, "\n")
}



func simpleBruteForce() (int, HouseSet) {
        var v []House
        for n := range nationalities {
                for c := range colours {
                        for a := range animals {
                                for d := range drinks {
                                        for s := range smokes {
                                                h := House{
                                                        n: Nationality(n),
                                                        c: Colour(c),
                                                        a: Animal(a),
                                                        d: Drink(d),
                                                        s: Smoke(s),
                                                }
                                                if !h.Valid() {
                                                        continue
                                                }
                                                v = append(v, h)
                                        }
                                }
                        }
                }
        }
        n := len(v)
        log.Println("Generated", n, "valid houses")

        combos := 0
        first := 0
        valid := 0
        var validSet HouseSet
        for a := 0; a < n; a++ {
                if v[a].n != Norwegian { 
                        continue
                }
                for b := 0; b < n; b++ {
                        if b == a {
                                continue
                        }
                        if v[b].anyDups(&v[a]) {
                                continue
                        }
                        for c := 0; c < n; c++ {
                                if c == b || c == a {
                                        continue
                                }
                                if v[c].d != Milk { 
                                        continue
                                }
                                if v[c].anyDups(&v[b], &v[a]) {
                                        continue
                                }
                                for d := 0; d < n; d++ {
                                        if d == c || d == b || d == a {
                                                continue
                                        }
                                        if v[d].anyDups(&v[c], &v[b], &v[a]) {
                                                continue
                                        }
                                        for e := 0; e < n; e++ {
                                                if e == d || e == c || e == b || e == a {
                                                        continue
                                                }
                                                if v[e].anyDups(&v[d], &v[c], &v[b], &v[a]) {
                                                        continue
                                                }
                                                combos++
                                                set := HouseSet{&v[a], &v[b], &v[c], &v[d], &v[e]}
                                                if set.Valid() {
                                                        valid++
                                                        if valid == 1 {
                                                                first = combos
                                                        }
                                                        validSet = set
                                                        
                                                }
                                        }
                                }
                        }
                }
        }
        log.Println("Tested", first, "different combinations of valid houses before finding solution")
        log.Println("Tested", combos, "different combinations of valid houses in total")
        return valid, validSet
}


func (h *House) anyDups(list ...*House) bool {
        for _, b := range list {
                if h.n == b.n || h.c == b.c || h.a == b.a || h.d == b.d || h.s == b.s {
                        return true
                }
        }
        return false
}

func (h *House) Valid() bool {
        
        if h.n == English && h.c != Red || h.n != English && h.c == Red {
                return false
        }
        
        if h.n == Swede && h.a != Dog || h.n != Swede && h.a == Dog {
                return false
        }
        
        if h.n == Dane && h.d != Tea || h.n != Dane && h.d == Tea {
                return false
        }
        
        if h.c == Green && h.d != Coffee || h.c != Green && h.d == Coffee {
                return false
        }
        
        if h.a == Birds && h.s != PallMall || h.a != Birds && h.s == PallMall {
                return false
        }
        
        if h.c == Yellow && h.s != Dunhill || h.c != Yellow && h.s == Dunhill {
                return false
        }
        
        if h.a == Cats && h.s == Blend {
                return false
        }
        
        if h.a == Horse && h.s == Dunhill {
                return false
        }
        
        if h.d == Beer && h.s != BlueMaster || h.d != Beer && h.s == BlueMaster {
                return false
        }
        
        if h.n == German && h.s != Prince || h.n != German && h.s == Prince {
                return false
        }
        
        if h.n == Norwegian && h.c == Blue {
                return false
        }
        
        if h.d == Water && h.s == Blend {
                return false
        }
        return true
}

func (hs *HouseSet) Valid() bool {
        ni := make(map[Nationality]int, 5)
        ci := make(map[Colour]int, 5)
        ai := make(map[Animal]int, 5)
        di := make(map[Drink]int, 5)
        si := make(map[Smoke]int, 5)
        for i, h := range hs {
                ni[h.n] = i
                ci[h.c] = i
                ai[h.a] = i
                di[h.d] = i
                si[h.s] = i
        }
        
        if ci[Green]+1 != ci[White] {
                return false
        }
        
        if dist(ai[Cats], si[Blend]) != 1 {
                return false
        }
        
        if dist(ai[Horse], si[Dunhill]) != 1 {
                return false
        }
        
        if dist(ni[Norwegian], ci[Blue]) != 1 {
                return false
        }
        
        if dist(di[Water], si[Blend]) != 1 {
                return false
        }

        
        if hs[2].d != Milk {
                return false
        }
        
        if hs[0].n != Norwegian {
                return false
        }
        return true
}

func dist(a, b int) int {
        if a > b {
                return a - b
        }
        return b - a
}

func main() {
        log.SetFlags(0)
        n, sol := simpleBruteForce()
        fmt.Println(n, "solution found")
        fmt.Println(sol)
}





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