Haskell Quiz/The Solitaire Cipher/Solution Thiago Arrais
From HaskellWiki
< Haskell Quiz | The Solitaire Cipher(Difference between revisions)
m |
m |
||
| Line 5: | Line 5: | ||
import Char(chr, isAlpha, ord, toUpper) | import Char(chr, isAlpha, ord, toUpper) | ||
| - | |||
import System.Environment(getArgs, getProgName) | import System.Environment(getArgs, getProgName) | ||
| Line 31: | Line 30: | ||
-- Pushes a card (j) down once | -- Pushes a card (j) down once | ||
| - | push' j xs = if | + | push' j xs = if not (null right) |
then left ++ head right : j : tail right | then left ++ head right : j : tail right | ||
else head left : j : tail left | else head left : j : tail left | ||
| Line 65: | Line 64: | ||
where newDeck = nextDeck oldDeck | where newDeck = nextDeck oldDeck | ||
number deck@(n:_) = deck !! valueFor n | number deck@(n:_) = deck !! valueFor n | ||
| - | letter n = if isJoker n then "" else toLetter n | + | letter n = if isJoker n then "" else [toLetter n] |
-- The keystream generated by an unkeyed deck | -- The keystream generated by an unkeyed deck | ||
keystream = concat [c | (c,_) <- tail $ iterate stepStream ([], unkeyeddeck)] | keystream = concat [c | (c,_) <- tail $ iterate stepStream ([], unkeyeddeck)] | ||
| - | |||
| - | |||
-- Combines an input string (xs) and the default keystream by applying the | -- Combines an input string (xs) and the default keystream by applying the | ||
-- given operation (f). This is the function that does the encoding/decoding | -- given operation (f). This is the function that does the encoding/decoding | ||
| - | codeWith f xs = | + | codeWith f xs = unwords.sanitize.letterize $ |
zipWith f (numberize letters) (numberize keyletters) | zipWith f (numberize letters) (numberize keyletters) | ||
where keyletters = take (length letters) keystream | where keyletters = take (length letters) keystream | ||
| Line 87: | Line 84: | ||
-- An action that applies the coding function (f) to a set of words | -- An action that applies the coding function (f) to a set of words | ||
-- and prints the resulting code | -- and prints the resulting code | ||
| - | printCode f = putStrLn . f . | + | printCode f = putStrLn . f . unwords |
main = do args <- getArgs | main = do args <- getArgs | ||
Current revision
module Main where import Char(chr, isAlpha, ord, toUpper) import System.Environment(getArgs, getProgName) toUpperCase = map toUpper toLetter n = chr $ ord 'A' + (n - 1) `mod` 26 toNumber l = ord l - ord 'A' + 1 split5 cs | length cs > 5 = (take 5 cs) : split5 (drop 5 cs) | otherwise = [cs] fill cs = cs ++ replicate (5 - length cs) 'X' -- Filters alpha characters and splits them into groups of five sanitize:: String -> [String] sanitize cs = reverse $ (fill.head) rchunks : tail rchunks where rchunks = reverse.split5.filter isAlpha.toUpperCase $ cs unkeyeddeck :: [Int] unkeyeddeck = [1..54] jokerA = 53 jokerB = 54 isJoker = (`elem` [jokerA, jokerB]) -- Pushes a card (j) down once push' j xs = if not (null right) then left ++ head right : j : tail right else head left : j : tail left where (left,_:right) = break (== j) xs -- Pushes a card (j) down a given number (n) of times push j n = (!! n) . iterate (push' j) pushJokerA = push jokerA 1 pushJokerB = push jokerB 2 -- Performs a triplecut around the first two cards that satisfy a predicate (p) tripleCut p xs = bottom ++ j1 : (middle ++ j2 : top) where (top,j1:b1) = break p xs (middle,j2:bottom) = break p b1 countCut n xs = (reverse.tail $ rbottom) ++ top ++ [head rbottom] where top = take n xs rbottom = reverse.drop n $ xs -- Performs a count cut by the number written on the bottom card deckCut xs = countCut (last xs) xs valueFor 54 = 53 -- B joker's value is 53 valueFor n = n -- Shuffles the deck once nextDeck = deckCut.tripleCut isJoker.pushJokerB.pushJokerA -- Shuffles the deck once and extracts the resulting letter stepStream :: (String, [Int]) -> (String, [Int]) stepStream (_, oldDeck) = (letter $ number newDeck, newDeck) where newDeck = nextDeck oldDeck number deck@(n:_) = deck !! valueFor n letter n = if isJoker n then "" else [toLetter n] -- The keystream generated by an unkeyed deck keystream = concat [c | (c,_) <- tail $ iterate stepStream ([], unkeyeddeck)] -- Combines an input string (xs) and the default keystream by applying the -- given operation (f). This is the function that does the encoding/decoding codeWith f xs = unwords.sanitize.letterize $ zipWith f (numberize letters) (numberize keyletters) where keyletters = take (length letters) keystream numberize = map toNumber letterize = map toLetter letters = concat $ sanitize xs encode, decode :: String -> String encode = codeWith (+) decode = codeWith (-) -- An action that applies the coding function (f) to a set of words -- and prints the resulting code printCode f = putStrLn . f . unwords main = do args <- getArgs case args of ("d":ws@(_:_)) -> printCode decode ws ("e":ws@(_:_)) -> printCode encode ws _ -> getProgName >>= \n -> putStrLn $ "Usage: " ++ n ++ " <d/e> <phrase>"
