all tasks

This commit is contained in:
Ingy döt Net 2013-04-11 01:07:29 -07:00
parent b83f433714
commit 68f8f3e56b
14735 changed files with 178959 additions and 0 deletions

View file

@ -0,0 +1,24 @@
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:
#There are five houses.
#The English man lives in the red house.
#The Swede has a dog.
#The Dane drinks tea.
#The green house is immediately to the left of the white house.
#They drink coffee in the green house.
#The man who smokes Pall Mall has birds.
#In the yellow house they smoke Dunhill.
#In the middle house they drink milk.
#The Norwegian lives in the first house.
#The man who smokes Blend lives in the house next to the house with cats.
#In a house next to the house where they have a horse, they smoke Dunhill.
#The man who smokes Blue Master drinks beer.
#The German smokes Prince.
#The Norwegian lives next to the blue house.
#They drink water in a house next to the house where they smoke Blend.
The question is, who owns the zebra?
Additionally, list the solution for all the houses. Optionally, show the solution is unique.
cf. [[Dinesman's multiple-dwelling problem]]

View file

@ -0,0 +1,2 @@
---
note: Logic puzzles

View file

@ -0,0 +1,106 @@
with Ada.Text_IO; use Ada.Text_IO;
procedure Zebra is
type Content is (Beer, Coffee, Milk, Tea, Water,
Danish, English, German, Norwegian, Swedish,
Blue, Green, Red, White, Yellow,
Blend, BlueMaster, Dunhill, PallMall, Prince,
Bird, Cat, Dog, Horse, Zebra);
type Test is (Drink, Person, Color, Smoke, Pet);
type House is (One, Two, Three, Four, Five);
type Street is array (Test'Range, House'Range) of Content;
type Alley is access all Street;
procedure Print (mat : Alley) is begin
for H in House'Range loop
Put(H'Img&": ");
for T in Test'Range loop
Put(T'Img&"="&mat(T,H)'Img&" ");
end loop; New_Line; end loop;
end Print;
function FinalChecks (mat : Alley) return Boolean is
function Diff (A, B : Content; CA , CB : Test) return Integer is begin
for H1 in House'Range loop for H2 in House'Range loop
if mat(CA,H1) = A and mat(CB,H2) = B then
return House'Pos(H1) - House'Pos(H2);
end if;
end loop; end loop;
end Diff;
begin
if abs(Diff(Norwegian, Blue, Person, Color)) = 1
and Diff(Green, White, Color, Color) = -1
and abs(Diff(Horse, Dunhill, Pet, Smoke)) = 1
and abs(Diff(Water, Blend, Drink, Smoke)) = 1
and abs(Diff(Blend, Cat, Smoke, Pet)) = 1
then return True;
end if;
return False;
end FinalChecks;
function Constrained (mat : Alley; atest : Natural) return Boolean is begin
-- Tests seperated into levels for speed, not strictly necessary
-- As such, the program finishes in around ~0.02s
case Test'Val (atest) is
when Drink => -- Drink
if mat (Drink, Three) /= Milk then return False; end if;
return True;
when Person => -- Drink+Person
for H in House'Range loop
if (mat(Person,H) = Norwegian and H /= One)
or (mat(Person,H) = Danish and mat(Drink,H) /= Tea)
then return False; end if;
end loop;
return True;
when Color => -- Drink+People+Color
for H in House'Range loop
if (mat(Person,H) = English and mat(Color,H) /= Red)
or (mat(Drink,H) = Coffee and mat(Color,H) /= Green)
then return False; end if;
end loop;
return True;
when Smoke => -- Drink+People+Color+Smoke
for H in House'Range loop
if (mat(Color,H) = Yellow and mat(Smoke,H) /= Dunhill)
or (mat(Smoke,H) = BlueMaster and mat(Drink,H) /= Beer)
or (mat(Person,H) = German and mat(Smoke,H) /= Prince)
then return False; end if;
end loop;
return True;
when Pet => -- Drink+People+Color+Smoke+Pet
for H in House'Range loop
if (mat(Person,H) = Swedish and mat(Pet,H) /= Dog)
or (mat(Smoke,H) = PallMall and mat(Pet,H) /= Bird)
then return False; end if;
end loop;
return FinalChecks(mat); -- Do the next-to checks
end case;
end Constrained;
procedure Solve (mat : Alley; t, n : Natural) is
procedure Swap (I, J : Natural) is
temp : constant Content := mat (Test'Val (t), House'Val (J));
begin
mat (Test'Val (t), House'Val (J)) := mat (Test'Val (t), House'Val (I));
mat (Test'Val (t), House'Val (I)) := temp;
end Swap;
begin
if n = 1 and Constrained (mat, t) then -- test t passed
if t < 4 then Solve (mat, t + 1, 5); -- Onto next test
else Print (mat); return; -- Passed and t=4 means a solution
end if;
end if;
for i in 0 .. n - 1 loop -- The permutations part
Solve (mat, t, n - 1);
if n mod 2 = 1 then Swap (0, n - 1);
else Swap (i, n - 1); end if;
end loop;
end Solve;
myStreet : aliased Street;
myAlley : constant Alley := myStreet'Access;
begin
for i in Test'Range loop for j in House'Range loop -- Init Matrix
myStreet (i,j) := Content'Val(Test'Pos(i)*5 + House'Pos(j));
end loop; end loop;
Solve (myAlley, 0, 5); -- start at test 0 with 5 options
end Zebra;

View file

@ -0,0 +1,101 @@
REM The names (only used for printing the results):
DIM Drink$(4), Nation$(4), Colr$(4), Smoke$(4), Animal$(4)
Drink$() = "Beer", "Coffee", "Milk", "Tea", "Water"
Nation$() = "Denmark", "England", "Germany", "Norway", "Sweden"
Colr$() = "Blue", "Green", "Red", "White", "Yellow"
Smoke$() = "Blend", "BlueMaster", "Dunhill", "PallMall", "Prince"
Animal$() = "Birds", "Cats", "Dog", "Horse", "Zebra"
REM Some single-character tags:
a$ = "A" : b$ = "B" : c$ = "C" : d$ = "D" : e$ = "E"
REM BBC BASIC Doesn't have enumerations!
Beer$=a$ : Coffee$=b$ : Milk$=c$ : Tea$=d$ : Water$=e$
Denmark$=a$ : England$=b$ : Germany$=c$ : Norway$=d$ : Sweden$=e$
Blue$=a$ : Green$=b$ : Red$=c$ : White$=d$ : Yellow$=e$
Blend$=a$ : BlueMaster$=b$ : Dunhill$=c$ : PallMall$=d$ : Prince$=e$
Birds$=a$ : Cats$=b$ : Dog$=c$ : Horse$=d$ : Zebra$=e$
REM Create the 120 permutations of 5 objects:
DIM perm$(120), x$(4) : x$() = a$, b$, c$, d$, e$
REPEAT
p% += 1
perm$(p%) = x$(0)+x$(1)+x$(2)+x$(3)+x$(4)
UNTIL NOT FNperm(x$())
REM Express the statements as conditional expressions:
ex2$ = "INSTR(Nation$,England$) = INSTR(Colr$,Red$)"
ex3$ = "INSTR(Nation$,Sweden$) = INSTR(Animal$,Dog$)"
ex4$ = "INSTR(Nation$,Denmark$) = INSTR(Drink$,Tea$)"
ex5$ = "INSTR(Colr$,Green$+White$) <> 0"
ex6$ = "INSTR(Drink$,Coffee$) = INSTR(Colr$,Green$)"
ex7$ = "INSTR(Smoke$,PallMall$) = INSTR(Animal$,Birds$)"
ex8$ = "INSTR(Smoke$,Dunhill$) = INSTR(Colr$,Yellow$)"
ex9$ = "MID$(Drink$,3,1) = Milk$"
ex10$ = "LEFT$(Nation$,1) = Norway$"
ex11$ = "ABS(INSTR(Smoke$,Blend$)-INSTR(Animal$,Cats$)) = 1"
ex12$ = "ABS(INSTR(Smoke$,Dunhill$)-INSTR(Animal$,Horse$)) = 1"
ex13$ = "INSTR(Smoke$,BlueMaster$) = INSTR(Drink$,Beer$)"
ex14$ = "INSTR(Nation$,Germany$) = INSTR(Smoke$,Prince$)"
ex15$ = "ABS(INSTR(Nation$,Norway$)-INSTR(Colr$,Blue$)) = 1"
ex16$ = "ABS(INSTR(Smoke$,Blend$)-INSTR(Drink$,Water$)) = 1"
REM Solve:
solutions% = 0
TIME = 0
FOR nation% = 1 TO 120
Nation$ = perm$(nation%)
IF EVAL(ex10$) THEN
FOR colr% = 1 TO 120
Colr$ = perm$(colr%)
IF EVAL(ex5$) IF EVAL(ex2$) IF EVAL(ex15$) THEN
FOR drink% = 1 TO 120
Drink$ = perm$(drink%)
IF EVAL(ex9$) IF EVAL(ex4$) IF EVAL(ex6$) THEN
FOR smoke% = 1 TO 120
Smoke$ = perm$(smoke%)
IF EVAL(ex14$) IF EVAL(ex13$) IF EVAL(ex16$) IF EVAL(ex8$) THEN
FOR animal% = 1 TO 120
Animal$ = perm$(animal%)
IF EVAL(ex3$) IF EVAL(ex7$) IF EVAL(ex11$) IF EVAL(ex12$) THEN
PRINT "House Drink Nation Colour Smoke Animal"
FOR house% = 1 TO 5
PRINT ; house% ,;
PRINT Drink$(ASCMID$(Drink$,house%)-65),;
PRINT Nation$(ASCMID$(Nation$,house%)-65),;
PRINT Colr$(ASCMID$(Colr$,house%)-65),;
PRINT Smoke$(ASCMID$(Smoke$,house%)-65),;
PRINT Animal$(ASCMID$(Animal$,house%)-65)
NEXT
solutions% += 1
ENDIF
NEXT animal%
ENDIF
NEXT smoke%
ENDIF
NEXT drink%
ENDIF
NEXT colr%
ENDIF
NEXT nation%
PRINT '"Number of solutions = "; solutions%
PRINT "Solved in " ; TIME/100 " seconds"
END
DEF FNperm(x$())
LOCAL i%, j%
FOR i% = DIM(x$(),1)-1 TO 0 STEP -1
IF x$(i%) < x$(i%+1) EXIT FOR
NEXT
IF i% < 0 THEN = FALSE
j% = DIM(x$(),1)
WHILE x$(j%) <= x$(i%) j% -= 1 : ENDWHILE
SWAP x$(i%), x$(j%)
i% += 1
j% = DIM(x$(),1)
WHILE i% < j%
SWAP x$(i%), x$(j%)
i% += 1
j% -= 1
ENDWHILE
= TRUE

View file

@ -0,0 +1,228 @@
#include <stdio.h>
#include <string.h>
enum HouseStatus {Invalid, Underfull, Valid};
enum Attrib {C, M, D, A, S};
// Unfilled attributes are represented by -1
enum Colors {Red, Green, White, Yellow, Blue};
enum Mans {English, Swede, Dane, German, Norwegian};
enum Drinks {Tea, Coffee, Milk, Beer, Water};
enum Animals {Dog, Birds, Cats, Horse, Zebra};
enum Smokes {PallMall, Dunhill, Blend, BlueMaster, Prince};
void printHouses(int ha[5][5])
{
const char *Color [] = {"Red", "Green", "White", "Yellow", "Blue"};
const char *Man [] = {"English", "Swede", "Dane", "German", "Norwegian"};
const char *Drink [] = {"Tea", "Coffee", "Milk", "Beer", "Water"};
const char *Animal[] = {"Dog", "Birds", "Cats", "Horse", "Zebra"};
const char *Smoke [] = {"PallMall", "Dunhill", "Blend", "BlueMaster", "Prince"};
printf("%-10.10s%-10.10s%-10.10s%-10.10s%-10.10s%-10.10s\n",
"House", "Color", "Man", "Drink", "Animal", "Smoke");
for(int i=0; i<5; i++) {
printf("%-10d", i);
if (ha[i][C]>=0)
printf("%-10.10s", Color[ha[i][C]]);
else
printf("%-10.10s", "-");
if (ha[i][M]>=0)
printf("%-10.10s", Man[ha[i][M]]);
else
printf("%-10.10s", "-");
if (ha[i][D]>=0)
printf("%-10.10s", Drink[ha[i][D]]);
else
printf("%-10.10s", "-");
if (ha[i][A]>=0)
printf("%-10.10s", Animal[ha[i][A]]);
else
printf("%-10.10s", "-");
if (ha[i][S]>=0)
printf("%-10.10s\n", Smoke[ha[i][S]]);
else
printf("-\n");
}
}
int checkHouses(int ha[5][5])
{
int c_add=0, c_or=0;
int m_add=0, m_or=0;
int d_add=0, d_or=0;
int a_add=0, a_or=0;
int s_add=0, s_or=0;
// Cond 9: In the middle house they drink milk
if ( ha[2][D] >= 0 && ha[2][D] != Milk )
return Invalid;
// Cond 10: The Norwegian lives in the first house
if ( ha[0][M] >= 0 && ha[0][M] != Norwegian )
return Invalid;
for (int i=0; i<5; i++) {
// Uniqueness tests
if (ha[i][C] >= 0) {
c_add += (1<<ha[i][C]);
c_or |= (1<<ha[i][C]);
}
if (ha[i][M] >= 0) {
m_add += (1<<ha[i][M]);
m_or |= (1<<ha[i][M]);
}
if (ha[i][D] >= 0) {
d_add += (1<<ha[i][D]);
d_or |= (1<<ha[i][D]);
}
if (ha[i][A] >= 0) {
a_add += (1<<ha[i][A]);
a_or |= (1<<ha[i][A]);
}
if (ha[i][S] >= 0) {
s_add += (1<<ha[i][S]);
s_or |= (1<<ha[i][S]);
}
// Cond 2: The English man lives in the red house
if ( ( ha[i][M] >= 0 && ha[i][C] >= 0 ) &&
( (ha[i][M] == English && ha[i][C] != Red ) || // checking both
(ha[i][M] != English && ha[i][C] == Red ) ) ) // to make things quicker
return Invalid;
// Cond 3: The Swede has a dog
if ( ( ha[i][M] >= 0 && ha[i][A] >= 0 ) &&
((ha[i][M] == Swede && ha[i][A] != Dog )||
(ha[i][M] != Swede && ha[i][A] == Dog ) ) )
return Invalid;
// Cond 4: The Dane drinks tea
if ( ( ha[i][M] >= 0 && ha[i][D] >= 0 ) &&
((ha[i][M] == Dane && ha[i][D] != Tea )||
(ha[i][M] != Dane && ha[i][D] == Tea ) ) )
return Invalid;
// Cond 5: The green house is immediately to the left of the white house
if ( ( i>0 && ha[i][C] >= 0 /*&& ha[i-1][C] >= 0 */) &&
((ha[i-1][C] == Green && ha[i][C] != White )||
(ha[i-1][C] != Green && ha[i][C] == White ) ) )
return Invalid;
// Cond 6: drink coffee in the green house
if ( ( ha[i][C] >= 0 && ha[i][D] >= 0 ) &&
((ha[i][C] == Green && ha[i][D] != Coffee )||
(ha[i][C] != Green && ha[i][D] == Coffee ) ) )
return Invalid;
// Cond 7: The man who smokes Pall Mall has birds
if ( ( ha[i][S] >= 0 && ha[i][A] >= 0 ) &&
((ha[i][S] == PallMall && ha[i][A] != Birds )||
(ha[i][S] != PallMall && ha[i][A] == Birds ) ) )
return Invalid;
// Cond 8: In the yellow house they smoke Dunhill
if ( ( ha[i][S] >= 0 && ha[i][C] >= 0 ) &&
((ha[i][S] == Dunhill && ha[i][C] != Yellow )||
(ha[i][S] != Dunhill && ha[i][C] == Yellow ) ) )
return Invalid;
// Cond 11: The man who smokes Blend lives in the house next to the house with cats
if (ha[i][S] == Blend) {
if (i==0 && ha[i+1][A]>=0&&ha[i+1][A]!=Cats)
return Invalid;
else if (i==4 && ha[i-1][A]!=Cats)
return Invalid;
else if (ha[i+1][A]>=0&&ha[i+1][A]!=Cats&&ha[i-1][A]!=Cats)
return Invalid;
}
// Cond 12: In a house next to the house where they have a horse, they smoke Dunhill
if (ha[i][S] == Dunhill) {
if (i==0 && ha[i+1][A]>=0&&ha[i+1][A]!=Horse)
return Invalid;
else if (i==4 && ha[i-1][A]!=Horse)
return Invalid;
else if (ha[i+1][A]>=0&&ha[i+1][A]!=Horse&&ha[i-1][A]!=Horse)
return Invalid;
}
// Cond 13: The man who smokes Blue Master drinks beer
if ( ( ha[i][S] >= 0 && ha[i][D] >= 0 ) &&
((ha[i][S] == BlueMaster && ha[i][D] != Beer )||
(ha[i][S] != BlueMaster && ha[i][D] == Beer ) ) )
return Invalid;
// Cond 14: The German smokes Prince
if ( ( ha[i][M] >= 0 && ha[i][S] >= 0 ) &&
((ha[i][M] == German && ha[i][S] != Prince )||
(ha[i][M] != German && ha[i][S] == Prince ) ) )
return Invalid;
// Cond 15: The Norwegian lives next to the blue house
if ( ha[i][M] == Norwegian &&
((i<4&&ha[i+1][C]>=0&&ha[i+1][C]!=Blue) ||
(i>0&&ha[i-1][C]!=Blue)))
return Invalid;
// Cond 16: They drink water in a house next to the house where they smoke Blend.
if (ha[i][S] == Blend) {
if (i==0 && ha[i+1][D]>=0&&ha[i+1][D]!=Water)
return Invalid;
else if (i==4 && ha[i-1][D]!=Water)
return Invalid;
else if (ha[i+1][D]>=0&&ha[i+1][D]!=Water&&ha[i-1][D]!=Water)
return Invalid;
}
}
if ((c_add != c_or)||(m_add != m_or)||(d_add != d_or)
||(a_add != a_or)||(s_add != s_or)) {
return Invalid;
}
if ((c_add != 0b11111)||(m_add != 0b11111)||(d_add != 0b11111)
||(a_add != 0b11111)||(s_add != 0b11111)) {
return Underfull;
}
return Valid;
}
int bruteFill(int ha[5][5], int hno, int attr)
{
int stat = checkHouses(ha);
if (( stat == Valid)||(stat == Invalid))
return stat;
int hb[5][5];
memcpy(hb, ha, sizeof(int)*5*5);
for (int i=0; i<5; i++) {
hb[hno][attr] = i;
stat = checkHouses(hb);
if (stat != Invalid) {
int nexthno, nextattr;
if (attr<4) {
nextattr = attr+1;
nexthno = hno;
} else {
nextattr = 0;
nexthno = hno+1;
}
stat = bruteFill(hb, nexthno, nextattr);
if (stat != Invalid) {
memcpy(ha, hb, sizeof(int)*5*5);
return stat;
}
}
}
// we only come here if none of the attr values assigned were valid
return Invalid;
}
int main(void)
{
int ha[5][5]={{-1,-1,-1,-1,-1},{-1,-1,-1,-1,-1},{-1,-1,-1,-1,-1},
{-1,-1,-1,-1,-1},{-1,-1,-1,-1,-1}};
bruteFill(ha, 0, 0);
printHouses(ha);
return 0;
}

View file

@ -0,0 +1,176 @@
#!/usr/bin/perl
use utf8;
no strict;
my (%props, %name, @pre, @conds, @works, $find_all_solutions);
sub do_consts {
local $";
for my $p (keys %props) {
my @s = @{ $props{$p} };
$" = ", ";
print "enum { ${p}_none = 0, @s };\n";
$" = '", "';
print "const char *string_$p [] = { \"###\", \"@s\" };\n\n";
}
print "#define FIND_BY(p) \\
int find_by_##p(int v) { \\
int i; \\
for (i = 0; i < N_ITEMS; i++) \\
if (house[i].p == v) return i; \\
return -1; }\n";
print "FIND_BY($_)" for (keys %props);
local $" = ", ";
my @k = keys %props;
my $sl = 0;
for (keys %name) {
if (length > $sl) { $sl = length }
}
my $fmt = ("%".($sl + 1)."s ") x @k;
my @arg = map { "string_$_"."[house[i].$_]" } @k;
print << "SNIPPET";
int work0(void) {
int i;
for (i = 0; i < N_ITEMS; i++)
printf("%d $fmt\\n", i, @arg);
puts(\"\");
return 1;
}
SNIPPET
}
sub setprops {
%props = @_;
my $l = 0;
my @k = keys %props;
for my $p (@k) {
my @s = @{ $props{$p} };
if ($l && $l != @s) {
die "bad length @s";
}
$l = @s;
$name{$_} = $p for @s;
}
local $" = ", ";
print "#include <stdio.h>
#define N_ITEMS $l
struct item_t { int @k; } house[N_ITEMS] = {{0}};\n";
}
sub pair {NB. h =.~.&> compose&.>~/y,<h
my ($c1, $c2, $diff) = @_;
$diff //= [0];
$diff = [$diff] unless ref $diff;
push @conds, [$c1, $c2, $diff];
}
sub make_conditions {
my $idx = 0;
my $return1 = $find_all_solutions ? "" : "return 1";
print "
#define TRY(a, b, c, d, p, n) \\
if ((b = a d) >= 0 && b < N_ITEMS) { \\
if (!house[b].p) { \\
house[b].p = c; \\
if (n()) $return1; \\
house[b].p = 0; \\
}}
";
while (@conds) {
my ($c1, $c2, $diff) = @{ pop @conds };
my $p2 = $name{$c2} or die "bad prop $c2";
if ($c1 =~ /^\d+$/) {
push @pre, "house[$c1].$p2 = $c2;";
next;
}
my $p1 = $name{$c1} or die "bad prop $c1";
my $next = "work$idx";
my $this = "work".++$idx;
print "
/* condition pair($c1, $c2, [@$diff]) */
int $this(void) {
int a = find_by_$p1($c1);
int b = find_by_$p2($c2);
if (a != -1 && b != -1) {
switch(b - a) {
";
print "case $_: " for @$diff;
print "return $next(); default: return 0; }\n } if (a != -1) {";
print "TRY(a, b, $c2, +($_), $p2, $next);" for @$diff;
print " return 0; } if (b != -1) {";
print "TRY(b, a, $c1, -($_), $p1, $next);" for @$diff;
print "
return 0; }
/* neither condition is set; try all possibles */
for (a = 0; a < N_ITEMS; a++) {
if (house[a].$p1) continue;
house[a].$p1 = $c1;
";
print "TRY(a, b, $c2, +($_), $p2, $next);" for @$diff;
print " house[a].$p1 = 0; } return 0; }";
}
print "int main() { @pre return !work$idx(); }";
}
sub make_c {
do_consts;
make_conditions;
}
# ---- above should be generic for all similar puzzles ---- #
# ---- below: per puzzle setup ---- #
# property names and values
setprops (
'nationality' # Svensk n. a Swede, not a swede (kålrot).
# AEnglisk (from middle Viking "Æŋløsåksen") n. a Brit.
=> [ qw(Deutsch Svensk Norske Danske AEnglisk) ],
'pet' => [ qw(birds dog horse zebra cats) ],
'drink' => [ qw(water tea milk beer coffee) ],
'smoke' => [ qw(dunhill blue_master prince blend pall_mall) ],
'color' => [ qw(red green yellow white blue) ]
);
# constraints
pair(AEnglisk, red);
pair(Svensk, dog);
pair(Danske, tea);
pair(green, white, 1); # "to the left of" can mean either 1 or -1: ambiguous
pair(coffee, green);
pair(pall_mall, birds);
pair(yellow, dunhill);
pair(2, milk);
pair(0, Norske);
pair(blend, cats, [-1, 1]);
pair(horse, dunhill, [-1, 1]);
pair(blue_master, beer); # Nicht das Deutsche Bier trinken? Huh.
pair(Deutsch, prince);
pair(Norske, blue, [-1, 1]);
pair(water, blend, [-1, 1]);
# "zebra lives *somewhere* relative to the Brit". It has no effect on
# the logic. It's here just to make sure the code will insert a zebra
# somewhere in the table (after all other conditions are met) so the
# final print-out shows it. (the C code can be better structured, but
# meh, I ain't reading it, so who cares).
pair(zebra, AEnglisk, [ -4 .. 4 ]);
# write C code. If it's ugly to you: I didn't write; Perl did.
make_c;

View file

@ -0,0 +1,96 @@
import std.stdio, std.traits, std.algorithm, std.math;
enum Content { Beer, Coffee, Milk, Tea, Water,
Danish, English, German, Norwegian, Swedish,
Blue, Green, Red, White, Yellow,
Blend, BlueMaster, Dunhill, PallMall, Prince,
Bird, Cat, Dog, Horse, Zebra }
enum Test { Drink, Person, Color, Smoke, Pet }
enum House { One, Two, Three, Four, Five }
alias Content[EnumMembers!Test.length][EnumMembers!House.length] TM;
bool finalChecks(in ref TM M) pure nothrow {
int diff(in Content a, in Content b, in Test ca, in Test cb)
nothrow {
foreach (immutable h1; EnumMembers!House)
foreach (immutable h2; EnumMembers!House)
if (M[ca][h1] == a && M[cb][h2] == b)
return h1 - h2;
assert(0); // Useless but required.
}
with (Content) with (Test) { // Braces required (8414).
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 {
with (Content) with (Test) with (House)
final switch (atest) {
case Drink:
return M[Drink][Three] == Milk;
case Person:
foreach (immutable h; EnumMembers!House)
if ((M[Person][h] == Norwegian && h != One) ||
(M[Person][h] == Danish && M[Drink][h] != Tea))
return false;
return true;
case Color:
foreach (immutable h; EnumMembers!House)
if ((M[Person][h] == English && M[Color][h] != Red) ||
(M[Drink][h] == Coffee && M[Color][h] != Green))
return false;
return true;
case Smoke:
foreach (immutable h; EnumMembers!House)
if ((M[Color][h] == Yellow && M[Smoke][h] != Dunhill) ||
(M[Smoke][h] == BlueMaster && M[Drink][h] != Beer) ||
(M[Person][h] == German && M[Smoke][h] != Prince))
return false;
return true;
case Pet:
foreach (immutable h; EnumMembers!House)
if ((M[Person][h] == Swedish && M[Pet][h] != Dog) ||
(M[Smoke][h] == PallMall && M[Pet][h] != Bird))
return false;
return finalChecks(M);
}
}
void show(in ref TM M) {
foreach (h; EnumMembers!House) {
writef("%5s: ", h);
foreach (immutable t; EnumMembers!Test)
writef("%10s ", M[t][h]);
writeln();
}
}
void solve(ref TM M, in Test t, in size_t n) {
if (n == 1 && constrained(M, t)) {
if (t < 4) {
solve(M, [EnumMembers!Test][t + 1], 5);
} else {
show(M);
return;
}
}
foreach (immutable i; 0 .. n) {
solve(M, t, n - 1);
swap(M[t][n % 2 ? 0 : i], M[t][n - 1]);
}
}
void main() {
TM M;
foreach (immutable t; EnumMembers!Test)
foreach (immutable h; EnumMembers!House)
M[t][h] = EnumMembers!Content[t * 5 + h];
solve(M, Test.Drink, 5);
}

View file

@ -0,0 +1,101 @@
import std.stdio, std.math, std.traits, std.typetuple;
const(T[N][]) permutationsFixed(T, size_t N)(in T[N] items)
pure nothrow {
const(T[N])[] result;
T[N] row;
void perms(in T[] s, in T[] prefix=null) nothrow {
if (s.length)
foreach (immutable i, immutable c; s)
perms(s[0 .. i] ~ s[i+1 .. $], prefix ~ c);
else {
row[] = prefix[];
result ~= row;
}
}
perms(items);
return result;
}
enum Number : uint { One, Two, Three, Four, Five }
enum Color : uint { Red, Green, Blue, White, Yellow }
enum Drink : uint { Milk, Coffee, Water, Beer, Tea }
enum Smoke : uint { PallMall, Dunhill, Blend, BlueMaster, Prince }
enum Pet : uint { Dog, Cat, Zebra, Horse, Bird }
enum Nation : uint { 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))
return false;
if (!number || !color || !drink || !smoke || !pet)
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))
return false;
foreach (immutable j; 0 .. 5) {
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))
return false;
}
}
return true;
}
void main() {
static immutable perms = permutationsFixed!(uint, 5)([0,1,2,3,4]);
// Not nice casts.
static permsNumber = cast(immutable(Number[5][]))perms;
static permsColor = cast(immutable(Color[5][]))perms;
static permsDrink = cast(immutable(Drink[5][]))perms;
static permsSmoke = cast(immutable(Smoke[5][]))perms;
static permsPet = cast(immutable(Pet[5][]))perms;
immutable nation = [EnumMembers!Nation];
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)) {
writeln("Found a solution:");
foreach (x; TypeTuple!(nation, number, color,
drink, smoke, pet))
writefln("%6s: %12s%12s%12s%12s%12s",
(Unqual!(typeof(x[0]))).stringof,
x[0], x[1], x[2], x[3], x[4]);
writeln();
}
}

View file

@ -0,0 +1,13 @@
ehs=: 5$a:
cr=: (('English';'red') 0 3} ehs);<('Dane';'tea') 0 2}ehs
cr=: cr, (('German';'Prince') 0 4}ehs);<('Swede';'dog') 0 1 }ehs
cs=: <('PallMall';'birds') 4 1}ehs
cs=: cs, (('yellow';'Dunhill') 3 4}ehs);<('BlueMaster';'beer') 4 2}ehs
lof=: (('coffee';'green')2 3}ehs);<(<'white')3}ehs
next=: <((<'Blend') 4 }ehs);<(<'water')2}ehs
next=: next,<((<'Blend') 4 }ehs);<(<'cats')1}ehs
next=: next,<((<'Dunhill') 4}ehs);<(<'horse')1}ehs

View file

@ -0,0 +1,12 @@
houses solve3 constraints,<zebra
┌─────────┬─────┬──────┬──────┬──────────┐
│Norwegian│cats │water │yellow│Dunhill │
├─────────┼─────┼──────┼──────┼──────────┤
│Dane │horse│tea │blue │Blend │
├─────────┼─────┼──────┼──────┼──────────┤
│English │birds│milk │red │PallMall │
├─────────┼─────┼──────┼──────┼──────────┤
│German │zebra│coffee│green │Prince │
├─────────┼─────┼──────┼──────┼──────────┤
│Swede │dog │beer │white │BlueMaster│
└─────────┴─────┴──────┴──────┴──────────┘

View file

@ -0,0 +1,6 @@
lof
┌─────────────────┬───────────┐
│┌┬┬──────┬─────┬┐│┌┬┬┬─────┬┐│
││││coffee│green│││││││white│││
│└┴┴──────┴─────┴┘│└┴┴┴─────┴┘│
└─────────────────┴───────────┘

View file

@ -0,0 +1,4 @@
hcr=: (<ehs),. (A.~i.@!@#)cr
hcs=:~. (A.~i.@!@#)cs,2$<ehs
hlof=:(-i.4) |."0 1 lof,3$<ehs
hnext=: ,/((i.4) |."0 1 (3$<ehs)&,)"1 ;(,,:|.)&.> next

View file

@ -0,0 +1 @@
houses=: ((<'Norwegian') 0}ehs);((<'blue') 3 }ehs);((<'milk') 2}ehs);ehs;<ehs

View file

@ -0,0 +1,6 @@
houses
┌───────────────┬──────────┬──────────┬──────┬──────┐
│┌─────────┬┬┬┬┐│┌┬┬┬────┬┐│┌┬┬────┬┬┐│┌┬┬┬┬┐│┌┬┬┬┬┐│
││Norwegian││││││││││blue││││││milk││││││││││││││││││
│└─────────┴┴┴┴┘│└┴┴┴────┴┘│└┴┴────┴┴┘│└┴┴┴┴┘│└┴┴┴┴┘│
└───────────────┴──────────┴──────────┴──────┴──────┘

View file

@ -0,0 +1 @@
constraints=: hcr;hcs;hlof;<hnext

View file

@ -0,0 +1,11 @@
select=: ~.@(,: #~ ,&(0~:#))
filter=: #~*./@:(2>#S:0)"1
compose=: [: filter f. [: ,/ select f. L:0"1"1 _
solve=: 4 :0
h=. ,:x
whilst. 0=# z do.
for_e. y do. h=. h compose > e end.
z=.(#~1=[:+/"1 (0=#)S:0"1) h=.~. h
end.
)

View file

@ -0,0 +1,12 @@
>"0 houses solve constraints
┌─────────┬─────┬──────┬──────┬──────────┐
│Norwegian│cats │water │yellow│Dunhill │
├─────────┼─────┼──────┼──────┼──────────┤
│Dane │horse│tea │blue │Blend │
├─────────┼─────┼──────┼──────┼──────────┤
│English │birds│milk │red │PallMall │
├─────────┼─────┼──────┼──────┼──────────┤
│German │ │coffee│green │Prince │
├─────────┼─────┼──────┼──────┼──────────┤
│Swede │dog │beer │white │BlueMaster│
└─────────┴─────┴──────┴──────┴──────────┘

View file

@ -0,0 +1,8 @@
zebra=: (-i.5)|."0 1 (<(<'zebra') 1}ehs),4$<ehs
solve3=: 4 :0
p=. *./@:((0~:#)S:0)
f=. [:~.&.> [: compose&.>~/y&, f.
z=. f^:(3>[:#(#~p"1)&>)^:_ <,:x
>"0 (#~([:*./[:;[:<@({.~:}.)\.;)"1)(#~p"1); z
)

View file

@ -0,0 +1,104 @@
#!/usr/bin/perl
use utf8;
use strict;
binmode STDOUT, ":utf8";
my (@tgt, %names);
sub setprops {
my %h = @_;
my @p = keys %h;
for my $p (@p) {
my @v = @{ $h{$p} };
@tgt = map(+{idx=>$_-1, map{ ($_, undef) } @p}, 1 .. @v)
unless @tgt;
$names{$_} = $p for @v;
}
}
my $solve = sub {
for my $i (@tgt) {
printf("%12s", ucfirst($i->{$_} // "¿Qué?"))
for reverse sort keys %$i;
print "\n";
}
"there is only one" # <--- change this to a false value to find all solutions (if any)
};
sub pair {
my ($a, $b, @v) = @_;
if ($a =~ /^(\d+)$/) {
$tgt[$1]{ $names{$b} } = $b;
return;
}
@v = (0) unless @v;
my %allowed;
$allowed{$_} = 1 for @v;
my ($p1, $p2) = ($names{$a}, $names{$b});
my $e = $solve;
$solve = sub { # <--- sorta like how TeX \let...\def macro
my ($x, $y);
($x) = grep { $_->{$p1} eq $a } @tgt;
($y) = grep { $_->{$p2} eq $b } @tgt;
$x and $y and
return $allowed{ $x->{idx} - $y->{idx} } && $e->();
my $try_stuff = sub {
my ($this, $p, $v, $sign) = @_;
for (@v) {
my $i = $this->{idx} + $sign * $_;
next unless $i >= 0 && $i < @tgt && !$tgt[$i]{$p};
local $tgt[$i]{$p} = $v;
$e->() and return 1;
}
return
};
$x and return $try_stuff->($x, $p2, $b, 1);
$y and return $try_stuff->($y, $p1, $a, -1);
for $x (@tgt) {
next if $x->{$p1};
local $x->{$p1} = $a;
$try_stuff->($x, $p2, $b, 1) and return 1;
}
};
}
# ---- above should be generic for all similar puzzles ---- #
# ---- below: per puzzle setup ---- #
# property names and values
setprops (
# Svensk n. a Swede, not a swede (kålrot).
# AEnglisk (from middle Viking "Æŋløsåksen") n. a Brit.
'Who' => [ qw(Deutsch Svensk Norske Danske AEnglisk) ],
'Pet' => [ qw(birds dog horse zebra cats) ],
'Drink' => [ qw(water tea milk beer coffee) ],
'Smoke' => [ qw(dunhill blue_master prince blend pall_mall) ],
'Color' => [ qw(red green yellow white blue) ]
);
# constraints
pair qw( AEnglisk red );
pair qw( Svensk dog );
pair qw( Danske tea );
pair qw( green white 1 ); # "to the left of" can mean either 1 or -1: ambiguous
pair qw( coffee green );
pair qw( pall_mall birds );
pair qw( yellow dunhill );
pair qw( 2 milk );
pair qw( 0 Norske );
pair qw( blend cats -1 1 );
pair qw( horse dunhill -1 1 );
pair qw( blue_master beer ); # Nicht das Deutsche Bier trinken? Huh.
pair qw( Deutsch prince );
pair qw( Norske blue -1 1 );
pair qw( water blend -1 1 );
$solve->();

View file

@ -0,0 +1,21 @@
...
# property names and values
setprops
'Who' => [ qw(baker cooper fletcher miller smith) ],
'Level' => [ qw(one two three four five) ];
# constraints
pair qw(0 one);
pair qw(1 two);
pair qw(2 three);
pair qw(3 four);
pair qw(4 five);
pair qw(baker five -4 -3 -2 -1 1 2 3 4);
pair qw(cooper one -4 -3 -2 -1 1 2 3 4);
pair qw(fletcher one -4 -3 -2 -1 1 2 3 4);
pair qw(fletcher five -4 -3 -2 -1 1 2 3 4);
pair qw(miller cooper -1 -2 -3 -4);
pair qw(smith fletcher 4 3 2 -2 -3 -4);
pair qw(cooper fletcher 4 3 2 -2 -3 -4);
$solve->();

View file

@ -0,0 +1,41 @@
(be match (@House @Person @Drink @Pet @Cigarettes)
(permute (red blue green yellow white) @House)
(left-of @House white @House green)
(permute (Norwegian English Swede German Dane) @Person)
(has @Person English @House red)
(equal @Person (Norwegian . @))
(next-to @Person Norwegian @House blue)
(permute (tea coffee milk beer water) @Drink)
(has @Drink tea @Person Dane)
(has @Drink coffee @House green)
(equal @Drink (@ @ milk . @))
(permute (dog birds cats horse zebra) @Pet)
(has @Pet dog @Person Swede)
(permute (Pall-Mall Dunhill Blend Blue-Master Prince) @Cigarettes)
(has @Cigarettes Pall-Mall @Pet birds)
(has @Cigarettes Dunhill @House yellow)
(next-to @Cigarettes Blend @Pet cats)
(next-to @Cigarettes Dunhill @Pet horse)
(has @Cigarettes Blue-Master @Drink beer)
(has @Cigarettes Prince @Person German)
(next-to @Drink water @Cigarettes Blend) )
(be has ((@A . @X) @A (@B . @Y) @B))
(be has ((@ . @X) @A (@ . @Y) @B)
(has @X @A @Y @B) )
(be right-of ((@A . @X) @A (@ @B . @Y) @B))
(be right-of ((@ . @X) @A (@ . @Y) @B)
(right-of @X @A @Y @B) )
(be left-of ((@ @A . @X) @A (@B . @Y) @B))
(be left-of ((@ . @X) @A (@ . @Y) @B)
(left-of @X @A @Y @B) )
(be next-to (@X @A @Y @B) (right-of @X @A @Y @B))
(be next-to (@X @A @Y @B) (left-of @X @A @Y @B))

View file

@ -0,0 +1,5 @@
(pilog '((match @House @Person @Drink @Pet @Cigarettes))
(let Fmt (-8 -11 -8 -7 -11)
(tab Fmt "HOUSE" "PERSON" "DRINKS" "HAS" "SMOKES")
(mapc '(@ (pass tab Fmt))
@House @Person @Drink @Pet @Cigarettes ) ) )

View file

@ -0,0 +1,22 @@
select([A|As],S):- select(A,S,S1),select(As,S1).
select([],_).
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).
:- zebra(Who, HS), maplist(writeln,HS), nl, write(Who), nl, nl, fail
;
write('No more solutions.').

View file

@ -0,0 +1,101 @@
import psyco; psyco.full()
class Content: elems= """Beer Coffee Milk Tea Water
Danish English German Norwegian Swedish
Blue Green Red White Yellow
Blend BlueMaster Dunhill PallMall Prince
Bird Cat Dog Horse Zebra""".split()
class Test: elems= "Drink Person Color Smoke Pet".split()
class House: elems= "One Two Three Four Five".split()
for c in (Content, Test, House):
c.values = range(len(c.elems))
for i, e in enumerate(c.elems):
exec "%s.%s = %d" % (c.__name__, e, i)
def finalChecks(M):
def diff(a, b, ca, cb):
for h1 in House.values:
for h2 in House.values:
if M[ca][h1] == a and M[cb][h2] == b:
return h1 - h2
assert False
return abs(diff(Content.Norwegian, Content.Blue,
Test.Person, Test.Color)) == 1 and \
diff(Content.Green, Content.White,
Test.Color, Test.Color) == -1 and \
abs(diff(Content.Horse, Content.Dunhill,
Test.Pet, Test.Smoke)) == 1 and \
abs(diff(Content.Water, Content.Blend,
Test.Drink, Test.Smoke)) == 1 and \
abs(diff(Content.Blend, Content.Cat,
Test.Smoke, Test.Pet)) == 1
def constrained(M, atest):
if atest == Test.Drink:
return M[Test.Drink][House.Three] == Content.Milk
elif atest == Test.Person:
for h in House.values:
if ((M[Test.Person][h] == Content.Norwegian and
h != House.One) or
(M[Test.Person][h] == Content.Danish and
M[Test.Drink][h] != Content.Tea)):
return False
return True
elif atest == Test.Color:
for h in House.values:
if ((M[Test.Person][h] == Content.English and
M[Test.Color][h] != Content.Red) or
(M[Test.Drink][h] == Content.Coffee and
M[Test.Color][h] != Content.Green)):
return False
return True
elif atest == Test.Smoke:
for h in House.values:
if ((M[Test.Color][h] == Content.Yellow and
M[Test.Smoke][h] != Content.Dunhill) or
(M[Test.Smoke][h] == Content.BlueMaster and
M[Test.Drink][h] != Content.Beer) or
(M[Test.Person][h] == Content.German and
M[Test.Smoke][h] != Content.Prince)):
return False
return True
elif atest == Test.Pet:
for h in House.values:
if ((M[Test.Person][h] == Content.Swedish and
M[Test.Pet][h] != Content.Dog) or
(M[Test.Smoke][h] == Content.PallMall and
M[Test.Pet][h] != Content.Bird)):
return False
return finalChecks(M)
def show(M):
for h in House.values:
print "%5s:" % House.elems[h],
for t in Test.values:
print "%10s" % Content.elems[M[t][h]],
print
def solve(M, t, n):
if n == 1 and constrained(M, t):
if t < 4:
solve(M, Test.values[t + 1], 5)
else:
show(M)
return
for i in xrange(n):
solve(M, t, n - 1)
M[t][0 if n % 2 else i], M[t][n - 1] = \
M[t][n - 1], M[t][0 if n % 2 else i]
def main():
M = [[None] * len(Test.elems) for _ in xrange(len(House.elems))]
for t in Test.values:
for h in House.values:
M[t][h] = Content.values[t * 5 + h]
solve(M, Test.Drink, 5)
main()

View file

@ -0,0 +1,89 @@
from itertools import permutations
import psyco
psyco.full()
class Number:elems= "One Two Three Four Five".split()
class Color: elems= "Red Green Blue White Yellow".split()
class Drink: elems= "Milk Coffee Water Beer Tea".split()
class Smoke: elems= "PallMall Dunhill Blend BlueMaster Prince".split()
class Pet: elems= "Dog Cat Zebra Horse Bird".split()
class Nation:elems= "British Swedish Danish Norvegian German".split()
for c in (Number, Color, Drink, Smoke, Pet, Nation):
for i, e in enumerate(c.elems):
exec "%s.%s = %d" % (c.__name__, e, i)
def is_possible(number, color, drink, smoke, pet):
if number and number[Nation.Norvegian] != Number.One:
return False
if color and color[Nation.British] != Color.Red:
return False
if drink and drink[Nation.Danish] != Drink.Tea:
return False
if smoke and smoke[Nation.German] != Smoke.Prince:
return False
if pet and pet[Nation.Swedish] != Pet.Dog:
return False
if not number or not color or not drink or not smoke or not pet:
return True
for i in xrange(5):
if color[i] == Color.Green and drink[i] != Drink.Coffee:
return False
if smoke[i] == Smoke.PallMall and pet[i] != Pet.Bird:
return False
if color[i] == Color.Yellow and smoke[i] != Smoke.Dunhill:
return False
if number[i] == Number.Three and drink[i] != Drink.Milk:
return False
if smoke[i] == Smoke.BlueMaster and drink[i] != Drink.Beer:
return False
if color[i] == Color.Blue and number[i] != Number.Two:
return False
for j in xrange(5):
if (color[i] == Color.Green and
color[j] == Color.White and
number[j] - number[i] != 1):
return False
diff = abs(number[i] - number[j])
if smoke[i] == Smoke.Blend and pet[j] == Pet.Cat and diff != 1:
return False
if pet[i]==Pet.Horse and smoke[j]==Smoke.Dunhill and diff != 1:
return False
if smoke[i]==Smoke.Blend and drink[j]==Drink.Water and diff!=1:
return False
return True
def show_row(t, data):
print "%6s: %12s%12s%12s%12s%12s" % (
t.__name__, t.elems[data[0]],
t.elems[data[1]], t.elems[data[2]],
t.elems[data[3]], t.elems[data[4]])
def main():
perms = list(permutations(range(5)))
for number in perms:
if is_possible(number, None, None, None, None):
for color in perms:
if is_possible(number, color, None, None, None):
for drink in perms:
if is_possible(number, color, drink, None, None):
for smoke in perms:
if is_possible(number, color, drink, smoke, None):
for pet in perms:
if is_possible(number, color, drink, smoke, pet):
print "Found a solution:"
show_row(Nation, range(5))
show_row(Number, number)
show_row(Color, color)
show_row(Drink, drink)
show_row(Smoke, smoke)
show_row(Pet, pet)
print
main()

View file

@ -0,0 +1,57 @@
#lang racket
(require racklog)
(define %select
(%rel (x xs S S1)
[(x (cons x xs) xs)]
[(x (cons S xs) (cons S S1)) (%select x xs S1)]
[((cons x xs) S)
(%select x S S1)
(%select xs S1)]
[('() (_))]))
(define %next-to
(%rel (A B C)
[(A B C)
(%or (%left-of A B C)
(%left-of B A C))]))
(define %left-of
(%rel (A B C)
[(A B C) (%append (_) (cons A (cons B (_))) C)]))
(define %zebra
(%rel (Owns HS)
[(Owns HS)
(%is HS (list (list (_) 'norwegian (_) (_) (_))
(_)
(list (_) (_) (_) 'milk (_))
(_) (_)))
(%select (list (list 'red 'englishman (_) (_) (_))
(list (_) 'swede 'dog (_) (_))
(list (_) 'dane (_) 'tea (_))
(list (_) 'german (_) (_) 'prince))
HS)
(%select (list (list (_) (_) 'birds (_) 'pallmall)
(list 'yellow (_) (_) (_) 'dunhill)
(list (_) (_) (_) 'beer 'bluemaster))
HS)
(%left-of (list 'green (_) (_) 'coffee (_))
(list 'white (_) (_) (_) (_))
HS)
(%next-to (list (_) (_) (_) (_) 'dunhill)
(list (_) (_) 'horse (_) (_))
HS)
(%next-to (list (_) (_) (_) (_) 'blend)
(list (_) (_) 'cats (_) (_))
HS)
(%next-to (list (_) (_) (_) (_) 'blend)
(list (_) (_) (_) 'water (_))
HS)
(%next-to (list (_) 'norwegian (_) (_) (_))
(list 'blue (_) (_) (_) (_))
HS)
(%member (list (_) Owns 'zebra (_) (_)) HS)]))
(%which (Who HS) (%zebra Who HS))

View file

@ -0,0 +1,116 @@
package require struct::list
# Implements the constants by binding them directly into the named procedures.
# This is much faster than the alternatives!
proc initConstants {args} {
global {}
set remap {}
foreach {class elems} {
Number {One Two Three Four Five}
Color {Red Green Blue White Yellow}
Drink {Milk Coffee Water Beer Tea}
Smoke {PallMall Dunhill Blend BlueMaster Prince}
Pet {Dog Cat Horse Bird Zebra}
Nation {British Swedish Danish Norwegian German}
} {
set i -1
foreach e $elems {lappend remap "\$${class}($e)" [incr i]}
set ($class) $elems
}
foreach procedure $args {
proc $procedure [info args $procedure] \
[string map $remap [info body $procedure]]
}
}
proc isPossible {number color drink smoke pet} {
if {[llength $number] && [lindex $number $Nation(Norwegian)] != $Number(One)} {
return false
} elseif {[llength $color] && [lindex $color $Nation(British)] != $Color(Red)} {
return false
} elseif {[llength $drink] && [lindex $drink $Nation(Danish)] != $Drink(Tea)} {
return false
} elseif {[llength $smoke] && [lindex $smoke $Nation(German)] != $Smoke(Prince)} {
return false
} elseif {[llength $pet] && [lindex $pet $Nation(Swedish)] != $Pet(Dog)} {
return false
}
if {!([llength $number] && [llength $color] && [llength $drink] && [llength $smoke] && [llength $pet])} {
return true
}
for {set i 0} {$i < 5} {incr i} {
if {[lindex $color $i] == $Color(Green) && [lindex $drink $i] != $Drink(Coffee)} {
return false
} elseif {[lindex $smoke $i] == $Smoke(PallMall) && [lindex $pet $i] != $Pet(Bird)} {
return false
} elseif {[lindex $color $i] == $Color(Yellow) && [lindex $smoke $i] != $Smoke(Dunhill)} {
return false
} elseif {[lindex $number $i] == $Number(Three) && [lindex $drink $i] != $Drink(Milk)} {
return false
} elseif {[lindex $smoke $i] == $Smoke(BlueMaster) && [lindex $drink $i] != $Drink(Beer)} {
return false
} elseif {[lindex $color $i] == $Color(Blue) && [lindex $number $i] != $Number(Two)} {
return false
}
for {set j 0} {$j < 5} {incr j} {
if {[lindex $color $i] == $Color(Green) && [lindex $color $j] == $Color(White) && [lindex $number $j] - [lindex $number $i] != 1} {
return false
}
set diff [expr {abs([lindex $number $i] - [lindex $number $j])}]
if {[lindex $smoke $i] == $Smoke(Blend) && [lindex $pet $j] == $Pet(Cat) && $diff != 1} {
return false
} elseif {[lindex $pet $i] == $Pet(Horse) && [lindex $smoke $j] == $Smoke(Dunhill) && $diff != 1} {
return false
} elseif {[lindex $smoke $i] == $Smoke(Blend) && [lindex $drink $j] == $Drink(Water) && $diff != 1} {
return false
}
}
}
return true
}
proc showRow {t data} {
upvar #0 ($t) elems
puts [format "%6s: %12s%12s%12s%12s%12s" $t \
[lindex $elems [lindex $data 0]] \
[lindex $elems [lindex $data 1]] \
[lindex $elems [lindex $data 2]] \
[lindex $elems [lindex $data 3]] \
[lindex $elems [lindex $data 4]]]
}
proc main {} {
set perms [struct::list permutations {0 1 2 3 4}]
foreach number $perms {
if {![isPossible $number {} {} {} {}]} continue
foreach color $perms {
if {![isPossible $number $color {} {} {}]} continue
foreach drink $perms {
if {![isPossible $number $color $drink {} {}]} continue
foreach smoke $perms {
if {![isPossible $number $color $drink $smoke {}]} continue
foreach pet $perms {
if {[isPossible $number $color $drink $smoke $pet]} {
puts "Found a solution:"
showRow Nation {0 1 2 3 4}
showRow Number $number
showRow Color $color
showRow Drink $drink
showRow Smoke $smoke
showRow Pet $pet
puts ""
}
}
}
}
}
}
}
initConstants isPossible
main