module Main where
import Control.Applicative ((<$>))
import Control.Monad (forever)
import Control.Monad.Trans (lift)
import Control.Monad.State (StateT, evalStateT, get, put)
import System.Random (getStdRandom, randomR)
data Move = Rock | Paper | Scissors deriving (Show, Eq, Enum)
data Result = HumanWins | AIWins | Draw deriving (Show, Eq)
beats :: Move -> Move -> Maybe Bool
Rock `beats` Scissors = Just True
Scissors `beats` Paper = Just True
Paper `beats` Rock = Just True
x `beats` y | x == y = Nothing
| otherwise = Just False
-- 1 - Rock, 2 - Paper, 3 - Scissors
intToMove :: Int -> Move
intToMove n = toEnum $ n - 1
askHumanMove :: IO Move
askHumanMove = do
putStr "Enter your move (1 - Rock, 2 - Paper, 3 - Scissors): "
intToMove <$> (read :: String -> Int) <$> getLine
generateAIMove :: IO Move
generateAIMove = intToMove <$> (getStdRandom $ randomR (1, 3))
doMove :: IO Result
doMove = do
aiMove <- generateAIMove
humanMove <- askHumanMove
putStrLn $ "AI move - " ++ (show aiMove) ++ ", human move - " ++ (show humanMove)
return $ case aiMove `beats` humanMove of
Just True -> AIWins
Just False -> HumanWins
_ -> Draw
gameStep :: StateT (Int, Int) IO ()
gameStep = do
s@(aiScore, humanScore) <- get
result <- lift doMove
newScore <- case result of
Draw -> lift (putStrLn "Draw... Once again!") >> return s
AIWins -> lift (putStrLn "AI wins!") >> return (aiScore + 1, humanScore)
HumanWins -> lift (putStrLn "Human wins!") >> return (aiScore, humanScore + 1)
lift $ displayScore newScore
put newScore
displayScore :: (Int, Int) -> IO ()
displayScore (aiScore, humanScore) = putStrLn $ "SCORE - " ++ (show aiScore) ++ ":" ++ (show humanScore) ++ " (AI:Human)"
main :: IO ()
main = evalStateT (forever gameStep) (0, 0)