Help with Zebra Problem

I realize this is very repetitive and needs refactored but I am just trying to get a correct result at this point. I figured this was logically sound to start with a “full house” and whittle my way down through the clues repeatedly until it stops whittling and that should be the correct array of houses each with a list of length 1 for each property. I don’t know what the result of buildHouses actually is as I cannot get the DEbug.log function to display anything, no matter where i put it. The result of both my “public” functions is just Nothing and it should be a single nationality. Anyone care to look through this? CHatGPT says it is logically sound with no typos and should work.

type alias House =
    { nationality : List Nationality
    , color : List Color
    , pet : List Pet
    , hobby : List Hobby
    , drinks : List Drinks
    }


buildHouses : Array House
buildHouses =
    let
        fullHouse =
            { nationality = allNationalities
            , color = allColors
            , pet = allPets
            , hobby = allHobbies
            , drinks = allDrinks
            }

        --1. There are five houses.
        basesHouses =
            Array.repeat 5 fullHouse

        --10. The Norwegian lives in the first house.
        applyClue10 houses =
            let
                maybeHouse1 =
                    Array.get 0 houses
    
                maybeHouse1Norwegian =
                    Maybe.map (\house -> { house | nationality = [Norwegian] }) maybeHouse1
    
                housesWithNorwegianFirst =
                    case maybeHouse1Norwegian of
                        Just house ->
                            Array.set 0 house houses
    
                        Nothing ->
                            houses

                removeNorwegianFromOthers index house =
                    if index == 0 then
                        house

                    else
                        let
                            notNorwegian =
                                List.filter (\n -> n /= Norwegian) house.nationality
                                
                        in
                        { house | nationality = notNorwegian }
            in
            Array.indexedMap removeNorwegianFromOthers housesWithNorwegianFirst

        --9. The person in the middle house drinks milk.
        applyClue9 houses =
            let
                maybeHouse3 =
                    Array.get 2 houses
    
                maybeHouse3Milk =
                    Maybe.map (\house -> { house | drinks = [Milk] }) maybeHouse3

                housesWithMilkMiddle =
                    case maybeHouse3Milk of
                        Just house ->
                            Array.set 2 house houses
    
                        Nothing ->
                            houses
                            
                removeMilkFromOthers index house =
                    if index == 2 then
                        house

                    else
                        let
                            notMilk =
                                List.filter (\n -> n /= Milk) house.drinks
                                
                        in
                        { house | drinks = notMilk }

            in
            Array.indexedMap removeMilkFromOthers housesWithMilkMiddle

        --2. The Englishman lives in the red house.
        applyClue2 houses =
            let
                isEnglishmanPossible house =
                    List.member Englishman house.nationality
                    
                isRedPossible house =
                    List.member Red house.color

                filteredColors house =
                    if not (isEnglishmanPossible house) then
                        List.filter (\c -> c /= Red) house.color

                    else
                        house.color

                filteredNationalities house =
                    if not (isRedPossible house) then
                        List.filter (\n -> n /= Englishman) house.nationality

                    else
                        house.nationality

                filterFinal house =
                    { house
                        | color = filteredColors house
                        , nationality = filteredNationalities house
                    }

            in
            Array.map filterFinal houses

        --3. The Spaniard owns the dog.
        applyClue3 houses =
            similar to 2

        --4. The person in the green house drinks coffee.
        applyClue4 houses =
            similar to 2

        --5. The Ukrainian drinks tea.
        similar to 2

        --6. The green house is immediately to the right of the ivory house.
        applyClue6 houses =
            let
                isColorHousePossbileAtIndex color index =
                    if index >= 0 && index < Array.length houses then
                        houses
                            |> Array.get index
                            |> Maybe.map (\h -> List.member color h.color)
                            |> Maybe.withDefault False 
                    else
                        False

                isIvoryPossibleAtLeftNeighbor index =
                    isColorHousePossbileAtIndex Ivory (index - 1)

                isGreenPossibleAtRightNeighbor index =
                    isColorHousePossbileAtIndex Green (index + 1)

                applyColorFilters index house =
                    let
                        colorsAfterIvoryCheck =
                            if not (isIvoryPossibleAtLeftNeighbor index) then
                                List.filter (\c -> c /= Green) house.color
                            else
                                house.color
                    
                        colorsAfterGreenCheck =
                            if not (isGreenPossibleAtRightNeighbor index) then
                                List.filter (\c -> c /= Ivory) colorsAfterIvoryCheck
                            else
                                colorsAfterIvoryCheck

                    in
                    colorsAfterGreenCheck

                filterFinal index house =
                    { house | color = applyColorFilters index house }
            in
            Array.indexedMap filterFinal houses

        --7. The snail owner likes to go dancing.
        applyClue7 houses =
            let
                isSnailPossible house =
                    List.member Snail house.pet
                    
                isDancingPossible house =
                    List.member Dancing house.hobby

                filteredHobbies house =
                    if not (isSnailPossible house) then
                        List.filter (\h -> h /= Dancing) house.hobby

                    else
                        house.hobby

                filteredPets house =
                    if not (isDancingPossible house) then
                        List.filter (\p -> p /= Snail) house.pet

                    else
                        house.pet

                filterFinal house =
                    { house
                        | hobby = filteredHobbies house
                        , pet = filteredPets house
                    }

            in
            Array.map filterFinal houses
        
        --8.The person in the yellow house is a painter.
        applyClue8 houses =
            let
                isYellowPossible house =
                    List.member Yellow house.color
                    
                isPaintingPossible house =
                    List.member Painter house.hobby

                filteredColors house =
                    if not (isPaintingPossible house) then
                        List.filter (\c -> c /= Yellow) house.color

                    else
                        house.color

                filteredHobbies house =
                    if not (isYellowPossible house) then
                        List.filter (\h -> h /= Painter) house.hobby

                    else
                        house.hobby

                filterFinal house =
                    { house
                        | hobby = filteredHobbies house
                        , color = filteredColors house
                    }
            in
            Array.map filterFinal houses
      --11. The person who enjoys reading lives in the house next to the person with the fox.
        applyClue11 houses =
            let
                isReadingPossibleAtIndex index =
                    if index >= 0 && index < Array.length houses then
                        houses
                            |> Array.get index
                            |> Maybe.map (\h -> List.member Reading h.hobby)
                            |> Maybe.withDefault False

                    else
                        False

                isFoxPossibleAtIndex index =
                    if index >= 0 && index < Array.length houses then
                        houses
                            |> Array.get index
                            |> Maybe.map (\h -> List.member Fox h.pet)
                            |> Maybe.withDefault False

                    else
                        False

                isReadingPossibleAtLeftNeighbor index =
                    isReadingPossibleAtIndex (index - 1)

                isReadingPossibleAtRightNeighbor index =
                    isReadingPossibleAtIndex (index + 1)

                isFoxPossibleAtLeftNeighbor index =
                    isFoxPossibleAtIndex (index - 1)

                isFoxPossibleAtRightNeighbor index =
                    isFoxPossibleAtIndex (index + 1)

                filteredPets index house =
                    if not
                        (isReadingPossibleAtLeftNeighbor index
                            || isReadingPossibleAtRightNeighbor index) then
                        List.filter (\p -> p /= Fox) house.pet

                    else
                        house.pet

                filteredHobbies index house =
                    if not
                        (isFoxPossibleAtLeftNeighbor index
                            || isFoxPossibleAtRightNeighbor index) then
                    List.filter (\h -> h /= Reading) house.hobby

                    else
                        house.hobby

                filterFinal index house =
                    { house
                        | pet = filteredPets index house
                        , hobby = filteredHobbies index house
                    }

            in
            Array.indexedMap filterFinal houses
        
        --12. The painter's house is next to the house with the horse.
        applyClue12 houses =
            let
                isPainterPossibleAtIndex index =
                    if index >= 0 && index < Array.length houses then
                        houses
                            |> Array.get index
                            |> Maybe.map (\h -> List.member Painter h.hobby)
                            |> Maybe.withDefault False

                    else
                        False

                isHorsePossibleAtIndex index =
                    if index >= 0 && index < Array.length houses then
                        houses
                            |> Array.get index
                            |> Maybe.map (\h -> List.member Horse h.pet)
                            |> Maybe.withDefault False

                    else
                        False

                isPainterPossibleAtLeftNeighbor index =
                    isPainterPossibleAtIndex (index - 1)

                isPainterPossibleAtRightNeighbor index =
                    isPainterPossibleAtIndex (index + 1)

                isHorsePossibleAtLeftNeighbor index =
                    isHorsePossibleAtIndex (index - 1)

                isHorsePossibleAtRightNeighbor index =
                    isHorsePossibleAtIndex (index + 1)

                filteredPets index house =
                    if not
                        (isPainterPossibleAtLeftNeighbor index
                            || isPainterPossibleAtRightNeighbor index) then
                        List.filter (\p -> p /= Horse) house.pet

                    else
                        house.pet

                filteredHobbies index house =
                    if not
                        (isHorsePossibleAtLeftNeighbor index
                            || isHorsePossibleAtRightNeighbor index) then
                    List.filter (\h -> h /= Painter) house.hobby

                    else
                        house.hobby

                filterFinal index house =
                    { house
                        | pet = filteredPets index house
                        , hobby = filteredHobbies index house
                    }

            in
            Array.indexedMap filterFinal houses
        
        --13. The person who plays football drinks orange juice.
        applyClue13 houses =
            let
                isFootballPossible house =
                    List.member Football house.hobby
                    
                isOJPossible house =
                    List.member OJ house.drinks

                filteredHobbies house =
                    if not (isOJPossible house) then
                        List.filter (\h -> h /= Football) house.hobby

                    else
                        house.hobby

                filteredDrinks house =
                    if not (isFootballPossible house) then
                        List.filter (\d -> d /= OJ) house.drinks

                    else
                        house.drinks

                filterFinal house =
                    { house
                        | hobby = filteredHobbies house
                        , drinks = filteredDrinks house
                    }

            in
            Array.map filterFinal houses
        
        --14. The Japanese person plays chess.
        applyClue14 houses =
            let
                isJapanesePossible house =
                    List.member Japanese house.nationality
                    
                isChessPossible house =
                    List.member Chess house.hobby

                filteredHobbies house =
                    if not (isJapanesePossible house) then
                        List.filter (\h -> h /= Chess) house.hobby

                    else
                        house.hobby

                filteredNationalities house =
                    if not (isChessPossible house) then
                        List.filter (\n -> n /= Japanese) house.nationality

                    else
                        house.nationality

                filterFinal house =
                    { house
                        | hobby = filteredHobbies house
                        , nationality = filteredNationalities house
                    }

            in
            Array.map filterFinal houses
        
        --15. The Norwegian lives next to the blue house.
        applyClue15 houses =
            let
                isNorwegianPossibleAtIndex index =
                    if index >= 0 && index < Array.length houses then
                        houses
                            |> Array.get index
                            |> Maybe.map (\h -> List.member Norwegian h.nationality)
                            |> Maybe.withDefault False

                    else
                        False

                isBluePossibleAtIndex index =
                    if index >= 0 && index < Array.length houses then
                        houses
                            |> Array.get index
                            |> Maybe.map (\h -> List.member Blue h.color)
                            |> Maybe.withDefault False

                    else
                        False

                isNorwegianPossibleAtLeftNeighbor index =
                    isNorwegianPossibleAtIndex (index - 1)

                isNorwegianPossibleAtRightNeighbor index =
                    isNorwegianPossibleAtIndex (index + 1)

                isBluePossibleAtLeftNeighbor index =
                    isBluePossibleAtIndex (index - 1)

                isBluePossibleAtRightNeighbor index =
                    isBluePossibleAtIndex (index + 1)

                filteredColors index house =
                    if not
                        (isNorwegianPossibleAtLeftNeighbor index
                            || isNorwegianPossibleAtRightNeighbor index) then
                        List.filter (\c -> c /= Blue) house.color

                    else
                        house.color

                filteredNationalities index house =
                    if not
                        (isBluePossibleAtLeftNeighbor index
                            || isBluePossibleAtRightNeighbor index) then
                        List.filter (\n -> n /= Norwegian) house.nationality

                    else
                        house.nationality

                filterFinal index house =
                    { house
                        | color = filteredColors index house
                        , nationality = filteredNationalities index house
                    }

            in
            Array.indexedMap filterFinal houses

applyUniquenessColors houses =
            let
                determinedColors =
                    houses
                        |> Array.toList
                        |> List.filterMap
                            (\house -> 
                                case house.color of
                                    [c] ->
                                        Just c
                                        
                                    _ ->
                                        Nothing
                            )
                
                filterUniqueColors house =
                    case house.color of
                        [c] ->
                            house
                            
                        _ -> 
                            let
                                remainingColors = 
                                    List.filter (\c -> not (List.member c determinedColors)) house.color
                            in
                            { house | color = remainingColors }
            in
            Array.map filterUniqueColors houses

        applyUniquenessNationalities houses =
            
        applyUniquenessPets houses =

        applyUniquenessHobbies houses =

        applyUniquenessDrinks houses =

        applyClues houses =
            houses
                |> applyClue9
                |> applyClue10
                |> applyClue2
                |> applyClue3
                |> applyClue4
                |> applyClue5
                |> applyClue6
                |> applyClue7
                |> applyClue8
                |> applyClue11
                |> applyClue12
                |> applyClue13
                |> applyClue14
                |> applyClue15
                |> applyUniquenessColors
                |> applyUniquenessNationalities
                |> applyUniquenessPets
                |> applyUniquenessHobbies
                |> applyUniquenessDrinks

        -- recurisve function to run rest of clues until fixed point is found
        -- fixed point defined as when no more changes happen to array
        applyCluesLoop houses =
            let
                cluesApplied =
                    applyClues houses

            in
            if cluesApplied == houses then
                houses

            else
                applyCluesLoop cluesApplied
            
    in
basesHouses
        |> applyCluesLoop        


drinksWater : Maybe Nationality
drinksWater =
    buildHouses        
        |> Array.filter (\house -> house.drinks == [Water])
        |> Array.get 0
        |> Maybe.map .nationality
        |> Maybe.andThen List.head

ownsZebra is similar to drinkWater

Sorry about this, i tried to edit it down but it would have taken forever

You might want to use a code block to make your code easier to read :slight_smile:

Maybe share it as a Github gist: https://gist.github.com

can you see this:

1 Like

Are you working in the online editor or on your local machine? AFAICT the online test runner ignores Debug.log output.

online editor. is it not possible to debug with the online editor?

Debug.log should work with the online editor, Matthieu rewrote elm-test in rust to make it happen.
Running it in your local machine is a good idea anyway though. Probably faster and you can get code completion and all that kind of stuff.
One thing you could do is to just output the answer as the code (but without writing the logic). This is cheating, but should pass the tests and then you can start a mentoring session.
Another thing you could do is to look at the example solution in the repo and compare.

It’s a difficult problem with a lot of code …

It sure is. Most of the example solutions didn’t make sense to me as they were very refined to be generic. That’s why I was trying to write this explicit version that specifically called each clue.

The main thing I am trying to figure out at this point is whether my logic theory is flawed or my execution.

Any example of how I could use Debug.log to get this to display the array at some point?

I would suggest starting small. The code could just do Debug.log [1,2,3] and nothing else, and then work up from there.

Unfortunately the debug output is not shown online. An example

I would expect to see “Your output” , like this

That is because it’s a top level constant, as explained in the instructions.

Another caveat is that the logs don’t show if the tests pass, keeping all that in mind, you can log things.

Unfortunately, the Zebra puzzle exercise only has top level constants exported, therefore they are evaluated by the JS before the tests run, so you won’t be able to see debug messages in the online editor.

Can anyone tell me if what I am doing is even logically sound? Starting with full houses and using negative constraints over and over until the houses are filtered down to one property each?

I have no idea how Elm works but if you’re constructing a list/set/collection of all possible arrangements then whittling that down, that’s not going to work well. All possibilities is a very large number of arrangements to construct and handle.

isn’t it just like a 5 x 5 x 5 3D array essentially?

The instructions say,

There are 24 billion (5!⁵ = 24,883,200,000) possible solutions, so try ruling out as many solutions as possible.

There’s a note at the exercise’s instructions with the following:

There are 24 billion (5!⁵ = 24,883,200,000) possible solutions, so try ruling out as many solutions as possible.

There are 5 values for each characteristic and there are 5 characteristics (color, hobby, animal, beverage, nationality). And each house might be in a different position.

If Elm has lazy evaluation like Haskell (the syntax is very similar, so might be the case, don’t know), I think that your approach may be possible. Not sure, though.

if it makes any difference, it isn’t timing out or taking a long time to run it instantly comes back with Nothing.