%%writefile raku-zebra-puzzle-002.raku use trace; my Hash @houses = (1 .. 5).map: { %(:num($_)) }; # 1 there are five houses my @facts = ( { :nat, :color }, # 2 The English man lives in the red house. { :nat, :pet }, # 3 The Swede has a dog. { :nat, :drink }, # 4 The Dane drinks tea. { :color, :Left-Of(:color) }, # 5 the green house is immediately to the left of the white house { :drink, :color }, # 6 They drink coffee in the green house. { :smoke, :pet }, # 7 The man who smokes Pall Mall has birds. { :color, :smoke }, # 8 In the yellow house they smoke Dunhill. { :num(3), :drink }, # 9 In the middle house they drink milk. { :num(1), :nat }, # 10 The Norwegian lives in the first house. { :smoke, :Next-To(:pet) }, # 11 The man who smokes Blend lives in the house next to the house with cats. { :pet, :Next-To(:smoke) }, # 12 In a house next to the house where they have a horse, they smoke Dunhill. { :smoke, :drink }, # 13 The man who smokes Blue Master drinks beer. { :nat, :smoke }, # 14 The German smokes Prince. { :nat, :Next-To(:color) }, # 15 The Norwegian lives next to the blue house. { :drink, :Next-To(:smoke) }, # 16 They drink water in a house next to the house where they smoke Blend. { :pet }, # who owns this? ); sub MAIN { for gather solve(@houses, @facts) { #-- output say .head.sort.map(*.key.uc.fmt("%-9s")).join(' | '); say .sort.map(*.value.fmt("%-9s")).join(' | ') for .list; last; # stop after first solution } } #| a solution has been found that fits all the facts multi sub solve(@solution, @facts [ ]) { say 'multi sub solve(@solution, @facts [ ]) {'; say @solution.elems; @solution.say; take @solution; } #| extend this scenario to fit the next fact multi sub solve(@scenario, [ $fact, *@facts ]) { for gather match(@scenario, |$fact) -> @houses { solve(@houses, @facts) } } #| find all possible solutions for pairs of houses with #| properties %b, left of a house with properties %a multi sub match(@houses, :Left-Of(%a)!, *%b) { for 1 ..^ @houses { my %left-house := @houses[$_-1]; my %right-house := @houses[$_]; if plausible(%left-house, %a) && plausible(%right-house, %b) { temp %left-house ,= %a; temp %right-house ,= %b; take @houses; } } } #| match these houses are next to each other (left or right) multi sub match(@houses, :Next-To(%b)!, *%a ) { match(@houses, |%a, :Left-Of(%b) ); match(@houses, |%b, :Left-Of(%a) ); } #| find all possible houses that match the given properties multi sub match(@houses, *%props) { for @houses.grep({plausible($_, %props)}) -> %house { temp %house ,= %props; take @houses; } } #| plausible if doesn't conflict with anything sub plausible(%house, %props) { ! %props.first: {%house{.key} && %house{.key} ne .value }; }