-- this is my first attempt at part one of the optional programming assignment -- for the stanford Introduction to Artificial Intelligence class -- it solves a rotating or caesar cypher assuming the most common -- letter in english is 'e' -- PAC 9th January, 2012 import Data.Char (ord, chr, isUpper, isLower, toLower) import Data.List (sortBy, elemIndices) import Data.Ord (comparing) import qualified Data.Map as M import System.Environment (getArgs) -- the code to break defaultEncodedMsg = "Esp qtcde nzyqpcpynp zy esp ezatn zq Lcetqtntlw Tyepwwtrpynp hld spwo le Olcexzfes Nzwwprp ty estd jplc." -- Shifts a character to the right if positive, left if negative. Wraps around. shift :: Int -> Char -> Char -- Modulus handles the wraparound(shift 1 'z' = 'a') shift n c | isUpper c = chr $ ord 'A' + ((ord c + n - ord 'A') `mod` 26) | isLower c = chr $ ord 'a' + ((ord c + n - ord 'a') `mod` 26) | otherwise = c -- rotate input string by n shiftString:: Int -> String -> String shiftString n = map (shift n) --count number of instances of a letter in a string instances:: Char -> String -> Int instances x ys = length $ elemIndices (toLower x) (map toLower ys) --calculate frequency of a letter frequency:: Char -> String -> Float frequency x string = fromIntegral (instances x string) / fromIntegral (length string) -- creates a sorted descending list list of tuples of letter and its frequency frequencyAll:: String -> [(Char, Float)] frequencyAll string = reverse $ sortBy (comparing snd) [ (x , frequency x string) | x <- ['a' .. 'z' ] ] -- assuming 'e' is the most common letter in english decode:: String -> String decode string = shiftString (ord 'e' - ord (fst $ head $ frequencyAll string)) string -- parse arguments, if no supplied use default parseArgs:: [String] -> String parseArgs args = if null args then defaultEncodedMsg else head args -- entry point main = do args <- getArgs let msg = parseArgs args putStrLn "Caesar Cypher Solver (ai-class) - naive approach\n" putStrLn "encoded message:" putStrLn msg putStrLn "\nThe top candidate based on the frequency of the letter 'e' in english is:\n" putStrLn $ decode msg