Just another update

This commit is contained in:
Ingy döt Net 2015-02-20 00:35:01 -05:00
parent a25938f123
commit 00a190b0a6
6591 changed files with 94363 additions and 23227 deletions

View file

@ -1,4 +1,6 @@
The [[wp:Zebra puzzle|Zebra puzzle]], a.k.a. Einstein's Riddle, is a logic puzzle which is to be solved programmatically. It has several variants, one of them this:
The [[wp:Zebra puzzle|Zebra puzzle]], a.k.a. Einstein's Riddle,
is a logic puzzle which is to be solved programmatically. <br>
It has several variants, one of them this:
#There are five houses.
#The English man lives in the red house.
@ -19,6 +21,7 @@ The [[wp:Zebra puzzle|Zebra puzzle]], a.k.a. Einstein's Riddle, is a logic puzzl
The question is, who owns the zebra?
Additionally, list the solution for all the houses. Optionally, show the solution is unique.
Additionally, list the solution for all the houses.
Optionally, show the solution is unique.
cf. [[Dinesman's multiple-dwelling problem]]
cf. [[Dinesman's multiple-dwelling problem]], [[Twelve statements]]

View file

@ -1,2 +1,4 @@
---
note: Logic puzzles
category:
- Constraint Handling Rules
note: Puzzles

View file

@ -0,0 +1,83 @@
( (English Swede Dane Norwegian German,)
(red green white yellow blue,(red.English.))
(dog birds cats horse zebra,(dog.?.Swede.))
( tea coffee milk beer water
, (tea.?.?.Dane.) (coffee.?.green.?.)
)
( "Pall Mall" Dunhill Blend "Blue Master" Prince
, ("Blue Master".beer.?.?.?.)
("Pall Mall".?.birds.?.?.)
(Dunhill.?.?.yellow.?.)
(Prince.?.?.?.German.)
)
( 1 2 3 4 5
, (3.?.milk.?.?.?.) (1.?.?.?.?.Norwegian.)
)
: ?properties
& ( relations
= next leftOf
. ( next
= a b A B
. !arg:(?S,?A,?B)
& !S:? (?a.!A) ?:? (?b.!B) ?
& (!a+1:!b|!b+1:!a)
)
& ( leftOf
= a b A B
. !arg:(?S,?A,?B)
& !S:? (?a.!A) ?:? (?b.!B) ?
& !a+1:!b
)
& leftOf
$ (!arg,(?.?.?.green.?.),(?.?.?.white.?.))
& next$(!arg,(Blend.?.?.?.?.),(?.?.cats.?.?.))
& next
$ (!arg,(?.?.horse.?.?.),(Dunhill.?.?.?.?.))
& next
$ (!arg,(?.?.?.?.Norwegian.),(?.?.?.blue.?.))
& next$(!arg,(?.water.?.?.?.),(Blend.?.?.?.?.))
)
& ( props
= a constraint constraints house houses
, remainingToDo shavedToDo toDo value values z
. !arg:(?toDo.?shavedToDo.?house.?houses)
& ( !toDo:(?values,?constraints) ?remainingToDo
& !values
: ( ?a
( %@?value
& !constraints
: ( ?
( !value
. ?constraint
& !house:!constraint
)
?
| ~( ?
( ?
. ?constraint
& !house:!constraint
)
?
| ? (!value.?) ?
)
)
)
( ?z
& props
$ ( !remainingToDo
. !shavedToDo (!a !z,!constraints)
. (!value.!house)
. !houses
)
)
|
& relations$!houses
& out$(Solution !houses)
)
| !toDo:
& props$(!shavedToDo...!house !houses)
)
)
& props$(!properties...)
& done
);

View file

@ -0,0 +1,147 @@
#include <stdio.h>
#include <string.h>
#define defenum(name, val0, val1, val2, val3, val4) \
enum name { val0, val1, val2, val3, val4 }; \
const char *name ## _str[] = { # val0, # val1, # val2, # val3, # val4 }
defenum( Attrib, Color, Man, Drink, Animal, Smoke );
defenum( Colors, Red, Green, White, Yellow, Blue );
defenum( Mans, English, Swede, Dane, German, Norwegian );
defenum( Drinks, Tea, Coffee, Milk, Beer, Water );
defenum( Animals, Dog, Birds, Cats, Horse, Zebra );
defenum( Smokes, PallMall, Dunhill, Blend, BlueMaster, Prince );
void printHouses(int ha[5][5]) {
const char **attr_names[5] = {Colors_str, Mans_str, Drinks_str, Animals_str, Smokes_str};
printf("%-10s", "House");
for (const char *name : Attrib_str) printf("%-10s", name);
printf("\n");
for (int i = 0; i < 5; i++) {
printf("%-10d", i);
for (int j = 0; j < 5; j++) printf("%-10s", attr_names[j][ha[i][j]]);
printf("\n");
}
}
struct HouseNoRule {
int houseno;
Attrib a; int v;
} housenos[] = {
{2, Drink, Milk}, // Cond 9: In the middle house they drink milk.
{0, Man, Norwegian} // Cond 10: The Norwegian lives in the first house.
};
struct AttrPairRule {
Attrib a1; int v1;
Attrib a2; int v2;
bool invalid(int ha[5][5], int i) {
return (ha[i][a1] >= 0 && ha[i][a2] >= 0) &&
((ha[i][a1] == v1 && ha[i][a2] != v2) ||
(ha[i][a1] != v1 && ha[i][a2] == v2));
}
} pairs[] = {
{Man, English, Color, Red}, // Cond 2: The English man lives in the red house.
{Man, Swede, Animal, Dog}, // Cond 3: The Swede has a dog.
{Man, Dane, Drink, Tea}, // Cond 4: The Dane drinks tea.
{Color, Green, Drink, Coffee}, // Cond 6: drink coffee in the green house.
{Smoke, PallMall, Animal, Birds}, // Cond 7: The man who smokes Pall Mall has birds.
{Smoke, Dunhill, Color, Yellow}, // Cond 8: In the yellow house they smoke Dunhill.
{Smoke, BlueMaster, Drink, Beer}, // Cond 13: The man who smokes Blue Master drinks beer.
{Man, German, Smoke, Prince} // Cond 14: The German smokes Prince
};
struct NextToRule {
Attrib a1; int v1;
Attrib a2; int v2;
bool invalid(int ha[5][5], int i) {
return (ha[i][a1] == v1) &&
((i == 0 && ha[i + 1][a2] >= 0 && ha[i + 1][a2] != v2) ||
(i == 4 && ha[i - 1][a2] != v2) ||
(ha[i + 1][a2] >= 0 && ha[i + 1][a2] != v2 && ha[i - 1][a2] != v2));
}
} nexttos[] = {
{Smoke, Blend, Animal, Cats}, // Cond 11: The man who smokes Blend lives in the house next to the house with cats.
{Smoke, Dunhill, Animal, Horse}, // Cond 12: In a house next to the house where they have a horse, they smoke Dunhill.
{Man, Norwegian, Color, Blue}, // Cond 15: The Norwegian lives next to the blue house.
{Smoke, Blend, Drink, Water} // Cond 16: They drink water in a house next to the house where they smoke Blend.
};
struct LeftOfRule {
Attrib a1; int v1;
Attrib a2; int v2;
bool invalid(int ha[5][5]) {
return (ha[0][a2] == v2) || (ha[4][a1] == v1);
}
bool invalid(int ha[5][5], int i) {
return ((i > 0 && ha[i][a1] >= 0) &&
((ha[i - 1][a1] == v1 && ha[i][a2] != v2) ||
(ha[i - 1][a1] != v1 && ha[i][a2] == v2)));
}
} leftofs[] = {
{Color, Green, Color, White} // Cond 5: The green house is immediately to the left of the white house.
};
bool invalid(int ha[5][5]) {
for (auto &rule : leftofs) if (rule.invalid(ha)) return true;
for (int i = 0; i < 5; i++) {
#define eval_rules(rules) for (auto &rule : rules) if (rule.invalid(ha, i)) return true;
eval_rules(pairs);
eval_rules(nexttos);
eval_rules(leftofs);
}
return false;
}
void search(bool used[5][5], int ha[5][5], const int hno, const int attr) {
int nexthno, nextattr;
if (attr < 4) {
nextattr = attr + 1;
nexthno = hno;
} else {
nextattr = 0;
nexthno = hno + 1;
}
if (ha[hno][attr] != -1) {
search(used, ha, nexthno, nextattr);
} else {
for (int i = 0; i < 5; i++) {
if (used[attr][i]) continue;
used[attr][i] = true;
ha[hno][attr] = i;
if (!invalid(ha)) {
if ((hno == 4) && (attr == 4)) {
printHouses(ha);
} else {
search(used, ha, nexthno, nextattr);
}
}
used[attr][i] = false;
}
ha[hno][attr] = -1;
}
}
int main() {
bool used[5][5] = {};
int ha[5][5]; memset(ha, -1, sizeof(ha));
for (auto &rule : housenos) {
ha[rule.houseno][rule.a] = rule.v;
used[rule.a][rule.v] = true;
}
search(used, ha, 0, 0);
return 0;
}

View file

@ -0,0 +1,43 @@
import Constraint (allC, anyC)
import Findall (findall)
data House = H Color Man Pet Drink Smoke
data Color = Red | Green | Blue | Yellow | White
data Man = Eng | Swe | Dan | Nor | Ger
data Pet = Dog | Birds | Cats | Horse | Zebra
data Drink = Coffee | Tea | Milk | Beer | Water
data Smoke = PM | DH | Blend | BM | Prince
houses :: [House] -> Success
houses hs@[H1,_,H3,_,_] = -- 1
H _ _ _ Milk _ =:= H3 -- 9
& H _ Nor _ _ _ =:= H1 -- 10
& allC (`member` hs)
[ H Red Eng _ _ _ -- 2
, H _ Swe Dog _ _ -- 3
, H _ Dan _ Tea _ -- 4
, H Green _ _ Coffee _ -- 6
, H _ _ Birds _ PM -- 7
, H Yellow _ _ _ DH -- 8
, H _ _ _ Beer BM -- 13
, H _ Ger _ _ Prince -- 14
]
& H Green _ _ _ _ `leftTo` H White _ _ _ _ -- 5
& H _ _ _ _ Blend `nextTo` H _ _ Cats _ _ -- 11
& H _ _ Horse _ _ `nextTo` H _ _ _ _ DH -- 12
& H _ Nor _ _ _ `nextTo` H Blue _ _ _ _ -- 15
& H _ _ _ Water _ `nextTo` H _ _ _ _ Blend -- 16
where
x `leftTo` y = _ ++ [x,y] ++ _ =:= hs
x `nextTo` y = x `leftTo` y
? y `leftTo` x
member :: a -> [a] -> Success
member = anyC . (=:=)
main = findall $ \(hs,who) -> houses hs & H _ who Zebra _ _ `member` hs

View file

@ -8,11 +8,11 @@ enum Content { Beer, Coffee, Milk, Tea, Water,
enum Test { Drink, Person, Color, Smoke, Pet }
enum House { One, Two, Three, Four, Five }
alias Content[EnumMembers!Test.length][EnumMembers!House.length] TM;
alias TM = Content[EnumMembers!Test.length][EnumMembers!House.length];
bool finalChecks(in ref TM M) pure nothrow {
bool finalChecks(in ref TM M) pure nothrow @safe @nogc {
int diff(in Content a, in Content b, in Test ca, in Test cb)
nothrow {
nothrow @safe @nogc {
foreach (immutable h1; EnumMembers!House)
foreach (immutable h2; EnumMembers!House)
if (M[ca][h1] == a && M[cb][h2] == b)
@ -20,16 +20,15 @@ bool finalChecks(in ref TM M) pure nothrow {
assert(0); // Useless but required.
}
with (Content) with (Test) { // Braces required (8414).
with (Content) with (Test)
return abs(diff(Norwegian, Blue, Person, Color)) == 1 &&
diff(Green, White, Color, Color) == -1 &&
abs(diff(Horse, Dunhill, Pet, Smoke)) == 1 &&
abs(diff(Water, Blend, Drink, Smoke)) == 1 &&
abs(diff(Blend, Cat, Smoke, Pet)) == 1;
}
}
bool constrained(in ref TM M, in Test atest) pure nothrow {
bool constrained(in ref TM M, in Test atest) pure nothrow @safe @nogc {
with (Content) with (Test) with (House)
final switch (atest) {
case Drink:
@ -67,7 +66,7 @@ void show(in ref TM M) {
writef("%5s: ", h);
foreach (immutable t; EnumMembers!Test)
writef("%10s ", M[t][h]);
writeln();
writeln;
}
}

View file

@ -1,4 +1,14 @@
import std.stdio, std.math, std.traits, std.typetuple, permutations1;
import std.stdio, std.math, std.traits, std.typecons, std.typetuple, permutations1;
uint factorial(in uint n) pure nothrow @nogc @safe
in {
assert(n <= 12);
} body {
uint result = 1;
foreach (immutable i; 1 .. n + 1)
result *= i;
return result;
}
enum Number { One, Two, Three, Four, Five }
enum Color { Red, Green, Blue, White, Yellow }
@ -7,42 +17,46 @@ enum Smoke { PallMall, Dunhill, Blend, BlueMaster, Prince }
enum Pet { Dog, Cat, Zebra, Horse, Bird }
enum Nation { British, Swedish, Danish, Norvegian, German }
bool isPossible(immutable Number[5]* number,
immutable Color[5]* color=null,
immutable Drink[5]* drink=null,
immutable Smoke[5]* smoke=null,
immutable Pet[5]* pet=null) pure nothrow {
if ((number && (*number)[Nation.Norvegian] != Number.One) ||
(color && (*color)[Nation.British] != Color.Red) ||
(drink && (*drink)[Nation.Danish] != Drink.Tea) ||
(smoke && (*smoke)[Nation.German] != Smoke.Prince) ||
(pet && (*pet)[Nation.Swedish] != Pet.Dog))
enum size_t M = EnumMembers!Number.length;
auto nullableRef(T)(ref T item) pure nothrow @nogc {
return NullableRef!T(&item);
}
bool isPossible(NullableRef!(immutable Number[M]) number,
NullableRef!(immutable Color[M]) color=null,
NullableRef!(immutable Drink[M]) drink=null,
NullableRef!(immutable Smoke[M]) smoke=null,
NullableRef!(immutable Pet[M]) pet=null) pure nothrow @safe @nogc {
if ((!number.isNull && number[Nation.Norvegian] != Number.One) ||
(!color.isNull && color[Nation.British] != Color.Red) ||
(!drink.isNull && drink[Nation.Danish] != Drink.Tea) ||
(!smoke.isNull && smoke[Nation.German] != Smoke.Prince) ||
(!pet.isNull && pet[Nation.Swedish] != Pet.Dog))
return false;
if (!number || !color || !drink || !smoke || !pet)
if (number.isNull || color.isNull || drink.isNull || smoke.isNull ||
pet.isNull)
return true;
foreach (immutable i; 0 .. 5) {
if (((*color)[i] == Color.Green && (*drink)[i] != Drink.Coffee) ||
((*smoke)[i] == Smoke.PallMall && (*pet)[i] != Pet.Bird) ||
((*color)[i] == Color.Yellow && (*smoke)[i] != Smoke.Dunhill) ||
((*number)[i] == Number.Three && (*drink)[i] != Drink.Milk) ||
((*smoke)[i] == Smoke.BlueMaster && (*drink)[i] != Drink.Beer)||
((*color)[i] == Color.Blue && (*number)[i] != Number.Two))
foreach (immutable i; 0 .. M) {
if ((color[i] == Color.Green && drink[i] != Drink.Coffee) ||
(smoke[i] == Smoke.PallMall && pet[i] != Pet.Bird) ||
(color[i] == Color.Yellow && smoke[i] != Smoke.Dunhill) ||
(number[i] == Number.Three && drink[i] != Drink.Milk) ||
(smoke[i] == Smoke.BlueMaster && drink[i] != Drink.Beer)||
(color[i] == Color.Blue && number[i] != Number.Two))
return false;
foreach (immutable j; 0 .. 5) {
if ((*color)[i] == Color.Green && (*color)[j] == Color.White &&
(*number)[j] - (*number)[i] != 1)
foreach (immutable j; 0 .. M) {
if (color[i] == Color.Green && color[j] == Color.White &&
number[j] - number[i] != 1)
return false;
immutable int diff = abs((*number)[i] - (*number)[j]);
if (((*smoke)[i] == Smoke.Blend &&
(*pet)[j] == Pet.Cat && diff != 1) ||
((*pet)[i] == Pet.Horse &&
(*smoke)[j] == Smoke.Dunhill && diff != 1) ||
((*smoke)[i] == Smoke.Blend &&
(*drink)[j] == Drink.Water && diff != 1))
immutable diff = abs(number[i] - number[j]);
if ((smoke[i] == Smoke.Blend && pet[j] == Pet.Cat && diff != 1) ||
(pet[i] == Pet.Horse && smoke[j] == Smoke.Dunhill && diff != 1) ||
(smoke[i] == Smoke.Blend && drink[j] == Drink.Water && diff != 1))
return false;
}
}
@ -50,32 +64,36 @@ bool isPossible(immutable Number[5]* number,
return true;
}
alias N = nullableRef; // At module level scope to be used with UFCS.
void main() {
static immutable int[5][] perms = [0, 1, 2, 3, 4].permutations;
immutable nation = [EnumMembers!Nation];
enum size_t FM = M.factorial;
// Not nice casts:
static immutable permsNumber = cast(immutable Number[5][])perms,
permsColor = cast(immutable Color[5][])perms,
permsDrink = cast(immutable Drink[5][])perms,
permsSmoke = cast(immutable Smoke[5][])perms,
permsPet = cast(immutable Pet[5][])perms;
static immutable Number[M][FM] numberPerms = [EnumMembers!Number].permutations;
static immutable Color[M][FM] colorPerms = [EnumMembers!Color].permutations;
static immutable Drink[M][FM] drinkPerms = [EnumMembers!Drink].permutations;
static immutable Smoke[M][FM] smokePerms = [EnumMembers!Smoke].permutations;
static immutable Pet[M][FM] petPerms = [EnumMembers!Pet].permutations;
foreach (immutable ref number; permsNumber)
if (isPossible(&number))
foreach (immutable ref color; permsColor)
if (isPossible(&number, &color))
foreach (immutable ref drink; permsDrink)
if (isPossible(&number, &color, &drink))
foreach (immutable ref smoke; permsSmoke)
if (isPossible(&number, &color, &drink, &smoke))
foreach (immutable ref pet; permsPet)
if (isPossible(&number,&color,&drink,&smoke,&pet)) {
// You can reduce the compile-time computations using four casts like this:
// static colorPerms = cast(immutable Color[M][FM])numberPerms;
static immutable Nation[M] nation = [EnumMembers!Nation];
foreach (immutable ref number; numberPerms)
if (isPossible(number.N))
foreach (immutable ref color; colorPerms)
if (isPossible(number.N, color.N))
foreach (immutable ref drink; drinkPerms)
if (isPossible(number.N, color.N, drink.N))
foreach (immutable ref smoke; smokePerms)
if (isPossible(number.N, color.N, drink.N, smoke.N))
foreach (immutable ref pet; petPerms)
if (isPossible(number.N, color.N, drink.N, smoke.N, pet.N)) {
writeln("Found a solution:");
foreach (immutable x; TypeTuple!(nation, number,
color, drink, smoke, pet))
foreach (x; TypeTuple!(nation, number, color, drink, smoke, pet))
writefln("%6s: %12s%12s%12s%12s%12s",
Unqual!(typeof(x[0])).stringof,
(Unqual!(typeof(x[0]))).stringof,
x[0], x[1], x[2], x[3], x[4]);
writeln;
}

View file

@ -0,0 +1,33 @@
void main() {
import std.stdio, std.algorithm, permutations2;
enum E { Red, Green, Blue, White, Yellow,
Milk, Coffee, Water, Beer, Tea,
PallMall, Dunhill, Blend, BlueMaster, Prince,
Dog, Cat, Zebra, Horse, Birds,
British, Swedish, Danish, Norvegian, German }
enum has = (E[] a, E x, E[] b, E y) => a.countUntil(x) == b.countUntil(y);
enum leftOf = (E[] a, E x, E[] b, E y) => a.countUntil(x) == b.countUntil(y) + 1;
enum nextTo = (E[] a, E x, E[] b, E y) => leftOf(a, x, b, y) || leftOf(b, y, a, x);
with (E) foreach (houses; [Red, Blue, Green, Yellow, White].permutations)
if (leftOf(houses, White, houses, Green))
foreach (persons; [Norvegian, British, Swedish, German, Danish].permutations)
if (has(persons, British, houses, Red) && persons[0] == Norvegian &&
nextTo(persons, Norvegian, houses, Blue))
foreach (drinks; [Tea, Coffee, Milk, Beer, Water].permutations)
if (has(drinks, Tea, persons, Danish) &&
has(drinks, Coffee, houses, Green) && drinks[$ / 2] == Milk)
foreach (pets; [Dog, Birds, Cat, Horse, Zebra].permutations)
if (has(pets, Dog, persons, Swedish))
foreach (smokes; [PallMall, Dunhill, Blend, BlueMaster, Prince].permutations)
if (has(smokes, PallMall, pets, Birds) &&
has(smokes, Dunhill, houses, Yellow) &&
nextTo(smokes, Blend, pets, Cat) &&
nextTo(smokes, Dunhill, pets, Horse) &&
has(smokes, BlueMaster, drinks, Beer) &&
has(smokes, Prince, persons, German) &&
nextTo(drinks, Water, smokes, Blend))
writefln("%(%10s\n%)\n", [houses, persons, drinks, pets, smokes]);
}

View file

@ -0,0 +1,294 @@
package main
import (
"fmt"
"log"
"strings"
)
// Define some types
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
// Define the possible values
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
)
// And how to print them
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")
}
// Simple brute force solution
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 { // Condition 10:
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 { // Condition 9:
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
//return 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
}
// anyDups returns true if h as any duplicate attributes with any of the specified houses
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 {
// Condition 2:
if h.n == English && h.c != Red || h.n != English && h.c == Red {
return false
}
// Condition 3:
if h.n == Swede && h.a != Dog || h.n != Swede && h.a == Dog {
return false
}
// Condition 4:
if h.n == Dane && h.d != Tea || h.n != Dane && h.d == Tea {
return false
}
// Condition 6:
if h.c == Green && h.d != Coffee || h.c != Green && h.d == Coffee {
return false
}
// Condition 7:
if h.a == Birds && h.s != PallMall || h.a != Birds && h.s == PallMall {
return false
}
// Condition 8:
if h.c == Yellow && h.s != Dunhill || h.c != Yellow && h.s == Dunhill {
return false
}
// Condition 11:
if h.a == Cats && h.s == Blend {
return false
}
// Condition 12:
if h.a == Horse && h.s == Dunhill {
return false
}
// Condition 13:
if h.d == Beer && h.s != BlueMaster || h.d != Beer && h.s == BlueMaster {
return false
}
// Condition 14:
if h.n == German && h.s != Prince || h.n != German && h.s == Prince {
return false
}
// Condition 15:
if h.n == Norwegian && h.c == Blue {
return false
}
// Condition 16:
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
}
// Condition 5:
if ci[Green]+1 != ci[White] {
return false
}
// Condition 11:
if dist(ai[Cats], si[Blend]) != 1 {
return false
}
// Condition 12:
if dist(ai[Horse], si[Dunhill]) != 1 {
return false
}
// Condition 15:
if dist(ni[Norwegian], ci[Blue]) != 1 {
return false
}
// Condition 16:
if dist(di[Water], si[Blend]) != 1 {
return false
}
// Condition 9: (already tested elsewhere)
if hs[2].d != Milk {
return false
}
// Condition 10: (already tested elsewhere)
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)
}

View file

@ -1,5 +1,5 @@
import Control.Applicative ((<$>), (<*>))
import Control.Monad
import Control.Monad (foldM, forM_)
import Data.List ((\\), isInfixOf)
-- types
@ -30,7 +30,8 @@ data Smoke = PallMall | Dunhill | Blend | BlueMaster | Prince
main :: IO ()
main = do
mapM_ (\x-> mapM_ print (reverse x) >> putStrLn "----") solutions
forM_ solutions $ \x -> mapM_ print (reverse x)
>> putStrLn "----"
putStrLn "No More Solutions"

View file

@ -0,0 +1,60 @@
import Control.Monad
import Data.List
values :: (Bounded a, Enum a) => [[a]]
values = permutations [minBound..maxBound]
data Nation = English | Swede | Dane | Norwegian | German
deriving (Bounded, Enum, Eq, Show)
data Color = Red | Green | White | Yellow | Blue
deriving (Bounded, Enum, Eq, Show)
data Pet = Dog | Birds | Cats | Horse | Zebra
deriving (Bounded, Enum, Eq, Show)
data Drink = Tea | Coffee | Milk | Beer | Water
deriving (Bounded, Enum, Eq, Show)
data Smoke = PallMall | Dunhill | Blend | BlueMaster | Prince
deriving (Bounded, Enum, Eq, Show)
answers = do
color <- values
leftOf color Green color White -- 5
nation <- values
first nation Norwegian -- 10
same nation English color Red -- 2
nextTo nation Norwegian color Blue -- 15
drink <- values
middle drink Milk -- 9
same nation Dane drink Tea -- 4
same drink Coffee color Green -- 6
pet <- values
same nation Swede pet Dog -- 3
smoke <- values
same smoke PallMall pet Birds -- 7
same color Yellow smoke Dunhill -- 8
nextTo smoke Blend pet Cats -- 11
nextTo pet Horse smoke Dunhill -- 12
same nation German smoke Prince -- 14
same smoke BlueMaster drink Beer -- 13
nextTo drink Water smoke Blend -- 16
return $ zip5 nation color pet drink smoke
where
same xs x ys y = guard $ (x, y) `elem` zip xs ys
leftOf xs x ys y = same xs x (tail ys) y
nextTo xs x ys y = leftOf xs x ys y `mplus`
leftOf ys y xs x
middle xs x = guard $ xs !! 2 == x
first xs x = guard $ head xs == x
main = do
forM_ answers $ (\answer -> -- for answer in answers:
do
mapM_ print answer
print [nation | (nation, _, Zebra, _, _) <- answer]
putStrLn "" )
putStrLn "No more solutions!"

View file

@ -0,0 +1,220 @@
package zebra;
public class LineOfPuzzle implements Cloneable{
private Integer order;
private String nation;
private String color;
private String animal;
private String drink;
private String cigarette;
private LineOfPuzzle rightNeighbor;
private LineOfPuzzle leftNeighbor;
private PuzzleSet<LineOfPuzzle> undefNeighbors;
public LineOfPuzzle (Integer order, String nation, String color,
String animal, String drink, String cigarette){
this.animal=animal;
this.cigarette=cigarette;
this.color=color;
this.drink=drink;
this.nation=nation;
this.order=order;
}
public Integer getOrder() {
return order;
}
public void setOrder(Integer order) {
this.order = order;
}
public String getNation() {
return nation;
}
public void setNation(String nation) {
this.nation = nation;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getAnimal() {
return animal;
}
public void setAnimal(String animal) {
this.animal = animal;
}
public String getDrink() {
return drink;
}
public void setDrink(String drink) {
this.drink = drink;
}
public String getCigarette() {
return cigarette;
}
public void setCigarette(String cigarette) {
this.cigarette = cigarette;
}
/**
* Overrides object equal method
* @param obj
* @return
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof LineOfPuzzle){
LineOfPuzzle searchLine = (LineOfPuzzle)obj;
return this.getWholeLine().equalsIgnoreCase(searchLine.getWholeLine());
}
else
return false;
}
public int getFactsCount(){
int facts = 0;
facts+=this.getOrder()!=null?1:0;
facts+=this.getNation()!=null?1:0;
facts+=this.getColor()!=null?1:0;
facts+=this.getAnimal()!=null?1:0;
facts+=this.getCigarette()!=null?1:0;
facts+=this.getDrink()!=null?1:0;
return facts;
}
public int getCommonFactsCount(LineOfPuzzle lineOfFacts){
int ordrCmp = (this.order!=null && lineOfFacts.getOrder()!= null &&
this.order.intValue()== lineOfFacts.getOrder().intValue())?1:0;
int natnCmp = (this.nation!=null && lineOfFacts.getNation()!= null &&
this.nation.equalsIgnoreCase(lineOfFacts.getNation()))?1:0;
int colrCmp = (this.color!=null && lineOfFacts.getColor()!= null &&
this.color.equalsIgnoreCase(lineOfFacts.getColor()))?1:0;
int petsCmp = (this.animal!=null && (lineOfFacts.getAnimal()!= null &&
this.animal.equalsIgnoreCase(lineOfFacts.getAnimal())))?1:0;
int cigrCmp = (this.cigarette!=null && lineOfFacts.getCigarette()!= null &&
this.cigarette.equalsIgnoreCase(lineOfFacts.getCigarette()))?1:0;
int drnkCmp = (this.drink!=null && lineOfFacts.getDrink()!= null &&
this.drink.equalsIgnoreCase(lineOfFacts.getDrink()))?1:0;
int result = (ordrCmp + natnCmp + colrCmp + petsCmp + cigrCmp + drnkCmp);
return result;
}
public void addUndefindedNeighbor(LineOfPuzzle newNeighbor){
if (this.undefNeighbors==null)
this.undefNeighbors = new PuzzleSet<>();
this.undefNeighbors.add(newNeighbor);
}
public boolean hasUndefNeighbors(){
return (this.undefNeighbors!=null);
}
public PuzzleSet<LineOfPuzzle> getUndefNeighbors(){
return this.undefNeighbors;
}
public void setLeftNeighbor(LineOfPuzzle leftNeighbor){
this.leftNeighbor = leftNeighbor;
this.leftNeighbor.setOrder(this.order - 1);
}
public void setRightNeighbor(LineOfPuzzle rightNeighbor){
this.rightNeighbor=rightNeighbor;
this.rightNeighbor.setOrder(this.order + 1);
}
public boolean hasLeftNeighbor(){
return (leftNeighbor!=null);
}
public LineOfPuzzle getLeftNeighbor(){
return this.leftNeighbor;
}
public boolean hasNeighbor(int direction){
if (direction < 0)
return (leftNeighbor!=null);
else
return (rightNeighbor!=null);
}
public boolean hasRightNeighbor(){
return (rightNeighbor!=null);
}
public LineOfPuzzle getRightNeighbor(){
return this.rightNeighbor;
}
public LineOfPuzzle getNeighbor(int direction){
if (direction < 0)
return this.leftNeighbor;
else
return this.rightNeighbor;
}
public String getWholeLine() {
String sLine = this.order + " - " +
this.nation + " - " +
this.color + " - " +
this.animal + " - " +
this.drink + " - " +
this.cigarette;
return sLine;
}
@Override
public int hashCode() {
int sLine = (this.order + " - " +
this.nation + " - " +
this.color + " - " +
this.animal + " - " +
this.drink + " - " +
this.cigarette
).hashCode();
return sLine;
}
public void merge(LineOfPuzzle mergedLine){
if (this.order == null) this.order = mergedLine.order;
if (this.nation == null) this.nation = mergedLine.nation;
if (this.color == null) this.color = mergedLine.color;
if (this.animal == null) this.animal = mergedLine.animal;
if (this.drink == null) this.drink = mergedLine.drink;
if (this.cigarette == null) this.cigarette = mergedLine.cigarette;
}
public LineOfPuzzle clone() {
try {
return (LineOfPuzzle) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}

View file

@ -0,0 +1,98 @@
package zebra;
import java.util.Iterator;
import java.util.LinkedHashSet;
public class PuzzleSet<T extends LineOfPuzzle> extends LinkedHashSet{
private T t;
private int countOfOne=0;
private int countOfTwo=0;
private int countOfThree=0;
private int countOfFour=0;
private int countOfFive=0;
PuzzleSet() {
super();
}
public void set(T t) { this.t = t; }
public T get(int index) {
return ((T)this.toArray()[index]);
}
public PuzzleSet<T> getSimilarLines(T searchLine) {
PuzzleSet<T> puzzleSubSet = new PuzzleSet<>();
for (Iterator<T> it = this.iterator(); it.hasNext();) {
T lineOfPuzzle = it.next();
if(lineOfPuzzle.getCommonFactsCount(searchLine) == searchLine.getFactsCount())
puzzleSubSet.add(lineOfPuzzle);
}
if (puzzleSubSet.isEmpty())
return null;
return puzzleSubSet;
}
public boolean contains(T searchLine) {
for (Iterator<T> it = this.iterator(); it.hasNext();) {
T puzzleLine = it.next();
if(puzzleLine.getCommonFactsCount(searchLine) == searchLine.getFactsCount())
return true;
}
return false;
}
public boolean accepts(T searchLine) {
int passed=0;
int notpassed=0;
for (Iterator<T> it = this.iterator(); it.hasNext();) {
T puzzleSetLine = it.next();
int lineFactsCnt = puzzleSetLine.getFactsCount();
int comnFactsCnt = puzzleSetLine.getCommonFactsCount(searchLine);
if( lineFactsCnt != comnFactsCnt && lineFactsCnt !=0 && comnFactsCnt !=0){
notpassed++;
}
if( lineFactsCnt == comnFactsCnt)
passed++;
}
return (passed >= 0 && notpassed == 0);
}
public void riseLineCountFlags(int lineOrderId){
switch (lineOrderId){
case 1: countOfOne++; break;
case 2: countOfTwo++; break;
case 3: countOfThree++; break;
case 4: countOfFour++; break;
case 5: countOfFive++; break;
default:;
}
}
public void clearLineCountFlags(){
countOfOne=0;
countOfTwo=0;
countOfThree=0;
countOfFour=0;
countOfFive=0;
}
public int getLineCountByOrderId(int lineOrderId){
switch (lineOrderId){
case 1: return countOfOne;
case 2: return countOfTwo;
case 3: return countOfThree;
case 4: return countOfFour;
case 5: return countOfFive;
default:return -1;
}
}
}

View file

@ -0,0 +1,317 @@
package zebra;
import java.util.ArrayList;
import java.util.Iterator;
public class Puzzle {
private static final ArrayList<Integer> orders = new ArrayList<>(5);
private static final ArrayList<String> nations = new ArrayList<>(5);
private static final ArrayList<String> animals = new ArrayList<>(5);
private static final ArrayList<String> drinks = new ArrayList<>(5);
private static final ArrayList<String> cigarettes = new ArrayList<>(5);
private static final ArrayList<String> colors = new ArrayList<>(5);
private static PuzzleSet<LineOfPuzzle> puzzleTable;
static
{
// House orders
orders.add(1);
orders.add(2);
orders.add(3);
orders.add(4);
orders.add(5);
// Man nations
nations.add("English");
nations.add("Danish");
nations.add("German");
nations.add("Swedesh");
nations.add("Norwegian");
//Animals
animals.add("Zebra");
animals.add("Horse");
animals.add("Birds");
animals.add("Dog");
animals.add("Cats");
//Drinks
drinks.add("Coffee");
drinks.add("Tea");
drinks.add("Beer");
drinks.add("Water");
drinks.add("Milk");
//Smokes
cigarettes.add("Pall Mall");
cigarettes.add("Blend");
cigarettes.add("Blue Master");
cigarettes.add("Prince");
cigarettes.add("Dunhill");
//Colors
colors.add("Red");
colors.add("Green");
colors.add("White");
colors.add("Blue");
colors.add("Yellow");
}
public static void main (String[] args){
boolean validLine=true;
puzzleTable = new PuzzleSet<>();
//Rules
LineOfPuzzle rule2 = new LineOfPuzzle(null, "English", "Red", null, null, null);
LineOfPuzzle rule3 = new LineOfPuzzle(null, "Swedesh", null, "Dog", null, null);
LineOfPuzzle rule4 = new LineOfPuzzle(null, "Danish", null, null, "Tea", null);
LineOfPuzzle rule6 = new LineOfPuzzle(null, null, "Green", null, "Coffee", null);
LineOfPuzzle rule7 = new LineOfPuzzle(null, null, null, "Birds", null, "Pall Mall");
LineOfPuzzle rule8 = new LineOfPuzzle(null, null, "Yellow", null, null, "Dunhill");
LineOfPuzzle rule9 = new LineOfPuzzle(3, null, null, null, "Milk", null);
LineOfPuzzle rule10 = new LineOfPuzzle(1, "Norwegian", null, null, null, null);
LineOfPuzzle rule13 = new LineOfPuzzle(null, null, null, null, "Beer", "Blue Master");
LineOfPuzzle rule14 = new LineOfPuzzle(null, "German", null, null, null, "Prince");
LineOfPuzzle rule15 = new LineOfPuzzle(2, null, "Blue", null, null, null);
PuzzleSet<LineOfPuzzle> ruleSet = new PuzzleSet<>();
ruleSet.add(rule2);
ruleSet.add(rule3);
ruleSet.add(rule4);
ruleSet.add(rule6);
ruleSet.add(rule7);
ruleSet.add(rule8);
ruleSet.add(rule9);
ruleSet.add(rule10);
ruleSet.add(rule13);
ruleSet.add(rule14);
ruleSet.add(rule15);
//Creating all possible combination of a puzzle line.
//The maximum number of lines is 5^^6 (15625).
//Each combination line is checked against a set of knowing facts, thus
//only a small number of line result at the end.
for (Integer orderId : Puzzle.orders) {
for (String nation : Puzzle.nations) {
for (String color : Puzzle.colors) {
for (String animal : Puzzle.animals) {
for (String drink : Puzzle.drinks) {
for (String cigarette : Puzzle.cigarettes) {
LineOfPuzzle pzlLine = new LineOfPuzzle(orderId,
nation,
color,
animal,
drink,
cigarette);
// Checking against a set of knowing facts
if (ruleSet.accepts(pzlLine)){
// Adding rules of neighbors
if (cigarette.equalsIgnoreCase("Blend")
&& (animal.equalsIgnoreCase("Cats")
|| drink.equalsIgnoreCase("Water")))
validLine = false;
if (cigarette.equalsIgnoreCase("Dunhill")
&& animal.equalsIgnoreCase("Horse"))
validLine = false;
if (validLine){
puzzleTable.add(pzlLine);
//set neighbors constraints
if (color.equalsIgnoreCase("Green")){
pzlLine.setRightNeighbor(new LineOfPuzzle(null, null, "White", null, null, null));
}
if (color.equalsIgnoreCase("White")){
pzlLine.setLeftNeighbor(new LineOfPuzzle(null, null, "Green", null, null, null));
}
//
if (animal.equalsIgnoreCase("Cats")
&& !cigarette.equalsIgnoreCase("Blend") ){
pzlLine.addUndefindedNeighbor(new LineOfPuzzle(null, null, null, null, null, "Blend"));
}
if (cigarette.equalsIgnoreCase("Blend")
&& !animal.equalsIgnoreCase("Cats")){
pzlLine.addUndefindedNeighbor(new LineOfPuzzle(null, null, null, "Cats", null, null));
}
//
if (drink.equalsIgnoreCase("Water")
&& !animal.equalsIgnoreCase("Cats")
&& !cigarette.equalsIgnoreCase("Blend")){
pzlLine.addUndefindedNeighbor(new LineOfPuzzle(null, null, null, null, null, "Blend"));
}
if (cigarette.equalsIgnoreCase("Blend")
&& !drink.equalsIgnoreCase("Water")){
pzlLine.addUndefindedNeighbor(new LineOfPuzzle(null, null, null, null, "Water", null));
}
//
if (animal.equalsIgnoreCase("Horse")
&& !cigarette.equalsIgnoreCase("Dunhill")){
pzlLine.addUndefindedNeighbor(new LineOfPuzzle(null, null, null, null, null, "Dunhill"));
}
if (cigarette.equalsIgnoreCase("Dunhill")
&& !animal.equalsIgnoreCase("Horse")){
pzlLine.addUndefindedNeighbor(new LineOfPuzzle(null, null, null, "Horse", null, null));
}
}
validLine = true;
}
} //cigarette end
} //drinks end
} //animal end
} //color end
} //nations end
} //order end
System.out.println("After general rule set validation, remains "+
puzzleTable.size() + " lines.");
for (Iterator<LineOfPuzzle> it = puzzleTable.iterator(); it.hasNext();){
validLine=true;
LineOfPuzzle lineOfPuzzle = it.next();
if (lineOfPuzzle.hasLeftNeighbor()){
LineOfPuzzle neighbor = lineOfPuzzle.getLeftNeighbor();
if (neighbor.getOrder()<1 || neighbor.getOrder()>5){
validLine=false;
it.remove();
}
}
if (validLine && lineOfPuzzle.hasRightNeighbor()){
LineOfPuzzle neighbor = lineOfPuzzle.getRightNeighbor();
if (neighbor.getOrder()<1 || neighbor.getOrder()>5){
it.remove();
}
}
}
System.out.println("After removing out of bound neighbors, remains " +
puzzleTable.size() + " lines.");
//Setting left and right neighbors
for (Iterator<LineOfPuzzle> it = puzzleTable.iterator(); it.hasNext();) {
LineOfPuzzle puzzleLine = it.next();
if (puzzleLine.hasUndefNeighbors()){
for (Iterator<LineOfPuzzle> it1 = puzzleLine.getUndefNeighbors().iterator(); it1.hasNext();) {
LineOfPuzzle leftNeighbor = it1.next();
LineOfPuzzle rightNeighbor = leftNeighbor.clone();
//make it left neighbor
leftNeighbor.setOrder(puzzleLine.getOrder()-1);
if (puzzleTable.contains(leftNeighbor)){
if (puzzleLine.hasLeftNeighbor())
puzzleLine.getLeftNeighbor().merge(leftNeighbor);
else
puzzleLine.setLeftNeighbor(leftNeighbor);
}
rightNeighbor.setOrder(puzzleLine.getOrder()+1);
if (puzzleTable.contains(rightNeighbor)){
if (puzzleLine.hasRightNeighbor())
puzzleLine.getRightNeighbor().merge(rightNeighbor);
else
puzzleLine.setRightNeighbor(rightNeighbor);
}
}
}
}
int iteration=1;
int lastSize=0;
//Recursively validate against neighbor rules
while (puzzleTable.size()>5 && lastSize != puzzleTable.size()) {
lastSize = puzzleTable.size();
puzzleTable.clearLineCountFlags();
recursiveSearch(null, puzzleTable, -1);
ruleSet.clear();
// Assuming we'll get at leas one valid line each iteration, we create
// a set of new rules with lines which have no more then one instance of same OrderId.
for (int i = 1; i < 6; i++) {
if (puzzleTable.getLineCountByOrderId(i)==1)
ruleSet.addAll(puzzleTable.getSimilarLines(new LineOfPuzzle(i, null, null, null, null, null)));
}
for (Iterator<LineOfPuzzle> it = puzzleTable.iterator(); it.hasNext();) {
LineOfPuzzle puzzleLine = it.next();
if (!ruleSet.accepts(puzzleLine))
it.remove();
}
//
System.out.println("After " + iteration + " recursive iteration, remains "
+ puzzleTable.size() + " lines");
iteration+=1;
}
// Print the results
System.out.println("-------------------------------------------");
if (puzzleTable.size()==5){
for (Iterator<LineOfPuzzle> it = puzzleTable.iterator(); it.hasNext();) {
LineOfPuzzle puzzleLine = it.next();
System.out.println(puzzleLine.getWholeLine());
}
}else
System.out.println("Sorry, solution not found!");
}
// Recursively checks the input set to ensure each line has right neighbor.
// Neighbors can be of three type, left, right or undefined.
// Direction: -1 left, 0 undefined, 1 right
private static boolean recursiveSearch(LineOfPuzzle pzzlNodeLine,
PuzzleSet puzzleSet, int direction){
boolean validLeaf = false;
boolean hasNeighbor = false;
PuzzleSet<LineOfPuzzle> puzzleSubSet = null;
for (Iterator<LineOfPuzzle> it = puzzleSet.iterator(); it.hasNext();) {
LineOfPuzzle pzzlLeafLine = it.next();
validLeaf = false;
hasNeighbor = pzzlLeafLine.hasNeighbor(direction);
if (hasNeighbor){
puzzleSubSet = puzzleTable.getSimilarLines(pzzlLeafLine.getNeighbor(direction));
if (puzzleSubSet != null){
if (pzzlNodeLine !=null)
validLeaf = puzzleSubSet.contains(pzzlNodeLine);
else
validLeaf = recursiveSearch(pzzlLeafLine, puzzleSubSet, -1*direction);
}
else
validLeaf = false;
}
if (!validLeaf && pzzlLeafLine.hasNeighbor(-1*direction)){
hasNeighbor = true;
if (hasNeighbor){
puzzleSubSet = puzzleTable.getSimilarLines(pzzlLeafLine.getNeighbor(-1*direction));
if (puzzleSubSet != null){
if (pzzlNodeLine !=null)
validLeaf = puzzleSubSet.contains(pzzlNodeLine);
else
validLeaf = recursiveSearch(pzzlLeafLine, puzzleSubSet, direction);
}
else
validLeaf = false;
}
}
if (pzzlNodeLine != null && validLeaf)
return validLeaf;
if (pzzlNodeLine == null && hasNeighbor && !validLeaf){
it.remove();
}
if (pzzlNodeLine == null){
if (hasNeighbor && validLeaf){
puzzleSet.riseLineCountFlags(pzzlLeafLine.getOrder());
}
if (!hasNeighbor){
puzzleSet.riseLineCountFlags(pzzlLeafLine.getOrder());
}
}
}
return validLeaf;
}
}

View file

@ -0,0 +1,114 @@
ClearAll[EliminatePoss, FilterPuzzle]
EliminatePoss[ct_, key1_, key2_] := Module[{t = ct, poss1, poss2, poss, notposs},
poss1 = Position[t, key1];
poss2 = Position[t, key2];
poss = Intersection[Last /@ poss1, Last /@ poss2];
notposs = Complement[Range[5], poss];
poss1 = Select[poss1, MemberQ[notposs, Last[#]] &];
poss2 = Select[poss2, MemberQ[notposs, Last[#]] &];
t = ReplacePart[t, poss1 -> Null];
t = ReplacePart[t, poss2 -> Null];
t
]
FilterPuzzle[tbl_] := Module[{t = tbl, poss1, poss2, poss, notposs, rows, columns, vals, sets, delpos},
t = EliminatePoss[t, "English", "Red"]; (*2. The English man lives in the red house. *)
t = EliminatePoss[t, "Swede", "Dog"]; (* 3. The Swede has a dog. *)
t = EliminatePoss[t, "Dane", "Tea"]; (* 4. The Dane drinks tea. *)
t = EliminatePoss[t, "Green", "Coffee"]; (* 6. They drink coffee in the green house. *)
t = EliminatePoss[t, "Pall Mall", "Birds"]; (* 7. The man who smokes Pall Mall has birds.*)
t = EliminatePoss[t, "Yellow", "Dunhill"]; (* 8. In the yellow house they smoke Dunhill. *)
t = EliminatePoss[t, "Blue Master", "Beer"]; (*13. The man who smokes Blue Master drinks beer. *)
t = EliminatePoss[t, "German", "Prince"]; (* 14. The German smokes Prince. *)
(* 9. In the middle house they drink milk. *)
poss = Position[t, "Milk"];
delpos = Select[poss, #[[2]] != 3 &];
t = ReplacePart[t, delpos -> Null];
(* 10. The Norwegian lives in the first house. *)
poss = Position[t, "Norwegian"];
delpos = Select[poss, #[[2]] != 1 &];
t = ReplacePart[t, delpos -> Null];
(* 15. The Norwegian lives next to the blue house.*)
poss1 = Position[t, "Norwegian"];
poss2 = Position[t, "Blue"];
poss = Tuples[{poss1, poss2}];
poss = Select[poss, #[[1, 2]] + 1 == #[[2, 2]] \[Or] #[[1, 2]] - 1 == #[[2, 2]] &]\[Transpose];
delpos = Complement[poss1, poss[[1]]];
t = ReplacePart[t, delpos -> Null];
delpos = Complement[poss2, poss[[2]]];
t = ReplacePart[t, delpos -> Null];
(* 5. The green house is immediately to the left of the white house. *)
poss1 = Position[t, "Green"];
poss2 = Position[t, "White"];
poss = Tuples[{poss1, poss2}];
poss = Select[poss, #[[1, 2]] + 1 == #[[2, 2]] &]\[Transpose];
delpos = Complement[poss1, poss[[1]]];
t = ReplacePart[t, delpos -> Null];
delpos = Complement[poss2, poss[[2]]];
t = ReplacePart[t, delpos -> Null];
(*11. The man who smokes Blend lives in the house next to the house with cats.*)
poss1 = Position[t, "Blend"];
poss2 = Position[t, "Cats"];
poss = Tuples[{poss1, poss2}];
poss = Select[poss, #[[1, 2]] + 1 == #[[2, 2]] \[Or] #[[1, 2]] - 1 == #[[2, 2]] &]\[Transpose];
delpos = Complement[poss1, poss[[1]]];
t = ReplacePart[t, delpos -> Null];
delpos = Complement[poss2, poss[[2]]];
t = ReplacePart[t, delpos -> Null];
(* 12. In a house next to the house where they have a horse, they smoke Dunhill. *)
poss1 = Position[t, "Horse"];
poss2 = Position[t, "Dunhill"];
poss = Tuples[{poss1, poss2}];
poss = Select[poss, #[[1, 2]] + 1 == #[[2, 2]] \[Or] #[[1, 2]] - 1 == #[[2, 2]] &]\[Transpose];
delpos = Complement[poss1, poss[[1]]];
t = ReplacePart[t, delpos -> Null];
delpos = Complement[poss2, poss[[2]]];
t = ReplacePart[t, delpos -> Null];
(* 16. They drink water in a house next to the house where they smoke Blend. *)
poss1 = Position[t, "Water"];
poss2 = Position[t, "Blend"];
poss = Tuples[{poss1, poss2}];
poss = Select[poss, #[[1, 2]] + 1 == #[[2, 2]] \[Or] #[[1, 2]] - 1 == #[[2, 2]] &]\[Transpose];
delpos = Complement[poss1, poss[[1]]];
t = ReplacePart[t, delpos -> Null];
delpos = Complement[poss2, poss[[2]]];
t = ReplacePart[t, delpos -> Null];
(*General rule 1 in a line => cross out vertical and horizontal lines*)
(* 1 in a row*)
vals = Select[t, Count[#, Null] == 4 &];
vals = DeleteCases[Flatten[vals], Null];
poss = Flatten[Position[t, #] & /@ vals, 1];
delpos = With[{r = First[#], c = Last[#]}, {#, c} & /@ (Range[-4, 0] + Ceiling[r, 5])] & /@ poss; (*delete in columns*)
delpos = Flatten[MapThread[DeleteCases, {delpos, poss}], 1];
t = ReplacePart[t, delpos -> Null];
(* 1 in a column*)
sets = Flatten[Table[{i + k*5, j}, {k, 0, 4}, {j, 1, 5}, {i, 1, 5}],1];
sets = {#, Extract[t, #]} & /@ sets;
sets = Select[sets, Count[#[[2]], Null] == 4 &];
sets = Flatten[Transpose /@ sets, 1];
sets = DeleteCases[sets, {{_, _}, Null}];
delpos = sets[[All, 1]];(*delete in rows*)
delpos = With[{r = First[#], c = Last[#]}, {r, #} & /@ (DeleteCases[Range[5], c])] & /@ delpos;
delpos = Flatten[delpos, 1];
t = ReplacePart[t, delpos -> Null];
t
]
colors = {"Blue", "Green", "Red", "White", "Yellow"};
nationality = {"Dane", "English", "German", "Norwegian", "Swede"};
beverage = {"Beer", "Coffee", "Milk", "Tea", "Water"};
animal = {"Birds", "Cats", "Dog", "Horse", "Zebra"};
smoke = {"Blend", "Blue Master", "Dunhill", "Pall Mall", "Prince"};
vals = {colors, nationality, beverage, animal, smoke};
bigtable = Join @@ (ConstantArray[#, 5]\[Transpose] & /@ vals);
bigtable = FixedPoint[FilterPuzzle, bigtable];
TableForm[DeleteCases[bigtable\[Transpose], Null, \[Infinity]], TableHeadings -> {Range[5], None}]

View file

@ -5,17 +5,17 @@ next_to(A,B,C):- left_of(A,B,C) ; left_of(B,A,C).
left_of(A,B,C):- append(_,[A,B|_],C).
zebra(Owns, HS):- % color,nation,pet,drink,smokes
HS = [h(_,norwegian,_,_,_),_, h(_,_,_,milk,_),_,_],
select( [h(red,englishman,_,_,_),h(_,swede,dog,_,_),
h(_,dane,_,tea,_), h(_,german,_,_,prince)], HS),
select( [h(_,_,birds,_,pallmall),h(yellow,_,_,_,dunhill),
h(_,_,_,beer,bluemaster)], HS),
left_of( h(green,_,_,coffee,_), h(white,_,_,_,_), HS),
next_to( h(_,_,_,_,dunhill), h(_,_,horse,_,_), HS),
next_to( h(_,_,_,_,blend), h(_,_,cats, _,_), HS),
next_to( h(_,_,_,_,blend), h(_,_,_,water,_), HS),
next_to( h(_,norwegian,_,_,_), h(blue,_,_,_,_), HS),
member( h(_,Owns,zebra,_,_), HS).
HS = [ h(_,norwegian,_,_,_), _, h(_,_,_,milk,_), _, _],
select( [ h(red,englishman,_,_,_), h(_,swede,dog,_,_),
h(_,dane,_,tea,_), h(_,german,_,_,prince) ], HS),
select( [ h(_,_,birds,_,pallmall), h(yellow,_,_,_,dunhill),
h(_,_,_,beer,bluemaster) ], HS),
left_of( h(green,_,_,coffee,_), h(white,_,_,_,_), HS),
next_to( h(_,_,_,_,dunhill), h(_,_,horse,_,_), HS),
next_to( h(_,_,_,_,blend), h(_,_,cats, _,_), HS),
next_to( h(_,_,_,_,blend), h(_,_,_,water,_), HS),
next_to( h(_,norwegian,_,_,_), h(blue,_,_,_,_), HS),
member( h(_,Owns,zebra,_,_), HS).
:- ?- time(( zebra(Who, HS), maplist(writeln,HS), nl, write(Who), nl, nl, fail
; write('No more solutions.') )).

View file

@ -1,30 +1,28 @@
% populate domain by selecting from it
nation(H,V):- memberchk( nation(X), H), X=V. % select the "nation" attribute
owns( H,V):- memberchk( owns( X), H), X=V. % ...
smoke( H,V):- memberchk( smoke( X), H), X=V.
color( H,V):- memberchk( color( X), H), X=V.
drink( H,V):- memberchk( drink( X), H), X=V.
to_the_left(A,B,HS):- append(_,[A,B|_],HS).
next_to(A,B,HS):- to_the_left(A,B,HS) ; to_the_left(B,A,HS).
middle(A, [_,_,A,_,_]).
first(A, [A|_]).
attrs(H,[N-V|R]):- memberchk( N-X, H), X=V, % unique attribute names
(R=[] -> true ; attrs(H,R)).
one_of(HS,AS) :- member(H,HS), attrs(H,AS).
two_of(HS,G,AS):- call(G,H1,H2,HS), maplist(attrs,[H1,H2],AS).
left_of(A,B,HS):- append(_,[A,B|_],HS).
next_to(A,B,HS):- left_of(A,B,HS) ; left_of(B,A,HS).
zebra(Zebra,Houses):-
length(Houses,5),
member(H2, Houses), nation(H2, englishman), color( H2, red),
member(H3, Houses), nation(H3, swede), owns( H3, dog),
member(H4, Houses), nation(H4, dane), drink( H4, tea),
to_the_left(H5,H5b,Houses), color(H5, green), color(H5b, white),
member(H6, Houses), drink( H6, coffee), color( H6, green),
member(H7, Houses), smoke( H7, 'Pall Mall'), owns( H7, birds),
member(H8, Houses), color( H8, yellow), smoke( H8, 'Dunhill'),
middle(H9, Houses), drink( H9, milk),
first(H10, Houses), nation(H10, norwegian),
next_to(H11,H11b,Houses), smoke( H11, 'Blend'), owns( H11b, cats),
next_to(H12,H12b,Houses), owns( H12, horse), smoke(H12b, 'Dunhill'),
member(H13, Houses), drink( H13, beer), smoke( H13, 'Blue Master'),
member(H14, Houses), nation(H14, german), smoke( H14, 'Prince'),
next_to(H15,H15b,Houses), nation(H15, norwegian), color(H15b, blue),
next_to(H16,H16b,Houses), drink( H16, water), smoke(H16b, 'Blend'),
member(Zebra,Houses), owns(Zebra, zebra).
Houses = [A,_,C,_,_], % 1
maplist( one_of(Houses), [ [ nation-englishman, color-red ] % 2
, [ nation-swede, owns -dog ] % 3
, [ nation-dane, drink-tea ] % 4
, [ drink -coffee, color-green ] % 6
, [ smoke -'Pall Mall', owns -birds ] % 7
, [ color -yellow, smoke-'Dunhill' ] % 8
, [ drink -beer, smoke-'Blue Master'] % 13
, [ nation-german, smoke-'Prince' ] % 14
] ),
two_of(Houses, left_of, [[color -green ], [color -white ]]), % 5
maplist(attrs, [C,A], [[drink -milk ], [nation-norwegian]]), % 9, 10
maplist(two_of(Houses,next_to),
[ [[smoke -'Blend' ], [owns -cats ]] % 11
, [[owns -horse ], [smoke-'Dunhill' ]] % 12
, [[nation-norwegian], [color-blue ]] % 15
, [[drink -water ], [smoke-'Blend' ]] % 16
] ),
one_of(Houses, [ owns-zebra, nation-Zebra]).

View file

@ -1,12 +1,12 @@
?- time(( zebra(Z,HS), ( maplist(length,HS,_) -> maplist(sort,HS,S),
maplist(writeln,S),nation(Z,R),nl,writeln(R) ), false ; true)).
maplist(writeln,S),nl,writeln(Z) ), false ; true)).
[color(yellow),drink(water), nation(norwegian), owns(cats), smoke(Dunhill) ]
[color(blue), drink(tea), nation(dane), owns(horse), smoke(Blend) ]
[color(red), drink(milk), nation(englishman),owns(birds), smoke(Pall Mall) ]
[color(green), drink(coffee),nation(german), owns(zebra), smoke(Prince) ]
[color(white), drink(beer), nation(swede), owns(dog), smoke(Blue Master)]
[color-yellow,drink-water, nation-norwegian, owns-cats, smoke-Dunhill ]
[color-blue, drink-tea, nation-dane, owns-horse, smoke-Blend ]
[color-red, drink-milk, nation-englishman,owns-birds, smoke-Pall Mall ]
[color-green, drink-coffee,nation-german, owns-zebra, smoke-Prince ]
[color-white, drink-beer, nation-swede, owns-dog, smoke-Blue Master]
german
% 138,899 inferences, 0.060 CPU in 0.110 seconds (55% CPU, 2311655 Lips)
% 234,852 inferences, 0.100 CPU in 0.170 seconds (59% CPU, 2345143 Lips)
true.

View file

@ -0,0 +1,45 @@
:- initialization(main).
zebra(X) :-
houses(Hs), member(h(_,X,zebra,_,_), Hs)
, findall(_, (member(H,Hs), write(H), nl), _), nl
, write('the one who keeps zebra: '), write(X), nl
.
houses(Hs) :-
Hs = [_,_,_,_,_] % 1
, H3 = h(_,_,_,milk,_), Hs = [_,_,H3,_,_] % 9
, H1 = h(_,nvg,_,_,_ ), Hs = [H1|_] % 10
, maplist( flip(member,Hs),
[ h(red,eng,_,_,_) % 2
, h(_,swe,dog,_,_) % 3
, h(_,dan,_,tea,_) % 4
, h(green,_,_,coffe,_) % 6
, h(_,_,birds,_,pm) % 7
, h(yellow,_,_,_,dh) % 8
, h(_,_,_,beer,bm) % 13
, h(_,ger,_,_,pri) % 14
])
, infix([ h(green,_,_,_,_)
, h(white,_,_,_,_) ], Hs) % 5
, maplist( flip(nextto,Hs),
[ [h(_,_,_,_,bl ), h(_,_,cats,_,_)] % 11
, [h(_,_,horse,_,_), h(_,_,_,_,dh )] % 12
, [h(_,nvg,_,_,_ ), h(blue,_,_,_,_)] % 15
, [h(_,_,_,water,_), h(_,_,_,_,bl )] % 16
])
.
flip(F,X,Y) :- call(F,Y,X).
infix(Xs,Ys) :- append(Xs,_,Zs) , append(_,Zs,Ys).
nextto(P,Xs) :- permutation(P,R), infix(R,Xs).
main :- findall(_, (zebra(_), nl), _), halt.

View file

@ -0,0 +1,80 @@
library(combinat)
col <- factor(c("Red","Green","White","Yellow","Blue"))
own <- factor(c("English","Swedish","Danish","German","Norwegian"))
pet <- factor(c("Dog","Birds","Cats","Horse","Zebra"))
drink <- factor(c("Coffee","Tea","Milk","Beer","Water"))
smoke <- factor(c("PallMall", "Blend", "Dunhill", "BlueMaster", "Prince"))
col_p <- permn(levels(col))
own_p <- permn(levels(own))
pet_p <- permn(levels(pet))
drink_p <- permn(levels(drink))
smoke_p <- permn(levels(smoke))
imright <- function(h1,h2){
return(h1-h2==1)
}
nextto <- function(h1,h2){
return(abs(h1-h2)==1)
}
house_with <- function(f,val){
return(which(levels(f)==val))
}
for (i in seq(length(col_p))){
col <- factor(col, levels=col_p[[i]])
if (imright(house_with(col,"Green"),house_with(col,"White"))) {
for (j in seq(length(own_p))){
own <- factor(own, levels=own_p[[j]])
if(house_with(own,"English") == house_with(col,"Red")){
if(house_with(own,"Norwegian") == 1){
if(nextto(house_with(own,"Norwegian"),house_with(col,"Blue"))){
for(k in seq(length(drink_p))){
drink <- factor(drink, levels=drink_p[[k]])
if(house_with(drink,"Coffee") == house_with(col,"Green")){
if(house_with(own,"Danish") == house_with(drink,"Tea")){
if(house_with(drink,"Milk") == 3){
for(l in seq(length(smoke_p))){
smoke <- factor(smoke, levels=smoke_p[[l]])
if(house_with(smoke,"Dunhill") == house_with(col,"Yellow")){
if(house_with(smoke,"BlueMaster") == house_with(drink,"Beer")){
if(house_with(own,"German") == house_with(smoke,"Prince")){
if(nextto(house_with(smoke,"Blend"),house_with(drink,"Water"))){
for(m in seq(length(pet_p))){
pet <- factor(pet, levels=pet_p[[m]])
if(house_with(own,"Swedish") == house_with(pet,"Dog")){
if(house_with(smoke,"PallMall") == house_with(pet,"Birds")){
if(nextto(house_with(smoke,"Blend"),house_with(pet,"Cats"))){
if(nextto(house_with(smoke,"Dunhill"),house_with(pet,"Horse"))){
res <- sapply(list(own,col,pet,smoke,drink),levels)
colnames(res) <- c("Nationality","Colour","Pet","Drink","Smoke")
print(res)
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}

View file

@ -0,0 +1,53 @@
# Solve Zebra Puzzle.
#
# Nigel_Galloway
# August 31st., 2014.
CONTENT = { :House => nil,
:Nationality => [:English, :Swedish, :Danish, :Norwegian, :German],
:Colour => [:Red, :Green, :White, :Blue, :Yellow],
:Pet => [:Dog, :Birds, :Cats, :Horse, :Zebra],
:Drink => [:Tea, :Coffee, :Milk, :Beer, :Water],
:Smoke => [:PallMall, :Dunhill, :BlueMaster, :Prince, :Blend] }
def adjacent? (n,i,g,e)
(0..3).any?{|x| (n[x]==i and g[x+1]==e) or (n[x+1]==i and g[x]==e)}
end
def leftof? (n,i,g,e)
(0..3).any?{|x| n[x]==i and g[x+1]==e}
end
def coincident? (n,i,g,e)
n.each_index.any?{|x| n[x]==i and g[x]==e}
end
def solve
CONTENT[:Nationality].permutation{|nation|
next if nation.first != :Norwegian # 10
CONTENT[:Colour].permutation{|colour|
next unless leftof?(colour,:Green,colour,:White) # 5
next unless coincident?(nation,:English,colour,:Red) # 2
next unless adjacent?(nation,:Norwegian,colour,:Blue) # 15
CONTENT[:Pet].permutation{|pet|
next unless coincident?(nation,:Swedish,pet,:Dog) # 3
CONTENT[:Drink].permutation{|drink|
next if drink[2] != :Milk # 9
next unless coincident?(nation,:Danish,drink,:Tea) # 4
next unless coincident?(colour,:Green,drink,:Coffee) # 6
CONTENT[:Smoke].permutation{|smoke|
next unless coincident?(smoke,:PallMall,pet,:Birds) # 7
next unless coincident?(smoke,:Dunhill,colour,:Yellow) # 8
next unless coincident?(smoke,:BlueMaster,drink,:Beer) # 13
next unless coincident?(smoke,:Prince,nation,:German) # 14
next unless adjacent?(smoke,:Blend,pet,:Cats) # 11
next unless adjacent?(smoke,:Blend,drink,:Water) # 16
next unless adjacent?(smoke,:Dunhill,pet,:Horse) # 12
return [nation,colour,pet,drink,smoke]
} } } } }
end
res = solve
width = CONTENT.map{|x| x.flatten.map{|y|y.to_s.size}.max}
fmt = width.map{|w| "%-#{w}s"}.join(" ")
puts "The Zebra is owned by the man who is #{res[0][res[2].find_index(:Zebra)]}",""
puts fmt % CONTENT.keys, fmt % width.map{|w| "-"*w}
res.transpose.each.with_index(1){|x,n| puts fmt % [n,*x]}

View file

@ -0,0 +1,76 @@
/* Note to the rules:
*
* It can further concluded that:
* 5a: The green house cannot be at the h1 position
* 5b: The white house cannot be at the h5 position
*
* 16: This rule is redundant.
*/
object Einstein extends App {
class House(val nationality: String, val color: String, val beverage: String, val animal: String, val brand: String) {
override def toString = { f"$nationality%10s, ${color + ", "}%-8s$beverage,\t$animal,\t$brand." }
def totalUnEqual(home2: House) =
this.animal != home2.animal &&
this.beverage != home2.beverage &&
this.brand != home2.brand &&
this.color != home2.color &&
this.nationality != home2.nationality
//** Checks if the this green house is next to the other white house*/
def checkAdjacentWhite(home2: House) = (this.color == "Green") == (home2.color == "White") // #5
}
val possibleMembers = for { // pair clues results in 78 members
nationality <- List("Norweigan", "German", "Dane", "Englishman", "Swede")
color <- List("Red", "Green", "Yellow", "White", "Blue")
beverage <- List("Milk", "Coffee", "Tea", "Beer", "Water")
animal <- List("Dog", "Horse", "Birds", "Cats", "Zebra")
brand <- List("Blend", "Pall Mall", "Prince", "Blue Master", "Dunhill")
if (color == "Red") == (nationality == "Englishman") // #2
if (nationality == "Swede") == (animal == "Dog") // #3
if (nationality == "Dane") == (beverage == "Tea") // #4
if (color == "Green") == (beverage == "Coffee") // #6
if (brand == "Pall Mall") == (animal == "Birds") // #7
if (brand == "Dunhill") == (color == "Yellow") // #8
if (brand == "Blue Master") == (beverage == "Beer") // #13
if (brand == "Prince") == (nationality == "German") // #14
} yield new House(nationality, color, beverage, animal, brand)
def matchMiddleBrandAnimal(home1: House, home2: House, home3: House, brand: String, animal: String) =
(home1.animal == animal || home2.brand != brand || home3.animal == animal) &&
(home1.brand == brand || home2.animal != animal || home3.brand == brand)
def matchCornerBrandAnimal(corner: House, inner: House, animal: String, brand: String) =
(corner.brand != brand || inner.animal == animal) && (corner.animal == animal || inner.brand != brand)
def housesLeftOver(pickedHouses: House*): List[House] = {
possibleMembers.filter(house => pickedHouses.forall(_.totalUnEqual(house)))
}
val members = for { // Neighborhood clues
h1 <- housesLeftOver().filter(p => (p.nationality == "Norweigan" /* #10 */ ) && (p.color != "Green") /* #5a */ ) // 28
h3 <- housesLeftOver(h1).filter(p => p.beverage == "Milk") // #9 // 24
h2 <- housesLeftOver(h1, h3).filter(_.color == "Blue") // #15
if matchMiddleBrandAnimal(h1, h2, h3, "Blend", "Cats") // #11
if matchCornerBrandAnimal(h1, h2, "Horse", "Dunhill") // #12
h4 <- housesLeftOver(h1, h2, h3).filter(_.checkAdjacentWhite(h3) /* #5 */ )
h5 <- housesLeftOver(h1, h2, h3, h4)
// Redundant tests
if h2.checkAdjacentWhite(h1)
if h3.checkAdjacentWhite(h2)
if matchCornerBrandAnimal(h5, h4, "Horse", "Dunhill")
if matchMiddleBrandAnimal(h2, h3, h4, "Blend", "Cats")
if matchMiddleBrandAnimal(h3, h4, h5, "Blend", "Cats")
} yield Seq(h1, h2, h3, h4, h5)
// Main program
val beest = "Zebra"
members.flatMap(p => p.filter(p => p.animal == beest)).
foreach(s => println(s"The ${s.nationality} is the owner of the ${beest.toLowerCase}."))
println(s"The ${members.size} solution(s) are:")
members.foreach(solution => solution.zipWithIndex.foreach(h => println("House " + (h._2 + 1) + " " + h._1)))
}