-- 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 )
-- rotate input string by n
shiftString n
= map ( shift n
)
--count number of instances of a letter in a string
instances x ys
= length $ elemIndices
( toLower x
) ( map toLower ys
)
--calculate frequency of a letter
-- creates a sorted descending list list of tuples of letter and its frequency
frequencyAll string
= reverse $ sortBy
( comparing
snd ) [ ( x
, frequency x string
) | x
<- [ 'a' .. 'z' ] ]
-- assuming 'e' is the most common letter in english
decode string
= shiftString
( ord
'e' - ord
( fst $ head $ frequencyAll string
) ) string
-- parse arguments, if no supplied use default
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 "\n The top candidate based on the frequency of the letter 'e' in english is:\n "
LS0gdGhpcyBpcyBteSBmaXJzdCBhdHRlbXB0IGF0IHBhcnQgb25lIG9mIHRoZSBvcHRpb25hbCBwcm9ncmFtbWluZyBhc3NpZ25tZW50IAotLSBmb3IgdGhlIHN0YW5mb3JkIEludHJvZHVjdGlvbiB0byBBcnRpZmljaWFsIEludGVsbGlnZW5jZSBjbGFzcwotLSBpdCBzb2x2ZXMgYSByb3RhdGluZyBvciBjYWVzYXIgY3lwaGVyIGFzc3VtaW5nIHRoZSBtb3N0IGNvbW1vbiAKLS0gbGV0dGVyIGluIGVuZ2xpc2ggaXMgJ2UnCi0tIFBBQyA5dGggSmFudWFyeSwgMjAxMgoKaW1wb3J0IERhdGEuQ2hhciAob3JkLCBjaHIsIGlzVXBwZXIsIGlzTG93ZXIsIHRvTG93ZXIpCmltcG9ydCBEYXRhLkxpc3QgKHNvcnRCeSwgZWxlbUluZGljZXMpCmltcG9ydCBEYXRhLk9yZCAoY29tcGFyaW5nKQppbXBvcnQgcXVhbGlmaWVkIERhdGEuTWFwIGFzIE0KaW1wb3J0IFN5c3RlbS5FbnZpcm9ubWVudCAoZ2V0QXJncykKCi0tIHRoZSBjb2RlIHRvIGJyZWFrCmRlZmF1bHRFbmNvZGVkTXNnID0gIkVzcCBxdGNkZSBuenlxcGNweW5wIHp5IGVzcCBlemF0biB6cSBMY2V0cXRudGx3IFR5ZXB3d3RycHlucCBobGQgc3B3byBsZSBPbGNleHpmZXMgTnp3d3BycCB0eSBlc3RkIGpwbGMuIgoKLS0gU2hpZnRzIGEgY2hhcmFjdGVyIHRvIHRoZSByaWdodCBpZiBwb3NpdGl2ZSwgbGVmdCBpZiBuZWdhdGl2ZS4gV3JhcHMgYXJvdW5kLgpzaGlmdCA6OiBJbnQgLT4gQ2hhciAtPiBDaGFyIC0tIE1vZHVsdXMgaGFuZGxlcyB0aGUgd3JhcGFyb3VuZChzaGlmdCAxICd6JyA9ICdhJykKc2hpZnQgbiBjIHwgaXNVcHBlciBjID0gY2hyICQgb3JkICdBJyArICgob3JkIGMgKyBuIC0gb3JkICdBJykgYG1vZGAgMjYpCiAgICAgICAgICB8IGlzTG93ZXIgYyA9IGNociAkIG9yZCAnYScgKyAoKG9yZCBjICsgbiAtIG9yZCAnYScpIGBtb2RgIDI2KQogICAgICAgICAgfCBvdGhlcndpc2UgPSBjIAoKLS0gcm90YXRlIGlucHV0IHN0cmluZyBieSBuCnNoaWZ0U3RyaW5nOjogSW50IC0+IFN0cmluZyAtPiBTdHJpbmcKc2hpZnRTdHJpbmcgbiA9IG1hcCAoc2hpZnQgbikKCi0tY291bnQgbnVtYmVyIG9mIGluc3RhbmNlcyBvZiBhIGxldHRlciBpbiBhIHN0cmluZwppbnN0YW5jZXM6OiBDaGFyIC0+IFN0cmluZyAtPiBJbnQKaW5zdGFuY2VzIHggeXMgPSBsZW5ndGggJCBlbGVtSW5kaWNlcyAodG9Mb3dlciB4KSAobWFwIHRvTG93ZXIgeXMpCgotLWNhbGN1bGF0ZSBmcmVxdWVuY3kgb2YgYSBsZXR0ZXIKZnJlcXVlbmN5OjogQ2hhciAtPiBTdHJpbmcgLT4gRmxvYXQKZnJlcXVlbmN5IHggc3RyaW5nID0gZnJvbUludGVncmFsIChpbnN0YW5jZXMgeCBzdHJpbmcpIC8gZnJvbUludGVncmFsIChsZW5ndGggc3RyaW5nKQoKLS0gY3JlYXRlcyBhIHNvcnRlZCBkZXNjZW5kaW5nIGxpc3QgbGlzdCBvZiB0dXBsZXMgb2YgbGV0dGVyIGFuZCBpdHMgZnJlcXVlbmN5CmZyZXF1ZW5jeUFsbDo6IFN0cmluZyAtPiBbKENoYXIsIEZsb2F0KV0KZnJlcXVlbmN5QWxsIHN0cmluZyA9IHJldmVyc2UgJCBzb3J0QnkgKGNvbXBhcmluZyBzbmQpIFsgKHggLCBmcmVxdWVuY3kgeCBzdHJpbmcpIHwgeCA8LSBbJ2EnIC4uICd6JyBdIF0KCi0tIGFzc3VtaW5nICdlJyBpcyB0aGUgbW9zdCBjb21tb24gbGV0dGVyIGluIGVuZ2xpc2gKZGVjb2RlOjogU3RyaW5nIC0+IFN0cmluZwpkZWNvZGUgc3RyaW5nID0gc2hpZnRTdHJpbmcgKG9yZCAnZScgLSBvcmQgKGZzdCAkIGhlYWQgJCBmcmVxdWVuY3lBbGwgc3RyaW5nKSkgc3RyaW5nCgotLSBwYXJzZSBhcmd1bWVudHMsIGlmIG5vIHN1cHBsaWVkIHVzZSBkZWZhdWx0CnBhcnNlQXJnczo6IFtTdHJpbmddIC0+IFN0cmluZwpwYXJzZUFyZ3MgYXJncyA9IGlmIG51bGwgYXJncyB0aGVuIGRlZmF1bHRFbmNvZGVkTXNnIGVsc2UgaGVhZCBhcmdzCgotLSBlbnRyeSBwb2ludAptYWluID0gZG8KIGFyZ3MgPC0gZ2V0QXJncwogbGV0IG1zZyA9IHBhcnNlQXJncyBhcmdzCiBwdXRTdHJMbiAiQ2Flc2FyIEN5cGhlciBTb2x2ZXIgKGFpLWNsYXNzKSAtIG5haXZlIGFwcHJvYWNoXG4iCiBwdXRTdHJMbiAiZW5jb2RlZCBtZXNzYWdlOiIKIHB1dFN0ckxuIG1zZwogcHV0U3RyTG4gIlxuVGhlIHRvcCBjYW5kaWRhdGUgYmFzZWQgb24gdGhlIGZyZXF1ZW5jeSBvZiB0aGUgbGV0dGVyICdlJyBpbiBlbmdsaXNoIGlzOlxuIgogcHV0U3RyTG4gJCBkZWNvZGUgbXNnCg==