Skip to content

Commit 76b48b6

Browse files
authored
Add Zebra Puzzle Exercise (#623)
1 parent 2b6b6d9 commit 76b48b6

File tree

7 files changed

+384
-0
lines changed

7 files changed

+384
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,14 @@
11551155
"practices": [],
11561156
"prerequisites": [],
11571157
"difficulty": 6
1158+
},
1159+
{
1160+
"slug": "zebra-puzzle",
1161+
"name": "Zebra Puzzle",
1162+
"uuid": "8a87919b-3bd0-4fc8-9774-daecd148de42",
1163+
"practices": [],
1164+
"prerequisites": [],
1165+
"difficulty": 7
11581166
}
11591167
]
11601168
},
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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"tomasnorre"
4+
],
5+
"files": {
6+
"solution": [
7+
"ZebraPuzzle.php"
8+
],
9+
"test": [
10+
"ZebraPuzzleTest.php"
11+
],
12+
"example": [
13+
".meta/example.php"
14+
]
15+
},
16+
"blurb": "Solve the zebra puzzle.",
17+
"source": "Wikipedia",
18+
"source_url": "https://en.wikipedia.org/wiki/Zebra_Puzzle"
19+
}
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
<?php
2+
3+
/*
4+
* By adding type hints and enabling strict type checking, code can become
5+
* easier to read, self-documenting and reduce the number of potential bugs.
6+
* By default, type declarations are non-strict, which means they will attempt
7+
* to change the original type to match the type specified by the
8+
* type-declaration.
9+
*
10+
* In other words, if you pass a string to a function requiring a float,
11+
* it will attempt to convert the string value to a float.
12+
*
13+
* To enable strict mode, a single declare directive must be placed at the top
14+
* of the file.
15+
* This means that the strictness of typing is configured on a per-file basis.
16+
* This directive not only affects the type declarations of parameters, but also
17+
* a function's return type.
18+
*
19+
* For more info review the Concept on strict type checking in the PHP track
20+
* <link>.
21+
*
22+
* To disable strict typing, comment out the directive below.
23+
*/
24+
25+
declare(strict_types=1);
26+
27+
class ZebraPuzzle
28+
{
29+
private $waterDrinker = '';
30+
private $zebraOwner = '';
31+
32+
private const FIRST = 1;
33+
private const MIDDLE = 3;
34+
35+
private $red = 0;
36+
private $green = 0;
37+
private $ivory = 0;
38+
private $yellow = 0;
39+
private $blue = 0;
40+
41+
private $englishman = 0;
42+
private $spaniard = 0;
43+
private $ukrainian = 0;
44+
private $japanese = 0;
45+
private $norwegian = 0;
46+
47+
private $coffee = 0;
48+
private $tea = 0;
49+
private $milk = 0;
50+
private $orangeJuice = 0;
51+
private $water = 0;
52+
53+
private $oldGold = 0;
54+
private $kools = 0;
55+
private $chesterfields = 0;
56+
private $luckyStrike = 0;
57+
private $parliaments = 0;
58+
59+
private $dog = 0;
60+
private $snails = 0;
61+
private $fox = 0;
62+
private $horse = 0;
63+
private $zebra = 0;
64+
65+
private $nationalityNames = [];
66+
67+
private $possiblePermutations;
68+
69+
public function __construct()
70+
{
71+
$this->possiblePermutations = $this->permuteValues([1, 2, 3, 4, 5]);
72+
$this->solve();
73+
}
74+
75+
public function waterDrinker(): string
76+
{
77+
return $this->waterDrinker;
78+
}
79+
80+
public function zebraOwner(): string
81+
{
82+
return $this->zebraOwner;
83+
}
84+
85+
private function permuteValues(array $array): array
86+
{
87+
$result = [];
88+
89+
$length = count($array);
90+
91+
if ($length === 0) {
92+
return [[]];
93+
}
94+
95+
foreach ($array as $index => $value) {
96+
$rest = $this->permuteValues(array_merge(array_slice($array, 0, $index), array_slice($array, $index + 1)));
97+
98+
if (empty($rest)) {
99+
$result[] = [$value];
100+
} else {
101+
foreach ($rest as $r) {
102+
$result[] = array_merge([$value], $r);
103+
}
104+
}
105+
}
106+
107+
return $result;
108+
}
109+
110+
private function isRightOf($houseA, $houseB): bool
111+
{
112+
return $houseA - 1 === $houseB;
113+
}
114+
115+
private function nextTo($houseA, $houseB): bool
116+
{
117+
return $this->isRightOf($houseA, $houseB) || $this->isRightOf($houseB, $houseA);
118+
}
119+
120+
private function solve(): void
121+
{
122+
foreach ($this->possiblePermutations as $permutation) {
123+
$this->solveHouseColors($permutation);
124+
}
125+
}
126+
127+
private function solveHouseColors($permutation): void
128+
{
129+
$this->red = $permutation[0];
130+
$this->green = $permutation[1];
131+
$this->ivory = $permutation[2];
132+
$this->yellow = $permutation[3];
133+
$this->blue = $permutation[4];
134+
135+
if ($this->isRightOf($this->green, $this->ivory)) { // Clue #6
136+
foreach ($this->possiblePermutations as $perm) {
137+
$this->solveNationalities($perm);
138+
}
139+
}
140+
}
141+
142+
private function solveNationalities($permutation): void
143+
{
144+
$this->englishman = $permutation[0];
145+
$this->spaniard = $permutation[1];
146+
$this->ukrainian = $permutation[2];
147+
$this->japanese = $permutation[3];
148+
$this->norwegian = $permutation[4];
149+
150+
if (
151+
$this->red === $this->englishman && // Clue #2
152+
$this->norwegian === self::FIRST && // Clue #10
153+
$this->nextTo($this->norwegian, $this->blue) // Clue #15
154+
) {
155+
$this->nationalityNames[$this->englishman] = 'Englishman';
156+
$this->nationalityNames[$this->spaniard] = 'Spaniard';
157+
$this->nationalityNames[$this->ukrainian] = 'Ukrainian';
158+
$this->nationalityNames[$this->japanese] = 'Japanese';
159+
$this->nationalityNames[$this->norwegian] = 'Norwegian';
160+
161+
foreach ($this->possiblePermutations as $perm) {
162+
$this->solveBeverages($perm);
163+
}
164+
}
165+
}
166+
167+
private function solveBeverages($permutation): void
168+
{
169+
$this->coffee = $permutation[0];
170+
$this->tea = $permutation[1];
171+
$this->milk = $permutation[2];
172+
$this->orangeJuice = $permutation[3];
173+
$this->water = $permutation[4];
174+
175+
if (
176+
$this->coffee === $this->green && // Clue #4
177+
$this->ukrainian === $this->tea && // Clue #5
178+
$this->milk === self::MIDDLE // Clue #9
179+
) {
180+
foreach ($this->possiblePermutations as $perm) {
181+
$this->solveCigars($perm);
182+
}
183+
}
184+
}
185+
186+
private function solveCigars($permutation): void
187+
{
188+
$this->oldGold = $permutation[0];
189+
$this->kools = $permutation[1];
190+
$this->chesterfields = $permutation[2];
191+
$this->luckyStrike = $permutation[3];
192+
$this->parliaments = $permutation[4];
193+
194+
if (
195+
$this->kools === $this->yellow && // Clue #8
196+
$this->luckyStrike === $this->orangeJuice && // Clue #13
197+
$this->japanese === $this->parliaments // Clue #14
198+
) {
199+
foreach ($this->possiblePermutations as $perm) {
200+
$this->solvePets($perm);
201+
}
202+
}
203+
}
204+
205+
private function solvePets($permutation): void
206+
{
207+
$this->dog = $permutation[0];
208+
$this->snails = $permutation[1];
209+
$this->fox = $permutation[2];
210+
$this->horse = $permutation[3];
211+
$this->zebra = $permutation[4];
212+
213+
if (
214+
$this->spaniard === $this->dog && // Clue #3
215+
$this->oldGold === $this->snails && // Clue #7
216+
$this->nextTo($this->chesterfields, $this->fox) && // Clue #11
217+
$this->nextTo($this->kools, $this->horse) // Clue #12
218+
) {
219+
$this->waterDrinker = $this->nationalityNames[$this->water];
220+
$this->zebraOwner = $this->nationalityNames[$this->zebra];
221+
}
222+
}
223+
}
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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
/*
4+
* By adding type hints and enabling strict type checking, code can become
5+
* easier to read, self-documenting and reduce the number of potential bugs.
6+
* By default, type declarations are non-strict, which means they will attempt
7+
* to change the original type to match the type specified by the
8+
* type-declaration.
9+
*
10+
* In other words, if you pass a string to a function requiring a float,
11+
* it will attempt to convert the string value to a float.
12+
*
13+
* To enable strict mode, a single declare directive must be placed at the top
14+
* of the file.
15+
* This means that the strictness of typing is configured on a per-file basis.
16+
* This directive not only affects the type declarations of parameters, but also
17+
* a function's return type.
18+
*
19+
* For more info review the Concept on strict type checking in the PHP track
20+
* <link>.
21+
*
22+
* To disable strict typing, comment out the directive below.
23+
*/
24+
25+
declare(strict_types=1);
26+
27+
class ZebraPuzzle
28+
{
29+
public function waterDrinker(): string
30+
{
31+
throw new \BadMethodCallException(sprintf('Implement the %s method', __FUNCTION__));
32+
}
33+
34+
public function zebraOwner(): string
35+
{
36+
throw new \BadMethodCallException(sprintf('Implement the %s method', __FUNCTION__));
37+
}
38+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
/*
4+
* By adding type hints and enabling strict type checking, code can become
5+
* easier to read, self-documenting and reduce the number of potential bugs.
6+
* By default, type declarations are non-strict, which means they will attempt
7+
* to change the original type to match the type specified by the
8+
* type-declaration.
9+
*
10+
* In other words, if you pass a string to a function requiring a float,
11+
* it will attempt to convert the string value to a float.
12+
*
13+
* To enable strict mode, a single declare directive must be placed at the top
14+
* of the file.
15+
* This means that the strictness of typing is configured on a per-file basis.
16+
* This directive not only affects the type declarations of parameters, but also
17+
* a function's return type.
18+
*
19+
* For more info review the Concept on strict type checking in the PHP track
20+
* <link>.
21+
*
22+
* To disable strict typing, comment out the directive below.
23+
*/
24+
25+
declare(strict_types=1);
26+
27+
class ZebraPuzzleTest extends PHPUnit\Framework\TestCase
28+
{
29+
private ZebraPuzzle $zebraPuzzle;
30+
31+
public static function setUpBeforeClass(): void
32+
{
33+
require_once 'ZebraPuzzle.php';
34+
}
35+
36+
public function setUp(): void
37+
{
38+
$this->zebraPuzzle = new ZebraPuzzle();
39+
}
40+
41+
/**
42+
* uuid: 16efb4e4-8ad7-4d5e-ba96-e5537b66fd42
43+
*/
44+
public function testResidentWhoDrinksWater(): void
45+
{
46+
$this->assertEquals('Norwegian', $this->zebraPuzzle->waterDrinker());
47+
}
48+
49+
/**
50+
* uuid: 084d5b8b-24e2-40e6-b008-c800da8cd257
51+
*/
52+
public function testResidentWhoOwnsZebra(): void
53+
{
54+
$this->assertEquals('Japanese', $this->zebraPuzzle->zebraOwner());
55+
}
56+
}

0 commit comments

Comments
 (0)