diff --git a/config.json b/config.json index d17efe14..4fb6d54d 100644 --- a/config.json +++ b/config.json @@ -1155,6 +1155,14 @@ "practices": [], "prerequisites": [], "difficulty": 6 + }, + { + "slug": "zebra-puzzle", + "name": "Zebra Puzzle", + "uuid": "8a87919b-3bd0-4fc8-9774-daecd148de42", + "practices": [], + "prerequisites": [], + "difficulty": 7 } ] }, diff --git a/exercises/practice/zebra-puzzle/.docs/instructions.md b/exercises/practice/zebra-puzzle/.docs/instructions.md new file mode 100644 index 00000000..6d62d18e --- /dev/null +++ b/exercises/practice/zebra-puzzle/.docs/instructions.md @@ -0,0 +1,24 @@ +# Instructions + +Solve the zebra puzzle. + +1. There are five houses. +2. The Englishman lives in the red house. +3. The Spaniard owns the dog. +4. Coffee is drunk in the green house. +5. The Ukrainian drinks tea. +6. The green house is immediately to the right of the ivory house. +7. The Old Gold smoker owns snails. +8. Kools are smoked in the yellow house. +9. Milk is drunk in the middle house. +10. The Norwegian lives in the first house. +11. The man who smokes Chesterfields lives in the house next to the man with the fox. +12. Kools are smoked in the house next to the house where the horse is kept. +13. The Lucky Strike smoker drinks orange juice. +14. The Japanese smokes Parliaments. +15. The Norwegian lives next to the blue house. + +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. + +Which of the residents drinks water? +Who owns the zebra? diff --git a/exercises/practice/zebra-puzzle/.meta/config.json b/exercises/practice/zebra-puzzle/.meta/config.json new file mode 100644 index 00000000..464551f3 --- /dev/null +++ b/exercises/practice/zebra-puzzle/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "tomasnorre" + ], + "files": { + "solution": [ + "ZebraPuzzle.php" + ], + "test": [ + "ZebraPuzzleTest.php" + ], + "example": [ + ".meta/example.php" + ] + }, + "blurb": "Solve the zebra puzzle.", + "source": "Wikipedia", + "source_url": "https://en.wikipedia.org/wiki/Zebra_Puzzle" +} diff --git a/exercises/practice/zebra-puzzle/.meta/example.php b/exercises/practice/zebra-puzzle/.meta/example.php new file mode 100644 index 00000000..25bc6b51 --- /dev/null +++ b/exercises/practice/zebra-puzzle/.meta/example.php @@ -0,0 +1,223 @@ +. + * + * To disable strict typing, comment out the directive below. + */ + +declare(strict_types=1); + +class ZebraPuzzle +{ + private $waterDrinker = ''; + private $zebraOwner = ''; + + private const FIRST = 1; + private const MIDDLE = 3; + + private $red = 0; + private $green = 0; + private $ivory = 0; + private $yellow = 0; + private $blue = 0; + + private $englishman = 0; + private $spaniard = 0; + private $ukrainian = 0; + private $japanese = 0; + private $norwegian = 0; + + private $coffee = 0; + private $tea = 0; + private $milk = 0; + private $orangeJuice = 0; + private $water = 0; + + private $oldGold = 0; + private $kools = 0; + private $chesterfields = 0; + private $luckyStrike = 0; + private $parliaments = 0; + + private $dog = 0; + private $snails = 0; + private $fox = 0; + private $horse = 0; + private $zebra = 0; + + private $nationalityNames = []; + + private $possiblePermutations; + + public function __construct() + { + $this->possiblePermutations = $this->permuteValues([1, 2, 3, 4, 5]); + $this->solve(); + } + + public function waterDrinker(): string + { + return $this->waterDrinker; + } + + public function zebraOwner(): string + { + return $this->zebraOwner; + } + + private function permuteValues(array $array): array + { + $result = []; + + $length = count($array); + + if ($length === 0) { + return [[]]; + } + + foreach ($array as $index => $value) { + $rest = $this->permuteValues(array_merge(array_slice($array, 0, $index), array_slice($array, $index + 1))); + + if (empty($rest)) { + $result[] = [$value]; + } else { + foreach ($rest as $r) { + $result[] = array_merge([$value], $r); + } + } + } + + return $result; + } + + private function isRightOf($houseA, $houseB): bool + { + return $houseA - 1 === $houseB; + } + + private function nextTo($houseA, $houseB): bool + { + return $this->isRightOf($houseA, $houseB) || $this->isRightOf($houseB, $houseA); + } + + private function solve(): void + { + foreach ($this->possiblePermutations as $permutation) { + $this->solveHouseColors($permutation); + } + } + + private function solveHouseColors($permutation): void + { + $this->red = $permutation[0]; + $this->green = $permutation[1]; + $this->ivory = $permutation[2]; + $this->yellow = $permutation[3]; + $this->blue = $permutation[4]; + + if ($this->isRightOf($this->green, $this->ivory)) { // Clue #6 + foreach ($this->possiblePermutations as $perm) { + $this->solveNationalities($perm); + } + } + } + + private function solveNationalities($permutation): void + { + $this->englishman = $permutation[0]; + $this->spaniard = $permutation[1]; + $this->ukrainian = $permutation[2]; + $this->japanese = $permutation[3]; + $this->norwegian = $permutation[4]; + + if ( + $this->red === $this->englishman && // Clue #2 + $this->norwegian === self::FIRST && // Clue #10 + $this->nextTo($this->norwegian, $this->blue) // Clue #15 + ) { + $this->nationalityNames[$this->englishman] = 'Englishman'; + $this->nationalityNames[$this->spaniard] = 'Spaniard'; + $this->nationalityNames[$this->ukrainian] = 'Ukrainian'; + $this->nationalityNames[$this->japanese] = 'Japanese'; + $this->nationalityNames[$this->norwegian] = 'Norwegian'; + + foreach ($this->possiblePermutations as $perm) { + $this->solveBeverages($perm); + } + } + } + + private function solveBeverages($permutation): void + { + $this->coffee = $permutation[0]; + $this->tea = $permutation[1]; + $this->milk = $permutation[2]; + $this->orangeJuice = $permutation[3]; + $this->water = $permutation[4]; + + if ( + $this->coffee === $this->green && // Clue #4 + $this->ukrainian === $this->tea && // Clue #5 + $this->milk === self::MIDDLE // Clue #9 + ) { + foreach ($this->possiblePermutations as $perm) { + $this->solveCigars($perm); + } + } + } + + private function solveCigars($permutation): void + { + $this->oldGold = $permutation[0]; + $this->kools = $permutation[1]; + $this->chesterfields = $permutation[2]; + $this->luckyStrike = $permutation[3]; + $this->parliaments = $permutation[4]; + + if ( + $this->kools === $this->yellow && // Clue #8 + $this->luckyStrike === $this->orangeJuice && // Clue #13 + $this->japanese === $this->parliaments // Clue #14 + ) { + foreach ($this->possiblePermutations as $perm) { + $this->solvePets($perm); + } + } + } + + private function solvePets($permutation): void + { + $this->dog = $permutation[0]; + $this->snails = $permutation[1]; + $this->fox = $permutation[2]; + $this->horse = $permutation[3]; + $this->zebra = $permutation[4]; + + if ( + $this->spaniard === $this->dog && // Clue #3 + $this->oldGold === $this->snails && // Clue #7 + $this->nextTo($this->chesterfields, $this->fox) && // Clue #11 + $this->nextTo($this->kools, $this->horse) // Clue #12 + ) { + $this->waterDrinker = $this->nationalityNames[$this->water]; + $this->zebraOwner = $this->nationalityNames[$this->zebra]; + } + } +} diff --git a/exercises/practice/zebra-puzzle/.meta/tests.toml b/exercises/practice/zebra-puzzle/.meta/tests.toml new file mode 100644 index 00000000..56c21c7a --- /dev/null +++ b/exercises/practice/zebra-puzzle/.meta/tests.toml @@ -0,0 +1,16 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[16efb4e4-8ad7-4d5e-ba96-e5537b66fd42] +description = "resident who drinks water" + +[084d5b8b-24e2-40e6-b008-c800da8cd257] +description = "resident who owns zebra" diff --git a/exercises/practice/zebra-puzzle/ZebraPuzzle.php b/exercises/practice/zebra-puzzle/ZebraPuzzle.php new file mode 100644 index 00000000..8fd4f99d --- /dev/null +++ b/exercises/practice/zebra-puzzle/ZebraPuzzle.php @@ -0,0 +1,38 @@ +. + * + * To disable strict typing, comment out the directive below. + */ + +declare(strict_types=1); + +class ZebraPuzzle +{ + public function waterDrinker(): string + { + throw new \BadMethodCallException(sprintf('Implement the %s method', __FUNCTION__)); + } + + public function zebraOwner(): string + { + throw new \BadMethodCallException(sprintf('Implement the %s method', __FUNCTION__)); + } +} diff --git a/exercises/practice/zebra-puzzle/ZebraPuzzleTest.php b/exercises/practice/zebra-puzzle/ZebraPuzzleTest.php new file mode 100644 index 00000000..a3f8baaa --- /dev/null +++ b/exercises/practice/zebra-puzzle/ZebraPuzzleTest.php @@ -0,0 +1,56 @@ +. + * + * To disable strict typing, comment out the directive below. + */ + +declare(strict_types=1); + +class ZebraPuzzleTest extends PHPUnit\Framework\TestCase +{ + private ZebraPuzzle $zebraPuzzle; + + public static function setUpBeforeClass(): void + { + require_once 'ZebraPuzzle.php'; + } + + public function setUp(): void + { + $this->zebraPuzzle = new ZebraPuzzle(); + } + + /** + * uuid: 16efb4e4-8ad7-4d5e-ba96-e5537b66fd42 + */ + public function testResidentWhoDrinksWater(): void + { + $this->assertEquals('Norwegian', $this->zebraPuzzle->waterDrinker()); + } + + /** + * uuid: 084d5b8b-24e2-40e6-b008-c800da8cd257 + */ + public function testResidentWhoOwnsZebra(): void + { + $this->assertEquals('Japanese', $this->zebraPuzzle->zebraOwner()); + } +}