trackler 1.0.4.0 → 1.0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/common/CONTRIBUTING.md +17 -2
  3. data/common/exercises/anagram/canonical-data.json +0 -14
  4. data/common/exercises/pig-latin/canonical-data.json +6 -1
  5. data/common/exercises/raindrops/description.md +4 -4
  6. data/common/exercises/raindrops/metadata.yml +1 -1
  7. data/common/exercises/roman-numerals/metadata.yml +1 -1
  8. data/lib/trackler/version.rb +1 -1
  9. data/tracks/c/config.json +12 -1
  10. data/tracks/c/exercises/roman-numerals/makefile +16 -0
  11. data/tracks/c/exercises/roman-numerals/src/example.c +43 -0
  12. data/tracks/c/exercises/roman-numerals/src/example.h +6 -0
  13. data/tracks/c/exercises/roman-numerals/test/test_roman_numerals.c +126 -0
  14. data/tracks/c/exercises/roman-numerals/test/vendor/unity.c +1300 -0
  15. data/tracks/c/exercises/roman-numerals/test/vendor/unity.h +274 -0
  16. data/tracks/c/exercises/roman-numerals/test/vendor/unity_internals.h +701 -0
  17. data/tracks/clojure/exercises/flatten-array/project.clj +4 -0
  18. data/tracks/csharp/exercises/protein-translation/ProteinTranslationTest.cs +1 -1
  19. data/tracks/elixir/exercises/bowling/bowling_test.exs +157 -62
  20. data/tracks/elixir/exercises/bowling/example.exs +33 -4
  21. data/tracks/erlang/config.json +176 -36
  22. data/tracks/go/.travis.yml +7 -1
  23. data/tracks/go/bin/test-without-stubs +29 -0
  24. data/tracks/go/exercises/robot-simulator/defs.go +1 -1
  25. data/tracks/go/exercises/variable-length-quantity/variable_length_quantity_test.go +31 -25
  26. data/tracks/haskell/.travis.yml +9 -5
  27. data/tracks/haskell/config.json +127 -121
  28. data/tracks/haskell/exercises/leap/src/LeapYear.hs +1 -1
  29. data/tracks/haskell/exercises/space-age/HINTS.md +14 -2
  30. data/tracks/haskell/exercises/space-age/{src/Example.hs → examples/success-double/SpaceAge.hs} +0 -0
  31. data/tracks/haskell/exercises/space-age/examples/success-double/package.yaml +19 -0
  32. data/tracks/haskell/exercises/space-age/examples/success-rational/SpaceAge.hs +24 -0
  33. data/tracks/haskell/exercises/space-age/examples/success-rational/package.yaml +19 -0
  34. data/tracks/haskell/exercises/space-age/src/SpaceAge.hs +4 -1
  35. data/tracks/haskell/exercises/space-age/test/Tests.hs +12 -5
  36. data/tracks/haskell/exercises/word-count/HINTS.md +21 -0
  37. data/tracks/haskell/exercises/word-count/examples/success-newtype/WordCount.hs +31 -0
  38. data/tracks/haskell/exercises/word-count/examples/success-newtype/package.yaml +21 -0
  39. data/tracks/haskell/exercises/word-count/examples/success-simple/WordCount.hs +13 -0
  40. data/tracks/haskell/exercises/word-count/examples/success-simple/package.yaml +20 -0
  41. data/tracks/haskell/exercises/word-count/package.yaml +0 -2
  42. data/tracks/haskell/exercises/word-count/src/WordCount.hs +2 -1
  43. data/tracks/haskell/exercises/word-count/test/Tests.hs +15 -6
  44. data/tracks/haskell/exercises/zebra-puzzle/package.yaml +19 -0
  45. data/tracks/haskell/exercises/zebra-puzzle/src/Example.hs +113 -0
  46. data/tracks/haskell/exercises/zebra-puzzle/src/ZebraPuzzle.hs +12 -0
  47. data/tracks/haskell/exercises/zebra-puzzle/stack.yaml +1 -0
  48. data/tracks/haskell/exercises/zebra-puzzle/test/Tests.hs +13 -0
  49. data/tracks/javascript/exercises/beer-song/beer-song.spec.js +5 -0
  50. data/tracks/lisp/config.json +147 -0
  51. data/tracks/ocaml/config.json +12 -0
  52. data/tracks/ocaml/exercises/atbash-cipher/.merlin +5 -0
  53. data/tracks/ocaml/exercises/atbash-cipher/HINTS.md +17 -0
  54. data/tracks/ocaml/exercises/atbash-cipher/Makefile +11 -0
  55. data/tracks/ocaml/exercises/atbash-cipher/atbash_cipher.mli +5 -0
  56. data/tracks/ocaml/exercises/atbash-cipher/example.ml +47 -0
  57. data/tracks/ocaml/exercises/atbash-cipher/test.ml +37 -0
  58. data/tracks/ocaml/exercises/meetup/.merlin +5 -0
  59. data/tracks/ocaml/exercises/meetup/Makefile +11 -0
  60. data/tracks/ocaml/exercises/meetup/example.ml +63 -0
  61. data/tracks/ocaml/exercises/meetup/meetup.mli +7 -0
  62. data/tracks/ocaml/exercises/meetup/test.ml +786 -0
  63. data/tracks/php/exercises/bowling/bowling_test.php +233 -10
  64. data/tracks/php/exercises/bowling/example.php +49 -6
  65. data/tracks/python/.travis.yml +5 -0
  66. data/tracks/python/config.json +33 -1
  67. data/tracks/python/exercises/flatten-array/example.py +18 -0
  68. data/tracks/python/exercises/flatten-array/flatten_array_test.py +40 -0
  69. data/tracks/python/exercises/robot-name/robot_name_test.py +8 -2
  70. data/tracks/r/config.json +32 -8
  71. data/tracks/ruby/bin/enable-executable +9 -5
  72. data/tracks/ruby/bin/executable-tests-check +7 -3
  73. data/tracks/ruby/bin/local-status-check +4 -4
  74. data/tracks/ruby/config.json +8 -1
  75. data/tracks/ruby/exercises/acronym/acronym_test.rb +0 -1
  76. data/tracks/ruby/exercises/anagram/.version +1 -0
  77. data/tracks/ruby/exercises/anagram/anagram_test.rb +101 -31
  78. data/tracks/ruby/exercises/anagram/example.rb +4 -0
  79. data/tracks/ruby/exercises/anagram/example.tt +19 -0
  80. data/tracks/ruby/exercises/gigasecond/.version +1 -1
  81. data/tracks/ruby/exercises/gigasecond/example.rb +1 -1
  82. data/tracks/ruby/exercises/gigasecond/gigasecond_test.rb +2 -2
  83. data/tracks/ruby/exercises/isogram/.version +1 -0
  84. data/tracks/ruby/exercises/isogram/example.rb +10 -0
  85. data/tracks/ruby/exercises/isogram/example.tt +20 -0
  86. data/tracks/ruby/exercises/isogram/isogram_test.rb +90 -0
  87. data/tracks/ruby/exercises/poker/example.rb +97 -39
  88. data/tracks/ruby/exercises/poker/poker_test.rb +67 -36
  89. data/tracks/ruby/exercises/sieve/.version +1 -0
  90. data/tracks/ruby/exercises/sieve/example.rb +4 -0
  91. data/tracks/ruby/exercises/sieve/example.tt +21 -0
  92. data/tracks/ruby/exercises/sieve/sieve_test.rb +36 -13
  93. data/tracks/ruby/exercises/word-count/example.tt +4 -3
  94. data/tracks/ruby/exercises/word-count/word_count_test.rb +38 -48
  95. data/tracks/ruby/lib/anagram_cases.rb +42 -0
  96. data/tracks/ruby/lib/isogram_cases.rb +24 -0
  97. data/tracks/ruby/lib/sieve_cases.rb +33 -0
  98. data/tracks/ruby/lib/word_count_cases.rb +19 -0
  99. data/tracks/scala/config.json +21 -0
  100. data/tracks/scala/docs/RESOURCES.md +1 -0
  101. data/tracks/scala/exercises/bracket-push/build.sbt +4 -0
  102. data/tracks/scala/exercises/bracket-push/example.scala +23 -0
  103. data/tracks/scala/exercises/bracket-push/src/main/scala/.keep +0 -0
  104. data/tracks/scala/exercises/bracket-push/src/test/scala/BracketsTest.scala +68 -0
  105. data/tracks/scala/exercises/change/src/test/scala/ChangeTest.scala +7 -0
  106. data/tracks/scala/exercises/sgf-parsing/build.sbt +7 -0
  107. data/tracks/scala/exercises/sgf-parsing/example.scala +66 -0
  108. data/tracks/scala/exercises/sgf-parsing/src/main/scala/.keep +0 -0
  109. data/tracks/scala/exercises/sgf-parsing/src/main/scala/Sgf.scala +17 -0
  110. data/tracks/scala/exercises/sgf-parsing/src/test/scala/SgfTest.scala +66 -0
  111. data/tracks/scala/exercises/zebra-puzzle/build.sbt +3 -0
  112. data/tracks/scala/exercises/zebra-puzzle/example.scala +152 -0
  113. data/tracks/scala/exercises/zebra-puzzle/src/main/scala/.keep +0 -0
  114. data/tracks/scala/exercises/zebra-puzzle/src/main/scala/ZebraPuzzle.scala +14 -0
  115. data/tracks/scala/exercises/zebra-puzzle/src/test/scala/ZebraPuzzleTest.scala +11 -0
  116. data/tracks/scala/testgen/src/main/scala/BracketPushTestGenerator.scala +44 -0
  117. metadata +63 -3
  118. data/tracks/haskell/exercises/word-count/src/Example.hs +0 -8
@@ -1,4 +1,4 @@
1
1
  module LeapYear (isLeapYear) where
2
2
 
3
3
  isLeapYear :: Integer -> Bool
4
- isLeapYear = undefined
4
+ isLeapYear year = undefined
@@ -1,4 +1,16 @@
1
1
  ## Hints
2
2
 
3
- To complete this exercise, you need to create the data type `Planet`
4
- and implement the `ageOn` function.
3
+ In this exercise, you need to complete the definition of the
4
+ [algebric data data](http://learnyouahaskell.com/making-our-own-types-and-typeclasses)
5
+ named `Planet`, and implement the `ageOn` function, that calculates how many
6
+ years old someone would be on a `Planet`, given an age in seconds.
7
+
8
+ Your can use the provided signature if you are unsure about the types, but
9
+ don't let it restrict your creativity:
10
+
11
+ ```haskell
12
+ ageOn :: Planet -> Float -> Float
13
+ ```
14
+
15
+ Keep in mind that the test suite will not compile until you correctly
16
+ implement the data type `Planet`.
@@ -0,0 +1,19 @@
1
+ name: space-age
2
+
3
+ dependencies:
4
+ - base
5
+
6
+ library:
7
+ exposed-modules: SpaceAge
8
+ source-dirs: src
9
+ dependencies:
10
+ # - foo # List here the packages you
11
+ # - bar # want to use in your solution.
12
+
13
+ tests:
14
+ test:
15
+ main: Tests.hs
16
+ source-dirs: test
17
+ dependencies:
18
+ - space-age
19
+ - hspec
@@ -0,0 +1,24 @@
1
+ module SpaceAge (Planet(..), ageOn) where
2
+
3
+ data Planet = Mercury
4
+ | Venus
5
+ | Earth
6
+ | Mars
7
+ | Jupiter
8
+ | Saturn
9
+ | Uranus
10
+ | Neptune
11
+
12
+ ageOn :: Real a => Planet -> a -> Rational
13
+ ageOn planet seconds = toRational seconds / (earthPeriod * planetMultiplier)
14
+ where
15
+ planetMultiplier = case planet of
16
+ Mercury -> 0.2408467
17
+ Venus -> 0.61519726
18
+ Earth -> 1
19
+ Mars -> 1.8808158
20
+ Jupiter -> 11.862615
21
+ Saturn -> 29.447498
22
+ Uranus -> 84.016846
23
+ Neptune -> 164.79132
24
+ earthPeriod = 31557600
@@ -0,0 +1,19 @@
1
+ name: space-age
2
+
3
+ dependencies:
4
+ - base
5
+
6
+ library:
7
+ exposed-modules: SpaceAge
8
+ source-dirs: src
9
+ dependencies:
10
+ # - foo # List here the packages you
11
+ # - bar # want to use in your solution.
12
+
13
+ tests:
14
+ test:
15
+ main: Tests.hs
16
+ source-dirs: test
17
+ dependencies:
18
+ - space-age
19
+ - hspec
@@ -1,3 +1,6 @@
1
1
  module SpaceAge (Planet(..), ageOn) where
2
2
 
3
- ageOn = undefined
3
+ data Planet
4
+
5
+ ageOn :: Planet -> Float -> Float
6
+ ageOn planet seconds = undefined
@@ -2,6 +2,7 @@
2
2
  {-# LANGUAGE RecordWildCards #-}
3
3
 
4
4
  import Data.Foldable (for_)
5
+ import Data.Function (on)
5
6
  import Test.Hspec (Spec, describe, it, shouldBe)
6
7
  import Test.Hspec.Runner (configFastFail, defaultConfig, hspecWith)
7
8
 
@@ -14,12 +15,18 @@ specs :: Spec
14
15
  specs = describe "space-age" $
15
16
  describe "ageOn" $ for_ cases test
16
17
  where
17
-
18
- test Case{..} = it description assertion
18
+ -- Here we used `fromIntegral`, `fromRational` and `toRational` to
19
+ -- generalize the test suite, allowing any function that takes a
20
+ -- `Planet` and a number, returning an instance of `Real`.
21
+ test Case{..} = it description $ expression `shouldBeAround` expected
19
22
  where
20
- expression = ageOn planet $ fromIntegral seconds
21
- assertion = roundAge expression `shouldBe` roundAge expected
22
- roundAge = (/ 100) . fromIntegral . round . (* 100)
23
+ expression = fromRational
24
+ . toRational
25
+ . ageOn planet
26
+ . fromIntegral
27
+ $ seconds
28
+ shouldBeAround = shouldBe `on` roundTo 2
29
+ roundTo n = (/ 10 ^ n) . fromIntegral . round . (* 10 ^ n)
23
30
 
24
31
  -- Test cases adapted from `exercism/x-common/space-age.json` on 2016-07-27.
25
32
 
@@ -0,0 +1,21 @@
1
+ ## Hints
2
+
3
+ To complete this exercise you need to implement the function `wordCount`,
4
+ that takes a *text* and returns how many times each *word* appears.
5
+
6
+ If it is your first time solving this exercise, it is recommended that you
7
+ stick to the provided signature:
8
+
9
+ ```haskell
10
+ wordCount :: String -> [(String, Int)]
11
+ ```
12
+
13
+ Later, it may be a good idea to revisit this problem and play with other data
14
+ types and libraries:
15
+
16
+ - `Text`, from package *text*.
17
+ - `Map`, from package *containers*.
18
+ - `MultiSet`, from package *multiset*
19
+
20
+ The test suite was intentionally designed to accept almost any type signature
21
+ that makes sense, so you are encouraged to find the one you think is the best.
@@ -0,0 +1,31 @@
1
+ {-# LANGUAGE TypeFamilies #-}
2
+
3
+ module WordCount (wordCount) where
4
+
5
+ import Prelude hiding (null)
6
+ import Data.Char (isAlphaNum)
7
+ import Data.Text (Text, null, split, toLower)
8
+ import Data.MultiSet (MultiSet, Occur, fromList, fromOccurList, toOccurList)
9
+
10
+ import qualified GHC.Exts (IsList(..))
11
+
12
+ wordCount :: Text -> Bag Text
13
+ wordCount = Bag
14
+ . fromList
15
+ . map toLower
16
+ . wordsBy (not . isAlphaNum)
17
+
18
+ -- The `text` package misses this function that
19
+ -- exists in package `split`, but works on lists.
20
+ wordsBy :: (Char -> Bool) -> Text -> [Text]
21
+ wordsBy p = filter (not . null) . split p
22
+
23
+ -- MultiSet is not an instance of `IsList`, so we create
24
+ -- a newtype to wrap it, avoiding an orphan instance.
25
+ newtype Bag a = Bag { toMultiSet :: MultiSet a }
26
+
27
+ instance (Ord a) => GHC.Exts.IsList (Bag a)
28
+ where
29
+ type Item (Bag a) = (a, Occur)
30
+ fromList = Bag . fromOccurList
31
+ toList = toOccurList . toMultiSet
@@ -0,0 +1,21 @@
1
+ name: word-count
2
+
3
+ dependencies:
4
+ - base
5
+
6
+ library:
7
+ exposed-modules: WordCount
8
+ source-dirs: src
9
+ dependencies:
10
+ # - foo # List here the packages you
11
+ # - bar # want to use in your solution.
12
+ - multiset
13
+ - text
14
+
15
+ tests:
16
+ test:
17
+ main: Tests.hs
18
+ source-dirs: test
19
+ dependencies:
20
+ - word-count
21
+ - hspec
@@ -0,0 +1,13 @@
1
+ module WordCount (wordCount) where
2
+
3
+ import Control.Arrow ((&&&))
4
+ import Data.Char (toLower, isAlphaNum)
5
+ import Data.List (group, sort)
6
+ import Data.List.Split (wordsBy)
7
+
8
+ wordCount :: String -> [(String, Int)]
9
+ wordCount = map (head &&& length)
10
+ . group
11
+ . sort
12
+ . map (map toLower)
13
+ . wordsBy (not . isAlphaNum)
@@ -0,0 +1,20 @@
1
+ name: word-count
2
+
3
+ dependencies:
4
+ - base
5
+
6
+ library:
7
+ exposed-modules: WordCount
8
+ source-dirs: src
9
+ dependencies:
10
+ # - foo # List here the packages you
11
+ # - bar # want to use in your solution.
12
+ - split
13
+
14
+ tests:
15
+ test:
16
+ main: Tests.hs
17
+ source-dirs: test
18
+ dependencies:
19
+ - word-count
20
+ - hspec
@@ -2,7 +2,6 @@ name: word-count
2
2
 
3
3
  dependencies:
4
4
  - base
5
- - containers
6
5
 
7
6
  library:
8
7
  exposed-modules: WordCount
@@ -10,7 +9,6 @@ library:
10
9
  dependencies:
11
10
  # - foo # List here the packages you
12
11
  # - bar # want to use in your solution.
13
- - split
14
12
 
15
13
  tests:
16
14
  test:
@@ -1,3 +1,4 @@
1
1
  module WordCount (wordCount) where
2
2
 
3
- wordCount = undefined
3
+ wordCount :: String -> [(String, Int)]
4
+ wordCount xs = undefined
@@ -1,9 +1,11 @@
1
1
  {-# OPTIONS_GHC -fno-warn-type-defaults #-}
2
2
  {-# LANGUAGE RecordWildCards #-}
3
3
 
4
+ import Data.Bifunctor (bimap)
5
+ import Data.Char (toLower)
4
6
  import Data.Foldable (for_)
5
- import Data.Map (fromList)
6
- import Test.Hspec (Spec, describe, it, shouldBe)
7
+ import GHC.Exts (fromList, toList)
8
+ import Test.Hspec (Spec, describe, it, shouldMatchList)
7
9
  import Test.Hspec.Runner (configFastFail, defaultConfig, hspecWith)
8
10
 
9
11
  import WordCount (wordCount)
@@ -15,11 +17,18 @@ specs :: Spec
15
17
  specs = describe "word-count" $
16
18
  describe "wordCount" $ for_ cases test
17
19
  where
18
-
19
- test Case{..} = it description $ returnedMap `shouldBe` expectedMap
20
+ -- Here we used `fromIntegral`, `fromList` and `toList` to generalize
21
+ -- the tests, accepting any function that receives a string-like argumment
22
+ -- and returns a type that can be converted to [(String, Integer)].
23
+ -- Also, the words are lower-cased before comparison and the output's
24
+ -- order is ignored.
25
+ test Case{..} = it description $ expression `shouldMatchList` expected
20
26
  where
21
- returnedMap = wordCount input
22
- expectedMap = fromIntegral <$> fromList expected
27
+ expression = map (bimap (map toLower . toList) fromIntegral)
28
+ . toList
29
+ . wordCount
30
+ . fromList
31
+ $ input
23
32
 
24
33
  -- Test cases adapted from `exercism/x-common/word-count.json` on 2016-07-26.
25
34
 
@@ -0,0 +1,19 @@
1
+ name: zebra-puzzle
2
+
3
+ dependencies:
4
+ - base
5
+
6
+ library:
7
+ exposed-modules: ZebraPuzzle
8
+ source-dirs: src
9
+ dependencies:
10
+ # - foo # List here the packages you
11
+ # - bar # want to use in your solution.
12
+
13
+ tests:
14
+ test:
15
+ main: Tests.hs
16
+ source-dirs: test
17
+ dependencies:
18
+ - zebra-puzzle
19
+ - hspec
@@ -0,0 +1,113 @@
1
+ module ZebraPuzzle (Resident(..), Solution(..), solve) where
2
+
3
+ import Control.Monad (guard)
4
+ import Data.List (nub, find)
5
+ import Data.Maybe (fromJust)
6
+
7
+ data Color = Red | Green | Yellow | Blue | Ivory
8
+ deriving (Eq, Show, Enum)
9
+ data Resident = Englishman | Spaniard | Ukrainian | Norwegian | Japanese
10
+ deriving (Eq, Show, Enum)
11
+ data Pet = Dog | Snails | Fox | Horse | Zebra
12
+ deriving (Eq, Show, Enum)
13
+ data Beverage = Coffee | Tea | Milk | OrangeJuice | Water
14
+ deriving (Eq, Show, Enum)
15
+ data Cigarette = OldGold | Kools | Chesterfields | LuckyStrike | Parliaments
16
+ deriving (Eq, Show, Enum)
17
+ data Position = One | Two | Three | Four | Five
18
+ deriving (Eq, Show, Enum)
19
+
20
+ data House = House { position :: Position
21
+ , color :: Color
22
+ , resident :: Resident
23
+ , beverage :: Beverage
24
+ , cigarette :: Cigarette
25
+ , pet :: Pet
26
+ } deriving (Show)
27
+
28
+ data Solution = Solution { waterDrinker :: Resident
29
+ , zebraOwner :: Resident
30
+ } deriving (Eq, Show)
31
+
32
+ solve :: Solution
33
+ solve = Solution waterDrinker' zebraOwner'
34
+ where
35
+ waterDrinker' = residentWith beverage Water
36
+ zebraOwner' = residentWith pet Zebra
37
+ residentWith :: (Eq a) => (House -> a) -> a -> Resident
38
+ residentWith what value = resident $ houseWith what value fiveHouses
39
+
40
+ fiveHouses :: [House]
41
+ fiveHouses = head $ do
42
+ one <- housesAtPosition One
43
+ two <- housesAtPosition Two
44
+ guard $ uniqueHouses [one, two] -- prune search tree as early as possible
45
+ three <- housesAtPosition Three
46
+ guard $ uniqueHouses [one, two, three]
47
+ four <- housesAtPosition Four
48
+ guard $ uniqueHouses [one, two, three, four]
49
+ five <- housesAtPosition Five
50
+ let candidates = [one, two, three, four, five]
51
+ guard $ uniqueHouses candidates
52
+ guard $ validPositions candidates
53
+ return candidates
54
+ where
55
+ housesAtPosition :: Position -> [House]
56
+ housesAtPosition pos = filter ((== pos) . position) validHouses
57
+
58
+ validHouses :: [House]
59
+ validHouses = do
60
+ position' <- [One .. Five]
61
+ color' <- [Red .. Ivory]
62
+ resident' <- [Englishman .. Japanese]
63
+ beverage' <- [Coffee .. Water]
64
+ cigarette' <- [OldGold .. Parliaments]
65
+ pet' <- [Dog .. Zebra]
66
+ let house = House position' color' resident' beverage' cigarette' pet'
67
+ guard $ validHouse house
68
+ return house
69
+
70
+ validHouse :: House -> Bool
71
+ validHouse (House position' color' resident' beverage' cigarette' pet') =
72
+ all (uncurry iff) [
73
+ (color' == Red, resident' == Englishman),
74
+ (resident' == Spaniard, pet' == Dog),
75
+ (color' == Green, beverage' == Coffee),
76
+ (resident' == Ukrainian, beverage' == Tea),
77
+ (cigarette' == OldGold, pet' == Snails),
78
+ (color' == Yellow, cigarette' == Kools),
79
+ (position' == Three, beverage' == Milk),
80
+ (position' == One, resident' == Norwegian),
81
+ (beverage' == OrangeJuice, cigarette' == LuckyStrike),
82
+ (resident' == Japanese, cigarette' == Parliaments)
83
+ ]
84
+
85
+ iff :: Bool -> Bool -> Bool
86
+ iff True True = True
87
+ iff False False = True
88
+ iff _ _ = False
89
+
90
+ uniqueHouses :: [House] -> Bool
91
+ uniqueHouses houses =
92
+ unique color && unique resident && unique beverage &&
93
+ unique cigarette && unique pet
94
+ where
95
+ unique :: (Eq a) => (House -> a) -> Bool
96
+ unique what = (== length houses) . length . nub $ map what houses
97
+
98
+ houseWith :: (Eq a) => (House -> a) -> a -> [House] -> House
99
+ houseWith what value = fromJust . find ((== value) . what)
100
+
101
+ validPositions :: [House] -> Bool
102
+ validPositions houses =
103
+ houseWith' color Green `toTheRight` houseWith' color Ivory &&
104
+ houseWith' cigarette Chesterfields `nextTo` houseWith' pet Fox &&
105
+ houseWith' cigarette Kools `nextTo` houseWith' pet Horse &&
106
+ houseWith' resident Norwegian `nextTo` houseWith' color Blue
107
+ where
108
+ houseWith' what value = houseWith what value houses
109
+ toTheRight :: House -> House -> Bool
110
+ toTheRight h1 h2 = fromEnum (position h1) == fromEnum (position h2) + 1
111
+ nextTo :: House -> House -> Bool
112
+ nextTo h1 h2 = abs (fromEnum (position h1) - fromEnum (position h2)) == 1
113
+
@@ -0,0 +1,12 @@
1
+ module ZebraPuzzle (Resident(..), Solution(..), solve) where
2
+
3
+ data Resident = Englishman | Spaniard | Ukrainian | Norwegian | Japanese
4
+ deriving (Eq, Show)
5
+
6
+ data Solution = Solution { waterDrinker :: Resident
7
+ , zebraOwner :: Resident
8
+ } deriving (Eq, Show)
9
+
10
+ solve :: Solution
11
+ solve = undefined
12
+