This is a simple game where the goal is to type digits of PI. If a wrong digit is entered the game restarts, and the user is told how many digits he got right, as well as the next few digits.
module Main where import System.IO import Data.Function (fix) import Data.Char (isDigit, digitToInt) import Data.List.Split (chunksOf) -- Digits of pi pi' = tail $ g (1,0,1,1,3,3) where g (q,r,t,k,n,l) | 4*q+r-t<n*t = n : g (10*q,10*(r-n*t),t,k,div(10*(3*q+r))t-10*n,l) | otherwise = g (q*k,(2*q+r)*l,t*l,k+1,div(q*(7*k+2)+r*l)(t*l),l+2) -- Digits from stdin getDigits = map (fromIntegral . digitToInt) . filter isDigit <$ > getContents main :: IO () main = do hSetBuffering stdin NoBuffering hSetBuffering stdout NoBuffering (getDigits >>=) . fix $ \rec inp -> do let (past, future) = span (uncurry (==)) $ zip pi' inp -- Print first digit putStr "3." -- Delimiter user input with spaces mapM_ (`seq` putChar ' ') . map last $ chunksOf 2 past -- Print score putStrLn $ "\nScore: " ++ show (length past) -- Print next few digits putStrLn . unwords . ("Next digits:" :) . take 10 . drop (length past `div` 2) . map (>>=show) $ chunksOf 2 pi' -- Try again putStrLn "" rec . map snd $ tail future
One concern that I have with this code is the last line
rec . map snd $ tail future. If the digits of PI were finite, then the game would end once the user had entered as many digits as there are in PI, because zip stops once one of its two input lists are empty. Therefor, evaluating future is clearly doing work lazily.
With an infinite PI, does this mean that my program will get slightly slower for every game played as the zips accumulate, until eventually it is entirely unplayable?