Zebra puzzle: Difference between revisions

Content added Content deleted
No edit summary
(→‎{{header|C}}: well, sort of)
Line 23: Line 23:
cf. [[Dinesman's multiple-dwelling problem]]
cf. [[Dinesman's multiple-dwelling problem]]


=={{header|C}}==
I'll be the first to admit the following doesn't quite look like a C program. It's in fact in Perl, which outputs a C source, which in turn solves the puzzle. If you think this is long, wait till you see the C it writes.
<lang perl>#!/usr/bin/perl

use utf8;
no strict;

my (%props, %name, @pre, @conds, @works);

sub do_consts {
local $";
for my $p (keys %props) {
my @s = @{ $props{$p} };

$" = ", ";
print "enum { ${p}_none = 0, @s };\n";

$" = '", "';
print "char *string_$p [] = { \"###\", \"@s\" };\n\n";
}
for (keys %props) {
print << "SNIPPET";
int find_by_$_(int val) {
int i;
for (i = 0; i < N_ITEMS; i++)
if (house[i].$_ == val) return i;
return -1;
}
SNIPPET
}

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);
}
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;
}
print "#include <stdio.h>\n";
print "#define N_PROPS ", scalar(@k), "\n";

local $" = ", ";
print "#define N_ITEMS $l\n
struct item_t { int @k; } house[N_ITEMS] = {{0}};\n";
}

sub pair {
my ($c1, $c2, $diff) = @_;
$diff //= [0];
$diff = [$diff] unless ref $diff;

push @conds, [$c1, $c2, $diff];
}

sub make_conditions {
my $idx = 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;\n";
next;
}

my $p1 = $name{$c1} or die "bad prop $c1";
my @txt;
my $next = "work$idx";
my $this = "work".++$idx;

push @txt, << "SNIPPET";
/* condition pair($c1, $c2, [@$diff]) */
int $this(void) {
/*
work0();
puts("$this");
getchar();
*/
int a = find_by_$p1($c1);
int b = find_by_$p2($c2);
if (a != -1 && b != -1) {
switch(b - a) {
SNIPPET
push @txt, "case $_: " for @$diff;
push @txt, "return $next(); default: return 0; }\n";
push @txt, << "SNIPPET";
}

if (a != -1) {
SNIPPET
for (@$diff) {
push @txt, << "SNIPPET";
b = a + ($_);
if (b >= 0 && b < N_ITEMS) {
if (!house[b].$p2) {
house[b].$p2 = $c2;
if ($next()) return 1;
house[b].$p2 = 0;
}
}
SNIPPET
}

push @txt, << "SNIPPET";
return 0;
}
if (b != -1) {
SNIPPET
for (@$diff) {
push @txt, << "SNIPPET";
a = b - ($_);
if (a >= 0 && a < N_ITEMS) {
if (!house[a].$p1) {
house[a].$p1 = $c1;
if ($next()) return 1;
house[a].$p1 = 0;
}
}
SNIPPET
}
push @txt, << "SNIPPET";
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;
SNIPPET
for (@$diff) {
push @txt, << "SNIPPET";
b = a + ($_);
if (b >= 0 && b < N_ITEMS) {
if (!house[b].$p2) {
house[b].$p2 = $c2;
if ($next()) return 1;
house[b].$p2 = 0;
}
}
SNIPPET
}

push @txt, << "SNIPPET";
house[a].$p1 = 0;
}
return 0;
}
SNIPPET
push @works, join('', @txt);
}

print @works;
print << "SNIPPET";
int main() {
@pre
return !work$idx();
}
SNIPPET
}

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;</lang>
output (ran as <code>perl test.pl | gcc -Wall -x c -; ./a.out</code>):<pre>
0 dunhill cats yellow water Norske
1 blend horse blue tea Danske
2 pall_mall birds red milk AEnglisk
3 prince zebra green coffee Deutsch
4 blue_master dog white beer Svensk
</pre>
=={{header|Prolog}}==
=={{header|Prolog}}==