From 2c65192fd17bdfbcfad4690dd37252349b8ea863 Mon Sep 17 00:00:00 2001 From: Blake Lewis Date: Fri, 26 Jan 2024 07:55:27 -0800 Subject: [PATCH] Add the Zebra Puzzle as a practice exercise. (#336) * Add the Zebra Puzzle as a practice exercise. * Call error in stub functions instead of returning dummy value * Add missing parens --- config.json | 8 ++ .../zebra-puzzle/.docs/instructions.md | 24 ++++ .../practice/zebra-puzzle/.meta/config.json | 17 +++ .../practice/zebra-puzzle/.meta/example.rkt | 119 ++++++++++++++++++ .../practice/zebra-puzzle/.meta/tests.toml | 16 +++ .../zebra-puzzle/zebra-puzzle-test.rkt | 18 +++ .../practice/zebra-puzzle/zebra-puzzle.rkt | 9 ++ 7 files changed, 211 insertions(+) create mode 100644 exercises/practice/zebra-puzzle/.docs/instructions.md create mode 100644 exercises/practice/zebra-puzzle/.meta/config.json create mode 100644 exercises/practice/zebra-puzzle/.meta/example.rkt create mode 100644 exercises/practice/zebra-puzzle/.meta/tests.toml create mode 100644 exercises/practice/zebra-puzzle/zebra-puzzle-test.rkt create mode 100644 exercises/practice/zebra-puzzle/zebra-puzzle.rkt diff --git a/config.json b/config.json index 3a841ad3..1584e9b2 100644 --- a/config.json +++ b/config.json @@ -558,6 +558,14 @@ "practices": [], "prerequisites": [], "difficulty": 1 + }, + { + "slug": "zebra-puzzle", + "name": "Zebra Puzzle", + "uuid": "cf7f3ccd-babc-417c-ba74-937c7e674065", + "practices": [], + "prerequisites": [], + "difficulty": 6 } ] }, 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..f128829d --- /dev/null +++ b/exercises/practice/zebra-puzzle/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": ["blakelewis"], + "files": { + "solution": [ + "zebra-puzzle.rkt" + ], + "test": [ + "zebra-puzzle-test.rkt" + ], + "example": [ + ".meta/example.rkt" + ] + }, + "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.rkt b/exercises/practice/zebra-puzzle/.meta/example.rkt new file mode 100644 index 00000000..68918179 --- /dev/null +++ b/exercises/practice/zebra-puzzle/.meta/example.rkt @@ -0,0 +1,119 @@ +#lang racket + +(provide owns-zebra drinks-water) + +(struct house (number color owner pet drink smokes) #:transparent) + +(define all-houses + (for*/list ([n '(1 2 3 4 5)] + [c '(red green ivory yellow blue)] + [o '(Englishman Spaniard Ukrainian + Norwegian Japanese)] + [p '(dog snails fox horse zebra)] + [d '(coffee tea milk orange-juice water)] + [s '(Old-Gold Kools Chesterfields + Lucky-Strike Parliaments)]) + (house n c o p d s))) + +(define house-constraints + (list + (λ (h) (eq? (eq? (house-owner h) 'Englishman) + (eq? (house-color h) 'red))) + (λ (h) (eq? (eq? (house-owner h) 'Spaniard) + (eq? (house-pet h) 'dog))) + (λ (h) (eq? (eq? (house-drink h) 'coffee) + (eq? (house-color h) 'green))) + (λ (h) (eq? (eq? (house-owner h) 'Ukrainian) + (eq? (house-drink h) 'tea))) + (λ (h) (eq? (eq? (house-smokes h) 'Old-Gold) + (eq? (house-pet h) 'snails))) + (λ (h) (eq? (eq? (house-smokes h) 'Kools) + (eq? (house-color h) 'yellow))) + (λ (h) (eq? (eq? (house-drink h) 'milk) + (= (house-number h) 3))) + (λ (h) (eq? (eq? (house-owner h) 'Norwegian) + (= (house-number h) 1))) + (λ (h) (eq? (eq? (house-smokes h) 'Lucky-Strike) + (eq? (house-drink h) 'orange-juice))) + (λ (h) (eq? (eq? (house-owner h) 'Japanese) + (eq? (house-smokes h) 'Parliaments))) + (λ (h) (eq? (= (house-number h) 2) + (eq? (house-color h) 'blue))) + (λ (h) (implies (eq? (house-color h) 'green) + (not (= (house-number h) 1)))) + (λ (h) (implies (eq? (house-color h) 'ivory) + (not (= (house-number h) 5)))))) + +(define (apply-constraints constraints items) + (for/fold ([it items]) + ([constraint constraints]) + (filter constraint it))) + +(define feasible-houses + (apply-constraints house-constraints all-houses)) + +(define (next-door? h1 h2) + (= (abs (- (house-number h1) (house-number h2))) 1)) + +(define two-house-constraints + (list + (λ (h1 h2) (implies (and (eq? (house-color h1) 'green) + (eq? (house-color h2) 'ivory)) + (= (house-number h1) + (add1 (house-number h2))))) + (λ (h1 h2) (implies (and (eq? (house-smokes h1) 'Chesterfields) + (eq? (house-pet h2) 'fox)) + (next-door? h1 h2))) + (λ (h1 h2) (implies (and (eq? (house-smokes h1) 'Kools) + (eq? (house-pet h2) 'horse)) + (next-door? h1 h2))))) + +(define (check-street constraint street) + (for*/and ([h1 street] + [h2 street]) + (constraint h1 h2))) + +(define street-constraints + (map (λ (c) (λ (s) (check-street c s))) two-house-constraints)) + +(define houses-by-number + (for/list ([n '(1 2 3 4 5)]) + (filter (λ (h) (= n (house-number h))) feasible-houses))) + +(define (disjoint? h1 h2) + (not (or (eq? (house-color h1) (house-color h2)) + (eq? (house-owner h1) (house-owner h2)) + (eq? (house-pet h1) (house-pet h2)) + (eq? (house-drink h1) (house-drink h2)) + (eq? (house-smokes h1) (house-smokes h2))))) + +(define (extends? street h) + (andmap (λ (h1) (disjoint? h h1)) street)) + +(define (build-streets houses street) + (if (null? houses) + (list street) + (for/fold + ([streets '()]) + ([h (car houses)] + #:when (extends? street h)) + (append streets (build-streets (cdr houses) (cons h street)))))) + +(define all-streets + (build-streets houses-by-number '())) + +(define feasible-streets + (apply-constraints street-constraints all-streets)) + +(define (find-owner condition) + (if (= 1 (length feasible-streets)) + (for/first ([h (car feasible-streets)] + #:when (condition h)) + (house-owner h)) + (null))) + +(define (owns-zebra) + (find-owner (λ (h) (eq? (house-pet h) 'zebra)))) + +(define (drinks-water) + (find-owner (λ (h) (eq? (house-drink h) 'water)))) 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/zebra-puzzle-test.rkt b/exercises/practice/zebra-puzzle/zebra-puzzle-test.rkt new file mode 100644 index 00000000..68e12ea8 --- /dev/null +++ b/exercises/practice/zebra-puzzle/zebra-puzzle-test.rkt @@ -0,0 +1,18 @@ +#lang racket/base + +(require "zebra-puzzle.rkt") + +(module+ test + (require rackunit rackunit/text-ui)) + +(module+ test + (define suite + (test-suite + "zebra puzzle tests" + + (test-equal? "who owns the zebra?" + (owns-zebra) 'Japanese) + + (test-equal? "who drinks water?" + (drinks-water) 'Norwegian))) + (run-tests suite)) diff --git a/exercises/practice/zebra-puzzle/zebra-puzzle.rkt b/exercises/practice/zebra-puzzle/zebra-puzzle.rkt new file mode 100644 index 00000000..73b472db --- /dev/null +++ b/exercises/practice/zebra-puzzle/zebra-puzzle.rkt @@ -0,0 +1,9 @@ +#lang racket + +(provide owns-zebra drinks-water) + +(define (owns-zebra) + (error "Not implemented yet")) + +(define (drinks-water) + (error "Not implemented yet"))