星期六, 3月 30, 2013

Guessing Number using Haskell

今天利用一點時間寫出來的簡單版(困難版我也不會 XD),明天有空再補心得 XD。如果有什麼可以加強或改寫的的地方還請指教,我會很開心的,謝謝 :)。

import System.Random

checkNum :: [Integer] -> [Integer] -> (Integer, Integer)
checkNum input ans = (posNum input ans, correctNum input ans) 
    where posNum input ans = sum $ zipWith (boolValue (x==y)) input ans 
          correctNum input ans = foldr (\x y -> y + boolValue (any (== x) ans)) 0 input
          boolValue a = if a == True then 1 else 0

game :: [Integer] -> IO ()
game ans = do
    input <- fmap (fmap (\x -> read [x])) getLine
    
    let (rpn, rn) = checkNum input ans
    if rpn == 4 then
        do putStrLn "Win"
           return () 
    else
        do putStrLn $ show rpn ++ "A" ++ show rn ++ "B"
           game ans

main = do
    gen <- getStdGen
    let ans = take 4 (randomRs (1,9) gen)
    game ans

---
繼續前進。


T55555的回應給了許多寶貴的意見,真的很謝謝他每次都給予很好的建議(至於我這麼晚回的原因是因為仍在當兵中...Orz),我也做出了回應,現在把整個修完的 code 再貼一次如下。


import System.Random

-- Wrong game logic
-- checkNum :: [Int] -> [Int] -> (Int, Int)
-- checkNum input ans = (posNum input ans, correctNum input ans) 
--    where posNum input ans = foldr (\x y -> y + fromEnum x) 0 $ zipWith (==) input ans 
--          correctNum input ans = foldr (\x y -> y + (fromEnum (any (==x) ans))) 0 input 

checkNum' :: [Int] -> [Int] -> (Int, Int)
checkNum' input ans = foldr (check ans) (0, 0) $ zip input ans 
    where check ans (x,y) (p,c) = 
            if x == y then (p+1,c)
            else
                if (any (==x) ans) then (p,c+1)
                else (p,c)

game :: [Int] -> IO ()
game ans = do
    input <- fmap (fmap (\x -> read [x])) getLine
    
    let (rpn, rn) = checkNum' input ans
    if rpn == 4 then
        do putStrLn "Win"
           return () 
    else
        do putStrLn $ show rpn ++ "A" ++ show rn ++ "B"
           game ans

main = do
    gen <- getStdGen
    let ans = take 4 (randomRs (1,9) gen)
    putStrLn $ show ans
    game ans

2 則留言:

T55555 提到...

You code seems miss something and cannot compile.
It seems to me two lines are missing code during post, my guess they are:

posNum input ans = sum $ zipWith (\x y -> boolValue (x==y)) input ans

correctNum input ans = foldr (\x y -> y + boolValue (any (== x) ans)) 0

If they are like above, then there is logic error:

checkNum [1,2,3,5] [6,7,8,5] will return (1,1)

BTW. Don't need boolValue, you can just use fromEnum.
fromEnum False ==> 0
fromEnum True ==> 1
This is because Bool is instance of Enum.

I tried to implememnt checkNum, here is my version:

checkNum :: (Eq a) => [a] -> [a] -> (Int, Int)
checkNum xs ys = foldl check (0, 0) $ productSet (zip xs [1..]) (zip ys [1..])
  where
  productSet xs ys = concatMap (zip xs . repeat) ys
  check ab@(a,b) ((v1, i1), (v2, i2))
    | v1 /= v2 = ab
    | i1 == i2 = (a+1, b)
    | otherwise = (a, b+1)

yen3 提到...

Thanks for your comments.

You are right. The piece of code is missing. Now I have corrected it in the original post.

The logic error about the function "checkNum" is my fault because I don't know the checking policy is like what you wrote.

If I write the code without 'boolValue' function, I can write as the follows (It is still using the wrong logic. XD)

checkNum :: [Int] -> [Int] -> (Int, Int)
checkNum input ans = (posNum input ans, correctNum input ans)
where posNum input ans = foldr (\x y -> y + fromEnum x) 0 $ zipWith (==) input ans
correctNum input ans = foldr (\x y -> y + (fromEnum (any (==x) ans))) 0 input

Follow your logic, I try to write the checkNum function again.

checkNum' :: [Int] -> [Int] -> (Int, Int)
checkNum' input ans = foldr (check ans) (0, 0) $ zip input ans
where check ans (x,y) (p,c) =
if x == y then (p+1,c)
else
if (any (==x) ans) then (p,c+1)
else (p,c)

I learned some good ideas from your comments. Thank you.