diff --git a/config.json b/config.json index 4e2d8f029..bab55addb 100644 --- a/config.json +++ b/config.json @@ -31,6 +31,15 @@ "topics": [ ] }, + { + "uuid": "5bf7612c-427e-484d-bc67-8553fac4f64d", + "slug": "rail-fence-cipher", + "core": false, + "unlocked_by": null, + "difficulty": 1, + "topics": [ + ] + }, { "uuid": "6cbb7841-9d96-4528-88e3-6e98a450fd7c", "slug": "space-age", diff --git a/exercises/rail-fence-cipher/README.md b/exercises/rail-fence-cipher/README.md new file mode 100644 index 000000000..f3c1bd22f --- /dev/null +++ b/exercises/rail-fence-cipher/README.md @@ -0,0 +1,119 @@ +# Rail Fence Cipher + +Implement encoding and decoding for the rail fence cipher. + +The Rail Fence cipher is a form of transposition cipher that gets its name from +the way in which it's encoded. It was already used by the ancient Greeks. + +In the Rail Fence cipher, the message is written downwards on successive "rails" +of an imaginary fence, then moving up when we get to the bottom (like a zig-zag). +Finally the message is then read off in rows. + +For example, using three "rails" and the message "WE ARE DISCOVERED FLEE AT ONCE", +the cipherer writes out: + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . A . . . I . . . V . . . D . . . E . . . N . . +``` + +Then reads off: + +```text +WECRLTEERDSOEEFEAOCAIVDEN +``` + +To decrypt a message you take the zig-zag shape and fill the ciphertext along the rows. + +```text +? . . . ? . . . ? . . . ? . . . ? . . . ? . . . ? +. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +The first row has seven spots that can be filled with "WECRLTE". + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . ? . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +Now the 2nd row takes "ERDSOEEFEAOC". + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . ? . . . ? . . . ? . . . ? . . . ? . . . ? . . +``` + +Leaving "AIVDEN" for the last row. + +```text +W . . . E . . . C . . . R . . . L . . . T . . . E +. E . R . D . S . O . E . E . F . E . A . O . C . +. . A . . . I . . . V . . . D . . . E . . . N . . +``` + +If you now read along the zig-zag shape you can read the original message. + + +## Getting Started + +For installation and learning resources, refer to the +[exercism help page](http://exercism.io/languages/haskell). + +## Running the tests + +To run the test suite, execute the following command: + +```bash +stack test +``` + +#### If you get an error message like this... + +``` +No .cabal file found in directory +``` + +You are probably running an old stack version and need +to upgrade it. + +#### Otherwise, if you get an error message like this... + +``` +No compiler found, expected minor version match with... +Try running "stack setup" to install the correct GHC... +``` + +Just do as it says and it will download and install +the correct compiler version: + +```bash +stack setup +``` + +## Running *GHCi* + +If you want to play with your solution in GHCi, just run the command: + +```bash +stack ghci +``` + +## Feedback, Issues, Pull Requests + +The [exercism/haskell](https://github.com/exercism/haskell) repository on +GitHub is the home for all of the Haskell exercises. + +If you have feedback about an exercise, or want to help implementing a new +one, head over there and create an issue. We'll do our best to help you! + +## Source + +Wikipedia [https://en.wikipedia.org/wiki/Transposition_cipher#Rail_Fence_cipher](https://en.wikipedia.org/wiki/Transposition_cipher#Rail_Fence_cipher) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/exercises/rail-fence-cipher/examples/success-standard/package.yaml b/exercises/rail-fence-cipher/examples/success-standard/package.yaml new file mode 100644 index 000000000..8d3e41ec0 --- /dev/null +++ b/exercises/rail-fence-cipher/examples/success-standard/package.yaml @@ -0,0 +1,16 @@ +name: rail-fence-cipher + +dependencies: + - base + +library: + exposed-modules: RailFenceCipher + source-dirs: src + +tests: + test: + main: Tests.hs + source-dirs: test + dependencies: + - rail-fence-cipher + - hspec diff --git a/exercises/rail-fence-cipher/examples/success-standard/src/RailFenceCipher.hs b/exercises/rail-fence-cipher/examples/success-standard/src/RailFenceCipher.hs new file mode 100644 index 000000000..6ba9391c8 --- /dev/null +++ b/exercises/rail-fence-cipher/examples/success-standard/src/RailFenceCipher.hs @@ -0,0 +1,12 @@ +module RailFenceCipher (encode, decode) where + +import Data.List (sortBy) +import Data.Function (on) + +encode :: Int -> [a] -> [a] +encode n xs = concat [[a | (a, b) <- zip xs c, b == i] | i <- [1..n]] + where c = cycle $ [1..n] ++ [n-1,n-2..2] + +decode :: Int -> [a] -> [a] +decode n xs = map snd $ sortBy (compare `on` fst) zippedL + where zippedL = zip (encode n [0..length xs - 1]) xs diff --git a/exercises/rail-fence-cipher/package.yaml b/exercises/rail-fence-cipher/package.yaml new file mode 100644 index 000000000..b406fbe19 --- /dev/null +++ b/exercises/rail-fence-cipher/package.yaml @@ -0,0 +1,20 @@ +name: rail-fence-cipher +version: 1.1.0.1 + +dependencies: + - base + +library: + exposed-modules: RailFenceCipher + source-dirs: src + dependencies: + # - foo # List here the packages you + # - bar # want to use in your solution. + +tests: + test: + main: Tests.hs + source-dirs: test + dependencies: + - rail-fence-cipher + - hspec diff --git a/exercises/rail-fence-cipher/src/RailFenceCipher.hs b/exercises/rail-fence-cipher/src/RailFenceCipher.hs new file mode 100644 index 000000000..09efeb9c9 --- /dev/null +++ b/exercises/rail-fence-cipher/src/RailFenceCipher.hs @@ -0,0 +1,7 @@ +module RailFenceCipher (encode, decode) where + +encode :: Int -> String -> String +encode = error "You need to implement this function!" + +decode :: Int -> String -> String +decode = error "You need to implement this function!" diff --git a/exercises/rail-fence-cipher/stack.yaml b/exercises/rail-fence-cipher/stack.yaml new file mode 100644 index 000000000..826c44fa7 --- /dev/null +++ b/exercises/rail-fence-cipher/stack.yaml @@ -0,0 +1 @@ +resolver: lts-9.11 diff --git a/exercises/rail-fence-cipher/test/Tests.hs b/exercises/rail-fence-cipher/test/Tests.hs new file mode 100644 index 000000000..37e17da00 --- /dev/null +++ b/exercises/rail-fence-cipher/test/Tests.hs @@ -0,0 +1,60 @@ +{-# LANGUAGE RecordWildCards #-} + +import Data.Foldable (for_) +import Test.Hspec (Spec, describe, it, shouldBe) +import Test.Hspec.Runner (configFastFail, defaultConfig, hspecWith) + +import RailFenceCipher (encode, decode) + +main :: IO () +main = hspecWith defaultConfig {configFastFail = True} specs + +specs :: Spec +specs = do + describe "encode" $ for_ encodeCases testE + describe "decode" $ for_ decodeCases testD + where + testE Case{..} = it description $ encode key text `shouldBe` expected + testD Case{..} = it description $ decode key text `shouldBe` expected + +data Case = Case { description :: String + , key :: Int + , text :: String + , expected :: String + } + +encodeCases :: [Case] +encodeCases = [ Case { description = "encode with two rails" + , key = 2 + , text = "XOXOXOXOXOXOXOXOXO" + , expected = "XXXXXXXXXOOOOOOOOO" + } + , Case { description = "encode with three rails" + , key = 3 + , text = "WEAREDISCOVEREDFLEEATONCE" + , expected = "WECRLTEERDSOEEFEAOCAIVDEN" + } + , Case { description = "encode with ending in the middle" + , key = 4 + , text = "EXERCISES" + , expected = "ESXIEECSR" + } + ] + +decodeCases :: [Case] +decodeCases = [ Case { description = "decode with three rails" + , key = 3 + , text = "TEITELHDVLSNHDTISEIIEA" + , expected = "THEDEVILISINTHEDETAILS" + } + , Case { description = "decode with five rails" + , key = 5 + , text = "EIEXMSMESAORIWSCE" + , expected = "EXERCISMISAWESOME" + } + , Case { description = "decode with six rails" + , key = 6 + , text = "133714114238148966225439541018335470986172518171757571896261" + , expected = "112358132134558914423337761098715972584418167651094617711286" + } + ]