Skip to content

Commit b03cb6f

Browse files
Add zebra-puzzle exercise (#643)
1 parent f89673b commit b03cb6f

11 files changed

Lines changed: 250 additions & 1 deletion

File tree

bin/add-practice-exercise

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ fi
6767
./bin/fetch-configlet
6868
./bin/configlet create --practice-exercise "${slug}" --author "${author}" --difficulty "${difficulty}"
6969

70-
filter='.exercises.practice = (.exercises.practice | sort_by(.difficulty, (.name|ascii_upcase))'
70+
filter='.exercises.practice = (.exercises.practice | sort_by(.difficulty, (.name|ascii_upcase)))'
7171
jq "${filter}" config.json > config.sorted && mv config.sorted config.json
7272

7373
exercise_dir="exercises/practice/${slug}"

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,6 +1459,14 @@
14591459
"strings"
14601460
]
14611461
},
1462+
{
1463+
"slug": "zebra-puzzle",
1464+
"name": "Zebra Puzzle",
1465+
"uuid": "87be2159-daf5-4a6f-b7e1-61dc93edb1e7",
1466+
"practices": [],
1467+
"prerequisites": [],
1468+
"difficulty": 8
1469+
},
14621470
{
14631471
"slug": "pov",
14641472
"name": "POV",
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
return {
2+
default = {
3+
ROOT = { '.' }
4+
}
5+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Instructions
2+
3+
Your task is to solve the Zebra Puzzle to find the answer to these two questions:
4+
5+
- Which of the residents drinks water?
6+
- Who owns the zebra?
7+
8+
## Puzzle
9+
10+
The following 15 statements are all known to be true:
11+
12+
1. There are five houses.
13+
2. The Englishman lives in the red house.
14+
3. The Spaniard owns the dog.
15+
4. The person in the green house drinks coffee.
16+
5. The Ukrainian drinks tea.
17+
6. The green house is immediately to the right of the ivory house.
18+
7. The snail owner likes to go dancing.
19+
8. The person in the yellow house is a painter.
20+
9. The person in the middle house drinks milk.
21+
10. The Norwegian lives in the first house.
22+
11. The person who enjoys reading lives in the house next to the person with the fox.
23+
12. The painter's house is next to the house with the horse.
24+
13. The person who plays football drinks orange juice.
25+
14. The Japanese person plays chess.
26+
15. The Norwegian lives next to the blue house.
27+
28+
Additionally, each of the five houses is painted a different color, and their inhabitants are of different national extractions, own different pets, drink different beverages and engage in different hobbies.
29+
30+
~~~~exercism/note
31+
There are 24 billion (5!⁵ = 24,883,200,000) possible solutions, so try ruling out as many solutions as possible.
32+
~~~~
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Introduction
2+
3+
The Zebra Puzzle is a famous logic puzzle in which there are five houses, each painted a different color.
4+
The houses have different inhabitants, who have different nationalities, own different pets, drink different beverages and enjoy different hobbies.
5+
6+
To help you solve the puzzle, you're given 15 statements describing the solution.
7+
However, only by combining the information in _all_ statements will you be able to find the solution to the puzzle.
8+
9+
~~~~exercism/note
10+
The Zebra Puzzle is a [Constraint satisfaction problem (CSP)][constraint-satisfaction-problem].
11+
In such a problem, you have a set of possible values and a set of constraints that limit which values are valid.
12+
Another well-known CSP is Sudoku.
13+
14+
[constraint-satisfaction-problem]: https://en.wikipedia.org/wiki/Constraint_satisfaction_problem
15+
~~~~
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"keiravillekode"
4+
],
5+
"files": {
6+
"solution": [
7+
"zebra-puzzle.lua"
8+
],
9+
"test": [
10+
"zebra-puzzle_spec.lua"
11+
],
12+
"example": [
13+
".meta/example.lua"
14+
]
15+
},
16+
"blurb": "Solve the zebra puzzle.",
17+
"source": "Wikipedia",
18+
"source_url": "https://en.wikipedia.org/wiki/Zebra_Puzzle"
19+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
local zebra_puzzle = {}
2+
3+
local function permutations(a)
4+
local n
5+
6+
return function()
7+
if not n then
8+
n = #a
9+
return a
10+
end
11+
12+
-- Step 1. Find largest j such that a[j] > a[j + 1].
13+
local j = n - 1
14+
while j >= 1 and a[j] <= a[j + 1] do
15+
j = j - 1
16+
end
17+
if j < 1 then
18+
return nil
19+
end
20+
21+
-- Step 2. Find largest l such that a[j] > a[l], then swap.
22+
local l = n
23+
while a[j] <= a[l] do
24+
l = l - 1
25+
end
26+
a[j], a[l] = a[l], a[j]
27+
28+
-- Step 3. Reverse a[j+1] ... a[n].
29+
local lo, hi = j + 1, n
30+
while lo < hi do
31+
a[lo], a[hi] = a[hi], a[lo]
32+
lo = lo + 1
33+
hi = hi - 1
34+
end
35+
36+
return a
37+
end
38+
end
39+
40+
local function index_of(t, val)
41+
for i, v in ipairs(t) do
42+
if v == val then
43+
return i
44+
end
45+
end
46+
return nil
47+
end
48+
49+
local function next_to(a, b)
50+
return math.abs(a - b) == 1
51+
end
52+
53+
local water_drinker, zebra_owner
54+
55+
local function solve()
56+
for colors in permutations({ 'yellow', 'red', 'ivory', 'green', 'blue' }) do
57+
-- 6. The green house is immediately to the right of the ivory house.
58+
if index_of(colors, 'green') == index_of(colors, 'ivory') + 1 then
59+
for drinks in permutations({ 'water', 'tea', 'orange juice', 'milk', 'coffee' }) do
60+
-- 4. Coffee is drunk in the green house.
61+
-- 9. Milk is drunk in the middle house.
62+
if index_of(drinks, 'coffee') == index_of(colors, 'green') and drinks[3] == 'milk' then
63+
for hobbies in permutations({ 'reading', 'painting', 'football', 'dancing', 'chess' }) do
64+
-- 8. The person in the yellow house is a painter.
65+
-- 13. The person who plays football drinks orange juice.
66+
if index_of(hobbies, 'painting') == index_of(colors, 'yellow') and index_of(hobbies, 'football') ==
67+
index_of(drinks, 'orange juice') then
68+
for nationalities in permutations({ 'Ukrainian', 'Spaniard', 'Norwegian', 'Japanese', 'Englishman' }) do
69+
-- 10. The Norwegian lives in the first house.
70+
-- 2. The Englishman lives in the red house.
71+
-- 15. The Norwegian lives next to the blue house.
72+
-- 5. The Ukrainian drinks tea.
73+
-- 14. The Japanese person plays chess.
74+
if nationalities[1] == 'Norwegian' and index_of(colors, 'red') == index_of(nationalities, 'Englishman') and
75+
next_to(index_of(nationalities, 'Norwegian'), index_of(colors, 'blue')) and index_of(drinks, 'tea') ==
76+
index_of(nationalities, 'Ukrainian') and index_of(hobbies, 'chess') ==
77+
index_of(nationalities, 'Japanese') then
78+
for pets in permutations({ 'zebra', 'snail', 'horse', 'fox', 'dog' }) do
79+
-- 3. The Spaniard owns the dog.
80+
-- 7. The snail owner likes to go dancing.
81+
-- 11. The person who enjoys reading lives in the house next to the person with the fox.
82+
-- 12. The painter's house is next to the house with the horse.
83+
if index_of(pets, 'dog') == index_of(nationalities, 'Spaniard') and index_of(pets, 'snail') ==
84+
index_of(hobbies, 'dancing') and next_to(index_of(hobbies, 'reading'), index_of(pets, 'fox')) and
85+
next_to(index_of(hobbies, 'painting'), index_of(pets, 'horse')) then
86+
return nationalities[index_of(drinks, 'water')], nationalities[index_of(pets, 'zebra')]
87+
end
88+
end
89+
end
90+
end
91+
end
92+
end
93+
end
94+
end
95+
end
96+
end
97+
end
98+
99+
local function ensure_solved()
100+
if not water_drinker then
101+
water_drinker, zebra_owner = solve()
102+
end
103+
end
104+
105+
function zebra_puzzle.drinks_water()
106+
ensure_solved()
107+
return water_drinker
108+
end
109+
110+
function zebra_puzzle.owns_zebra()
111+
ensure_solved()
112+
return zebra_owner
113+
end
114+
115+
return zebra_puzzle
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
local function snake_case(str)
2+
local s = str:gsub('%u', function(c)
3+
return '_' .. c:lower()
4+
end)
5+
if s:sub(1, 1) == '_' then
6+
s = s:sub(2)
7+
end
8+
return s
9+
end
10+
11+
return {
12+
module_name = 'zebra_puzzle',
13+
14+
generate_test = function(case)
15+
local template = [[
16+
assert.equal('%s', zebra_puzzle.%s())]]
17+
return template:format(case.expected, snake_case(case.property))
18+
end
19+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[16efb4e4-8ad7-4d5e-ba96-e5537b66fd42]
13+
description = "resident who drinks water"
14+
15+
[084d5b8b-24e2-40e6-b008-c800da8cd257]
16+
description = "resident who owns zebra"
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
local zebra_puzzle = {}
2+
3+
function zebra_puzzle.drinks_water()
4+
end
5+
6+
function zebra_puzzle.owns_zebra()
7+
end
8+
9+
return zebra_puzzle

0 commit comments

Comments
 (0)