RosettaCodeData/Task/Zebra-puzzle/JavaScript/zebra-puzzle.js

164 lines
6.2 KiB
JavaScript

// Define enums and their string representations
const Attrib = { Color: 0, Man: 1, Drink: 2, Animal: 3, Smoke: 4 };
const Attrib_str = ["Color", "Man", "Drink", "Animal", "Smoke"];
const Colors = { Red: 0, Green: 1, White: 2, Yellow: 3, Blue: 4 };
const Colors_str = ["Red", "Green", "White", "Yellow", "Blue"];
const Mans = { English: 0, Swede: 1, Dane: 2, German: 3, Norwegian: 4 };
const Mans_str = ["English", "Swede", "Dane", "German", "Norwegian"];
const Drinks = { Tea: 0, Coffee: 1, Milk: 2, Beer: 3, Water: 4 };
const Drinks_str = ["Tea", "Coffee", "Milk", "Beer", "Water"];
const Animals = { Dog: 0, Birds: 1, Cats: 2, Horse: 3, Zebra: 4 };
const Animals_str = ["Dog", "Birds", "Cats", "Horse", "Zebra"];
const Smokes = { PallMall: 0, Dunhill: 1, Blend: 2, BlueMaster: 3, Prince: 4 };
const Smokes_str = ["PallMall", "Dunhill", "Blend", "BlueMaster", "Prince"];
function printHouses(ha) {
const attr_names = [Colors_str, Mans_str, Drinks_str, Animals_str, Smokes_str];
let output = "House".padEnd(10);
for (const name of Attrib_str) {
output += name.padEnd(10);
}
console.log(output);
for (let i = 0; i < 5; i++) {
output = String(i).padEnd(10);
for (let j = 0; j < 5; j++) {
output += attr_names[j][ha[i][j]].padEnd(10);
}
console.log(output);
}
}
// House number specific rules
const housenos = [
{ houseno: 2, a: Attrib.Drink, v: Drinks.Milk }, // Cond 9: In the middle house they drink milk.
{ houseno: 0, a: Attrib.Man, v: Mans.Norwegian } // Cond 10: The Norwegian lives in the first house.
];
// Attribute pair rules
const pairs = [
{ a1: Attrib.Man, v1: Mans.English, a2: Attrib.Color, v2: Colors.Red }, // Cond 2: The English man lives in the red house.
{ a1: Attrib.Man, v1: Mans.Swede, a2: Attrib.Animal, v2: Animals.Dog }, // Cond 3: The Swede has a dog.
{ a1: Attrib.Man, v1: Mans.Dane, a2: Attrib.Drink, v2: Drinks.Tea }, // Cond 4: The Dane drinks tea.
{ a1: Attrib.Color, v1: Colors.Green, a2: Attrib.Drink, v2: Drinks.Coffee }, // Cond 6: drink coffee in the green house.
{ a1: Attrib.Smoke, v1: Smokes.PallMall, a2: Attrib.Animal, v2: Animals.Birds }, // Cond 7: The man who smokes Pall Mall has birds.
{ a1: Attrib.Smoke, v1: Smokes.Dunhill, a2: Attrib.Color, v2: Colors.Yellow }, // Cond 8: In the yellow house they smoke Dunhill.
{ a1: Attrib.Smoke, v1: Smokes.BlueMaster, a2: Attrib.Drink, v2: Drinks.Beer }, // Cond 13: The man who smokes Blue Master drinks beer.
{ a1: Attrib.Man, v1: Mans.German, a2: Attrib.Smoke, v2: Smokes.Prince } // Cond 14: The German smokes Prince
];
// Next to rules
const nexttos = [
{ a1: Attrib.Smoke, v1: Smokes.Blend, a2: Attrib.Animal, v2: Animals.Cats }, // Cond 11: The man who smokes Blend lives in the house next to the house with cats.
{ a1: Attrib.Smoke, v1: Smokes.Dunhill, a2: Attrib.Animal, v2: Animals.Horse }, // Cond 12: In a house next to the house where they have a horse, they smoke Dunhill.
{ a1: Attrib.Man, v1: Mans.Norwegian, a2: Attrib.Color, v2: Colors.Blue }, // Cond 15: The Norwegian lives next to the blue house.
{ a1: Attrib.Smoke, v1: Smokes.Blend, a2: Attrib.Drink, v2: Drinks.Water } // Cond 16: They drink water in a house next to the house where they smoke Blend.
];
// Left of rules
const leftofs = [
{ a1: Attrib.Color, v1: Colors.Green, a2: Attrib.Color, v2: Colors.White } // Cond 5: The green house is immediately to the left of the white house.
];
function invalid(ha) {
// Check leftofs global rules
for (const rule of leftofs) {
if (ha[0][rule.a2] === rule.v2 || ha[4][rule.a1] === rule.v1) {
return true;
}
}
// Check rules for each house
for (let i = 0; i < 5; i++) {
// Check pair rules
for (const rule of pairs) {
if (ha[i][rule.a1] >= 0 && ha[i][rule.a2] >= 0 &&
((ha[i][rule.a1] === rule.v1 && ha[i][rule.a2] !== rule.v2) ||
(ha[i][rule.a1] !== rule.v1 && ha[i][rule.a2] === rule.v2))) {
return true;
}
}
// Check next to rules
for (const rule of nexttos) {
if (ha[i][rule.a1] === rule.v1) {
if (i === 0 && ha[i + 1][rule.a2] >= 0 && ha[i + 1][rule.a2] !== rule.v2) {
return true;
} else if (i === 4 && ha[i - 1][rule.a2] !== rule.v2) {
return true;
} else if (i > 0 && i < 4 &&
ha[i + 1][rule.a2] >= 0 && ha[i + 1][rule.a2] !== rule.v2 &&
ha[i - 1][rule.a2] !== rule.v2) {
return true;
}
}
}
// Check left of rules for this house
for (const rule of leftofs) {
if (i > 0 && ha[i][rule.a1] >= 0 &&
((ha[i - 1][rule.a1] === rule.v1 && ha[i][rule.a2] !== rule.v2) ||
(ha[i - 1][rule.a1] !== rule.v1 && ha[i][rule.a2] === rule.v2))) {
return true;
}
}
}
return false;
}
function search(used, ha, hno, attr) {
let 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 (let 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;
}
}
}
function main() {
// Initialize arrays
const used = Array(5).fill().map(() => Array(5).fill(false));
const ha = Array(5).fill().map(() => Array(5).fill(-1));
// Apply house number specific rules
for (const rule of housenos) {
ha[rule.houseno][rule.a] = rule.v;
used[rule.a][rule.v] = true;
}
// Start the search
search(used, ha, 0, 0);
}
// Run the program
main();