import Data.List (partition, intersect, nub)
import Control.Monad
import System.Random (StdGen, getStdRandom, randomR)
import Text.Printf

numberOfDigits = 4 :: Int

main = bullsAndCows

bullsAndCows :: IO ()
bullsAndCows = do
    digits <- getStdRandom $ pick numberOfDigits ['1' .. '9']
    putStrLn "Guess away!"
    loop digits

  where loop digits = do
            input <- getLine
            if okay input
              then
                  let (bulls, cows) = score digits input in
                  if bulls == numberOfDigits then
                      putStrLn "You win!"
                  else do
                      printf "%d bulls,%d cows.\n" bulls cows
                      loop digits
              else do
                  putStrLn "Malformed guess; try again."
                  loop digits

        okay :: String -> Bool
        okay input =
            length input == numberOfDigits && 
            input == nub input &&
            all legalchar input
          where legalchar c = '1' <= c && c <= '9'

        score :: String -> String -> (Int, Int)
        score secret guess = (length bulls, cows)
          where (bulls, nonbulls) = partition (uncurry (==)) $
                    zip secret guess
                cows = length $ uncurry intersect $ unzip nonbulls

pick :: Int -> [a] -> StdGen -> ([a], StdGen)

pick n l g = f n l g (length l - 1) []
  where  f 0 _ g _   ps = (ps, g)
         f n l g max ps =
             f (n - 1) (left ++ right) g' (max - 1) (picked: ps)
          where (i, g') = randomR (0, max) g
                (left, picked: right) = splitAt i l