import Numeric import Data.Char import System.Random import System.Random.Shuffle import System.IO import System.Console.ANSI import Control.Monad -- Settings: startingLifes, tableSize :: Int startingLifes = 4 tableSize = 32 -- Static data targetWords :: [String] targetWords = [ "mutate" , "dubbed" , "repair" , "rugrat" , "system" , "human" , "defend" , "stream" , "wagons" , "subtle" , "carved" , "banana" , "guitar" , "temple" , "frozen" , "candle" , "rocket" , "spring" , "sunset" , "rhythm" , "yellow" , "zephyr" , "jigsaw" , "quasar" , "fluffy" , "whale" , "glitch" , "pickle" , "planet" , "rabbit" , "silent" , "thirst" , "violet" , "waffle" , "summer" , "breeze" , "flame" , "rocket" , "dragon" , "bucket" , "palace" , "shadow" , "thorny" , "window" , "fierce" , "ocean" , "whisky" , "forest" , "python" , "garden" , "humble" , "jungle" , "kettle" , "mellow" , "noodle" , "pickle" , "quartz" , "rattle" , "sizzle" , "tender" , "unique" , "velvet" , "wombat" , "yogurt" ] garbage :: String garbage = "$@#`|:=;-<>()[]{}%+*^/\\!?'\"" -- entryWidth :: Int entryWidth = 12 -- data GameHand = GameHand { start :: Int , entries :: [String] , litteredEntries :: [String] , solution :: String } deriving Show likeness :: Eq a => [a] -> [a] -> Int likeness [] _ = 0 likeness _ [] = 0 likeness (g:gx) (l:lx) = (if g == l then 1 else 0) + (likeness gx lx) truncatedHex' :: String -> String truncatedHex' s | len < 4 = truncatedHex' ("0" ++ s) | len == 4 = "0x" ++ s where len = length s truncatedHex :: Int -> String truncatedHex i | len < 4 = truncatedHex' $ "0" ++ h | len > 4 = truncatedHex $ i' `mod` (16^4 - tableSize) | len == 4 = truncatedHex' $ h where { i' = abs i ; h = showHex i' "" ; len = length h } garbageChar :: IO Char garbageChar = do rindex <- randomIO :: IO Int return $ garbage !! (mod rindex (length garbage)) garbageEntry' :: Int -> [IO Char] -> [IO Char] garbageEntry' i s | i > 0 = [garbageChar] ++ (garbageEntry' (i - 1) s) | otherwise = s garbageEntry :: [IO Char] garbageEntry = garbageEntry' entryWidth [] litterRight :: String -> IO String litterRight e | (length e) < entryWidth = do { a <- garbageChar ; litterRight (e ++ [a]) } | otherwise = return e litterEntry' :: String -> Int -> IO String litterEntry' e i | i > 0 = do { a <- garbageChar ; litterEntry' ([a] ++ e) (i - 1) } | otherwise = litterRight e litterEntry :: String -> IO String litterEntry e = do r <- randomIO :: IO Int let i = (r `mod` entryWidth - (length e)) litterEntry' e i litterEntries :: [String] -> IO [String] litterEntries l | (length l) < tableSize = do { a <- sequence garbageEntry ; litterEntries (l ++ [a]) } | otherwise = do { a <- sequence garbageEntry ; return l } renderEntry :: Int -> String -> String renderEntry i w = (truncatedHex i) ++ " " ++ w renderOdd, renderEven, renderEntries :: Int -> [String] -> String renderOdd i [] = "" renderOdd i (l:ls) = (renderEntry i l) ++ " " ++ (renderEven (i + entryWidth) ls) renderEven i [] = "" renderEven i (l:ls) = (renderEntry i l) ++ "\n" ++ (renderOdd (i + entryWidth) ls) renderEntries i l = renderOdd i l printConsole :: GameHand -> IO () printConsole s = do putStr $ renderEntries (start s) (litteredEntries s) printLifes, printLifes' :: Int -> IO () printLifes' i | i > 0 = do { putStr " #" ; printLifes' (i - 1) } | otherwise = putStrLn "" printLifes i = do putStr "Attempts remaining:" printLifes' i gameCycle :: Int -> GameHand -> IO () gameCycle i s = do putStrLn "Password Required" putStrLn "" printLifes i putStrLn "" printConsole s putStr ">" hFlush stdout guess <- getLine putStrLn "" putStrLn $ ">" ++ (map toUpper guess) if guess == (solution s) then do putStrLn "" putStrLn "> Password Accepted." else do putStrLn ">Entry denied." let l = likeness guess (solution s) when (elem guess (entries s)) $ putStrLn $ ">Likeness=" ++ show l putStrLn "" putStrLn "" when (i > 1) $ gameCycle (i - 1) s main :: IO () main = do setSGR [SetConsoleIntensity BoldIntensity, SetColor Foreground Dull Yellow] seed <- randomIO :: IO Int i <- randomIO :: IO Int let ronnie = mkStdGen seed let e = take 12 (shuffle' targetWords (length targetWords) ronnie) e' <- litterEntries e e' <- mapM litterEntry e' let e'' = shuffle' e' (length e') ronnie let solutionIndex = i `mod` (length targetWords) gameCycle startingLifes (GameHand { entries = e , litteredEntries = e'' , start = seed , solution = targetWords !! solutionIndex } )