-- 数当てゲーム MOO import Random import List ((\\), nub) -- Listのモジュールを取り込み (\\), nub を使う import Char (isDigit) type PackedInt = Int type UnpackedInt = [Int] type MOOProduct = (Int, Int) -- ユーティリティ関数 showUnpackedInt :: UnpackedInt -> String showUnpackedInt ds = concatMap show ds -- concatMap f = concat . map f showMOOProduct :: MOOProduct -> String showMOOProduct (bulls, cows) = " Bulls: " ++ show bulls ++ ", Cows: " ++ show cows pack :: UnpackedInt -> PackedInt pack ds = foldl (\ x y -> 10*x + y) 0 ds unpack :: Int -> PackedInt -> UnpackedInt unpack 1 d = [d] unpack (n+1) d = unpack n q ++ [r] where (q, r) = d `divMod` 10 disjoint :: UnpackedInt -> Bool disjoint xs = length xs == length (nub xs) mooNum :: Int -> MOOProduct -> Bool mooNum n (bulls, cows) = bulls == n && cows == 0 -- 出題プログラム main :: IO () main = moo 4 -- コンパイルして生成されるのは4桁版 moo :: Int -> IO () moo n = do randomGen <- newStdGen let randoms = randomRs (0, 10^n-1) randomGen -- 0 <= r < 10^n を満たす乱数 r の無限列を生成 answer = (head . filter disjoint . map (unpack n)) randoms -- n 桁の数に重複のないものを一つ抽出 loop n answer [] loop :: Int -> UnpackedInt -> [UnpackedInt] -> IO () loop n answer history = do putStr "Your guess? " -- 入力を促す guessStr <- getLine -- 改行までの文字列を入力 if not (legal guessStr) then loop n answer history -- redo else let guess = unpack n (read guessStr :: Int) mooProduct = score answer guess in if mooNum n mooProduct then do putStrLn (" You got it in " ++ show (1+length history) ++ " guesses!") else do putStrLn (showMOOProduct mooProduct) loop n answer (guess:history) where legal str = length str == n && and (map isDigit str) score :: UnpackedInt -> UnpackedInt -> MOOProduct score xs ys = (bulls, cows) where (xs', ys') = unzip [ (x,y) | (x,y) <- zip xs ys, x /= y ] bulls = length xs - length xs' cows = length xs' - length (xs' \\ ys') -- 解答プログラム digits :: [Int] digits = [0..9] gen :: Int -> [UnpackedInt] gen n = gens n [] where gens :: Int -> [Int] -> [UnpackedInt] gens 0 rs = if disjoint rs then [rs] else [] gens (n+1) rs = concatMap (\r -> gens n (rs++[r])) digits {- 参考: gen4 == gen 4 gen4 = [ ds | p<-digits, q<-digits, r<-digits, s<-digits, let ds=[p,q,r,s], disjoint ds] -} solver :: Int -> Int -> IO () solver n answer = putStr result where score1 = score (unpack n answer) -- 予想からMOO積を返す関数 candidates = gen n result = solver1 n score1 [] candidates solver1 :: Int -> (UnpackedInt -> MOOProduct) -> [UnpackedInt] -> [UnpackedInt] -> String solver1 n score1 history (guess:rs) = solver2 n score1 history rs guess solver2 :: Int -> (UnpackedInt -> MOOProduct) -> [UnpackedInt] -> [UnpackedInt] -> UnpackedInt -> String solver2 n score1 history rs guess = let mooProduct = score1 guess in if mooNum n mooProduct then "Computer found " ++ showUnpackedInt guess ++ " in " ++ show (1+length history) ++ " guesses." else let rs' = [ r | r <- rs, score guess r == mooProduct ] in "Computer guesses " ++ showUnpackedInt guess ++ "\n" ++ showMOOProduct mooProduct ++ "\n" ++ solver1 n score1 (guess:history) rs'