You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

154 lines
4.2 KiB

module Morra
(runGame, Game(..), Player(..), PlayerType(..))
where
import Data.Maybe (listToMaybe)
import qualified Data.Map as M
import Control.Monad (forever)
import Control.Monad.Trans.State
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import System.Exit (exitSuccess)
import System.Random (randomRIO)
import System.Console.ANSI
import System.IO (hFlush, stdout)
-- Two player game
-- Player chooses 0-5 and number
-- AI chooses 0-5 and number
type Source = (Integer, Integer)
type Table = M.Map Source Integer
type Guesses = (Integer, Integer)
data PlayerType = Human | AI Guesses Table deriving (Show, Eq)
data Player = Player {
pType :: PlayerType,
score :: Integer
} deriving (Show, Eq)
data Game = Game {
playerA :: Player,
playerB :: Player,
pointsToWin :: Integer
} deriving (Show, Eq)
-- type Score = (Integer, Integer)
type GameState = StateT Game IO
isPlayerWinner :: (Game -> Player) -> Game -> Bool
isPlayerWinner p g = (score . p) g >= pointsToWin g
endGameMsg :: Bool -> Bool -> IO ()
endGameMsg True True = putStrLn "Everybody is a winner!" >> exitSuccess
endGameMsg True False = putStrLn "Player A is a winner!" >> exitSuccess
endGameMsg False True = putStrLn "Player B is a winner!" >> exitSuccess
endGameMsg _ _ = return ()
gameEnd :: GameState ()
gameEnd = do
g <- get
let pA = isPlayerWinner playerA g
pB = isPlayerWinner playerB g
liftIO $ endGameMsg pA pB
return ()
handleHumanPlayer :: IO (Integer, Integer)
handleHumanPlayer = do
putStr "Fingers and total (f, t): "
hFlush stdout
s <- getLine
clearScreen
case fmap fst . listToMaybe . reads $ s of
Nothing -> do
putStrLn "Incorrect format."
handleHumanPlayer
Just a -> return a
handleAIPlayer :: Guesses -> Table -> IO (Integer, Integer)
handleAIPlayer g t = do
f <- randomRIO (0,5)
g <- case M.lookup g t of
Nothing -> randomRIO (0, 5)
Just n -> return n
return (f, f + g)
updateAI :: Player -> Integer -> Player
updateAI p n = case pType p of
Human -> p
AI g@(g1, g2) t -> p { pType = AI g' t' }
where g' = (g2, n)
t' = M.insert g n t
updateAIs :: (Integer, Integer) -> GameState ()
updateAIs (gA, gB) = do
g <- get
let pA = playerA g
pB = playerB g
pA' = updateAI pA gB
pB' = updateAI pB gA
g' = g { playerA = pA' }
put $ g' { playerB = pB' }
handlePlayer :: (Game -> Player) -> GameState (Integer, Integer)
handlePlayer p = do
g <- get
let player = p g
case pType player of
Human -> liftIO handleHumanPlayer
AI g t -> liftIO $ handleAIPlayer g t
handleGuessPlayerA :: Integer -> Integer -> GameState ()
handleGuessPlayerA guess total =
if (guess == total)
then do
g <- get
let p = playerA g
s = score p
put $ g { playerA = (p { score = s + 1 })}
liftIO $ putStrLn $ "PlayerA guessed correctly!"
else return ()
handleGuessPlayerB :: Integer -> Integer -> GameState ()
handleGuessPlayerB guess total =
if (guess == total)
then do
g <- get
let p = playerB g
s = score p
put $ g { playerB = (p { score = s + 1 })}
liftIO $ putStrLn $ "PlayerB guessed correctly!"
else return ()
runGame :: GameState ()
runGame = forever $ do
gameEnd
liftIO $ putStrLn "Turn PlayerA"
(f, g) <- handlePlayer playerA
liftIO $ putStrLn "Turn PlayerB"
(f', g') <- handlePlayer playerB
let total = f + f'
liftIO $ do
putStrLn "=========================="
putStr $ "PlayerA showed: " ++ show f
putStrLn $ " and guessed: " ++ show g
putStr $ "PlayerB showed: " ++ show f'
putStrLn $ " and guessed: " ++ show g'
putStrLn $ "Total was: " ++ show total
putStrLn "=========================="
handleGuessPlayerA g total
handleGuessPlayerB g' total
liftIO $ putStrLn "=========================="
-- update any ai
updateAIs (f, f')
-- print the player status
g <- get
let pA = playerA g
pB = playerB g
liftIO $ do
putStrLn $ "PlayerA: " ++ show pA
putStrLn $ "PlayerB: " ++ show pB