Skip to content

Commit 314d424

Browse files
authored
Add Zebra Puzzle exercise (#1647)
* Run `bin/configlet create --practice-exercise zebra-puzzle` * Fill in config * Add starter comment to zebra_puzzle.rb * Add tests * Add example solution
1 parent f6214fc commit 314d424

File tree

7 files changed

+236
-0
lines changed

7 files changed

+236
-0
lines changed

config.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,6 +1567,17 @@
15671567
"enumeration"
15681568
],
15691569
"difficulty": 8
1570+
},
1571+
{
1572+
"slug": "zebra-puzzle",
1573+
"name": "Zebra Puzzle",
1574+
"uuid": "0c585fee-0afc-44c3-b5bf-7f29ed6f5b42",
1575+
"practices": [],
1576+
"prerequisites": [
1577+
"arrays",
1578+
"advanced-enumeration"
1579+
],
1580+
"difficulty": 7
15701581
}
15711582
]
15721583
},
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Instructions
2+
3+
Solve the zebra puzzle.
4+
5+
1. There are five houses.
6+
2. The Englishman lives in the red house.
7+
3. The Spaniard owns the dog.
8+
4. Coffee is drunk in the green house.
9+
5. The Ukrainian drinks tea.
10+
6. The green house is immediately to the right of the ivory house.
11+
7. The Old Gold smoker owns snails.
12+
8. Kools are smoked in the yellow house.
13+
9. Milk is drunk in the middle house.
14+
10. The Norwegian lives in the first house.
15+
11. The man who smokes Chesterfields lives in the house next to the man with the fox.
16+
12. Kools are smoked in the house next to the house where the horse is kept.
17+
13. The Lucky Strike smoker drinks orange juice.
18+
14. The Japanese smokes Parliaments.
19+
15. The Norwegian lives next to the blue house.
20+
21+
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 smoke different brands of cigarettes.
22+
23+
Which of the residents drinks water?
24+
Who owns the zebra?
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"authors": ["fpsvogel"],
3+
"files": {
4+
"solution": [
5+
"zebra_puzzle.rb"
6+
],
7+
"test": [
8+
"zebra_puzzle_test.rb"
9+
],
10+
"example": [
11+
".meta/example.rb"
12+
]
13+
},
14+
"blurb": "Solve the zebra puzzle.",
15+
"source": "Wikipedia",
16+
"source_url": "https://en.wikipedia.org/wiki/Zebra_Puzzle"
17+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
CATEGORIES = {
2+
color: %i[
3+
red
4+
green
5+
blue
6+
yellow
7+
ivory
8+
].freeze,
9+
nationality: %i[
10+
englishman
11+
spaniard
12+
ukrainian
13+
norwegian
14+
japanese
15+
].freeze,
16+
beverage: %i[
17+
coffee
18+
tea
19+
milk
20+
orange_juice
21+
water
22+
].freeze,
23+
cigarette: %i[
24+
old_gold
25+
kool
26+
chesterfield
27+
lucky_strike
28+
parliament
29+
].freeze,
30+
pet: %i[
31+
dog
32+
snails
33+
fox
34+
horse
35+
zebra
36+
].freeze
37+
}.freeze
38+
39+
House = Data.define(*CATEGORIES.keys)
40+
41+
module ZebraPuzzle
42+
def self.water_drinker
43+
houses.find { _1.beverage == :water }.nationality.to_s.capitalize
44+
end
45+
46+
def self.zebra_owner
47+
houses.find { _1.pet == :zebra }.nationality.to_s.capitalize
48+
end
49+
50+
private_class_method def self.houses
51+
@houses ||= ZebraPuzzleSolver.new.final_row_of_houses
52+
end
53+
end
54+
55+
class ZebraPuzzleSolver
56+
def initialize
57+
@table = null_table
58+
end
59+
60+
def final_row_of_houses
61+
return @table unless @table == null_table
62+
63+
CATEGORIES.each_key do |key|
64+
add_into_table!(key)
65+
send("filter_by_#{key}!")
66+
end
67+
68+
raise "More than one final possibility exists" if @table.count > 1
69+
70+
@table.first
71+
end
72+
73+
private
74+
def null_table
75+
null_house = CATEGORIES.keys.to_h { |key| [key, nil] }
76+
77+
# Clue: There are five houses.
78+
[[null_house] * 5]
79+
end
80+
81+
def add_into_table!(new_key)
82+
permutations = CATEGORIES.fetch(new_key).permutation
83+
84+
@table.map! do |houses|
85+
permutations.map do |new_values|
86+
houses.zip(new_values).map do |house, new_value|
87+
House.new(**house.to_h.merge(new_key => new_value))
88+
end
89+
end
90+
end.flatten!(1)
91+
end
92+
93+
def filter_by_color!
94+
@table.select! do |houses|
95+
# Clue: The green house is immediately to the right of the ivory house.
96+
houses.index { _1.color == :green } == houses.index { _1.color == :ivory } + 1 &&
97+
# (Clue: The second house is blue. From these clues:)
98+
# - The Norwegian lives in the first house.
99+
# - The Norwegian lives next to the blue house.
100+
houses.index { _1.color == :blue } == 1
101+
end
102+
end
103+
104+
def filter_by_nationality!
105+
@table.select! do |houses|
106+
# Clue: The Englishman lives in the red house.
107+
houses.any? { _1.nationality == :englishman && _1.color == :red } &&
108+
# Clue: The Norwegian lives in the first house.
109+
houses.first.nationality == :norwegian
110+
end
111+
end
112+
113+
def filter_by_beverage!
114+
@table.select! do |houses|
115+
# Clue: Coffee is drunk in the green house.
116+
houses.any? { _1.beverage == :coffee && _1.color == :green } &&
117+
# Clue: The Ukrainian drinks tea.
118+
houses.any? { _1.nationality == :ukrainian && _1.beverage == :tea } &&
119+
# Clue: Milk is drunk in the middle house.
120+
houses[2].beverage == :milk
121+
end
122+
end
123+
124+
def filter_by_cigarette!
125+
@table.select! do |houses|
126+
# Clue: Kools are smoked in the yellow house.
127+
houses.any? { _1.cigarette == :kool && _1.color == :yellow } &&
128+
# Clue: The Lucky Strike smoker drinks orange juice.
129+
houses.any? { _1.cigarette == :lucky_strike && _1.beverage == :orange_juice } &&
130+
# Clue: The Japanese smokes Parliaments.
131+
houses.any? { _1.nationality == :japanese && _1.cigarette == :parliament }
132+
end
133+
end
134+
135+
# rubocop:disable Metrics/AbcSize
136+
137+
def filter_by_pet!
138+
@table.select! do |houses|
139+
# Clue: The Spaniard owns the dog.
140+
houses.any? { _1.nationality == :spaniard && _1.pet == :dog } &&
141+
# Clue: The Old Gold smoker owns snails.
142+
houses.any? { _1.cigarette == :old_gold && _1.pet == :snails } &&
143+
# Clue: The man who smokes Chesterfields lives in the house next to the man with the fox.
144+
(houses.index { _1.cigarette == :chesterfield } - houses.index { _1.pet == :fox }).abs == 1 &&
145+
# Clue: Kools are smoked in the house next to the house where the horse is kept.
146+
(houses.index { _1.cigarette == :kool } - houses.index { _1.pet == :horse }).abs == 1
147+
end
148+
end
149+
# rubocop:enable Metrics/AbcSize
150+
end
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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
=begin
2+
Write your code for the 'Zebra Puzzle' exercise in this file. Make the tests in
3+
`zebra_puzzle_test.rb` pass.
4+
To get started with TDD, see the `README.md` file in your
5+
`ruby/zebra-puzzle` directory.
6+
=end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
require 'minitest/autorun'
2+
require_relative 'zebra_puzzle'
3+
4+
class ZebraPuzzleTest < Minitest::Test
5+
def test_resident_who_drinks_water
6+
assert_equal ZebraPuzzle.water_drinker, "Norwegian"
7+
end
8+
9+
def test_resident_who_owns_zebra
10+
assert_equal ZebraPuzzle.zebra_owner, "Japanese"
11+
end
12+
end

0 commit comments

Comments
 (0)