{- ストリームを使った対話型 Moo プログラム(山下) -} {- dialogMoo: コンピュータの出題を対話しながらコンピュータが当てる playMoo: コンピュータの出題を対話しながらひとが当てる helpMoo: ひとの出題を対話しながらコンピュータが当てる -} module MooDialogue where import Monad (foldM) import Random -- 問題の生成,候補のしぼり込みに使う順列の生成 ------------------------ perm :: [a] -> Int -> [[a]] -- 順列の生成 perm _ 0 = [[]] perm [] _ = [] perm xs n = concat [ pm n hs ts | (hs,ts) <- splits xs ] where pm _ _ [] = [] pm n hs (t:ts) = [ t:ys | ys <- perm (hs++ts) (n-1) ] splits :: [a] -> [([a],[a])] splits [] = [([],[])] splits xxs@(x:xs) = ([],xxs) : [ (x:ys,zs) | (ys,zs) <- splits xs ] nPr :: Int -> Int -> Int -- 順列の数 nPr n r = product [n-r+1..n] nthPerm :: [a] -> Int -> Int -> [a] -- n番目の順列 nthPerm xs r m = nprm xs (length xs) r m where nprm _ _ 0 0 = [] nprm xs n r m = case divMod m (nPr (n-1) (r-1)) of (p,q) -> case splitAt p xs of (xs,y:ys) -> y : nprm (xs++ys) (n-1) (r-1) q ---- クライアント/サーバ シミュレーション --------------------------------- server :: (a -> b) -> ([a] -> [b]) server _ [] = [] server process (req:reqs) = process req : server process reqs client :: a -> (b -> a) -> ([b] -> [a]) client req0 next ~(resp:resps) = req0 : client (next resp) next resps ---- MOO --------------------------------------------------------------- type Moo = String type Bulls = Int type Cows = Int type Prod = (Bulls,Cows) type History = [(Moo,Prod)] type Candidates = [Moo] type State = (Candidates,Moo,History) score :: Moo -> Moo -> Prod -- Moo積を求める score answer guess = (bulls,cows) where bulls = sum (zipWith ((fromEnum .) . (==)) answer guess) cows = sum (map (fromEnum . (`elem` answer)) guess) - bulls guess :: Prod -> State -> (Moo, State) -- Moo を推測する guess prod (cads,moo,his) = (moo',(cads',moo',his')) where his' = (moo,prod):his moo':cads' = filter (\ x -> and [ score x m == p | (m,p) <- his' ]) cads ndig :: Int ndig = 4 digs :: String digs = "0123456789" cs :: [Moo] cs = perm digs 4 ---- 質問者コンピュータ,解答者コンピュータ ---------------------------- dialogMoo = do { gen <- newStdGen ; let (r,_) = randomR (0,nPr (length digs) ndig - 1) gen ans = nthPerm digs ndig r reqs = client ("0123",(cs,"0123",[])) (uncurry guess) resps resps = server (fstApp (score ans)) reqs comms = map (uncurry (display ans)) $ zip (map fst reqs) (takeWhile (/=(4,0)) (map fst resps)++[(4,0)]) in mapM_ putStrLn comms >> putStrLn ("Computer got it in "++show (length comms)++" guesses.") } fstApp f (x,y) = (f x,y) display :: Moo -> Moo -> Prod -> String display ans "" _ = "Answer: "++ans display ans moo prod = "Guess : "++moo++"\nScore : "++show prod ---- 質問者コンピュータ,解答者ひと ------------------------------------ playMoo = do { gen <- newStdGen ; let (r,_) = randomR (0,nPr (length digs) ndig - 1) gen ans = nthPerm digs ndig r reqs = client getLine (const getLine) resps resps = server (scoreIO ans) reqs in catch (foldM trav 0 resps) (const (return 0)) >> return () } scoreIO :: Moo -> IO Moo -> IO Prod scoreIO ans moo = do { putStr "Guess: "; moo >>= return . score ans } trav :: Int -> IO Prod -> IO Int trav i iop = do { p <- iop ; if p == (4,0) then putStrLn ("You got it in "++show (i+1)++" guesses.") >> fail "" else (putStr "Score: " >> putStrLn (show p)) >> return (i+1) } ---- 質問者ひと,解答者コンピュータ ------------------------------------ helpMoo = let req0 = getLine >>= return . read reqs = client req0 (const req0) resps resps = server id reqs in putStrLn "Guess: 0123" >> catch (foldM guessIO (cs,"0123",[]) resps) (const (return ([],"",[]))) >> return () guessIO :: State -> IO Prod -> IO State guessIO s iop = do { putStr "Score: " ; p <- iop ; let (m,s'@(cs,_,_)) = guess p s in putStrLn ("Guess: "++m) >> if null cs then fail "You must get it." else return s' }