summaryrefslogtreecommitdiff
path: root/Game.hs
blob: 871d188735c6ca1050ddeea2d3e26a64aa366137 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
module Game where

import Data.Char (isDigit)
import Control.Monad (when)
import System.Random (RandomGen)
import Player (Player, shed, draw)
import qualified Player as P
import Card (Card(..))
import qualified Card as C

data Game = Game { players :: [Player]
                 , playerIdx :: Int
                 , attack :: Int
                 , direction :: Int      -- ^ 1 when CCW, -1 when CW
                 , stockPile :: [Card]
                 , discardPile :: [Card]
                 }

-- | Begin new round of game.
beginRounds :: Int -> Game -> IO Game
beginRounds n game = do
    if n == 0
       then return game
       else do
           game' <- beginTurn game
           beginRounds (n - 1) game'

-- | Let current player take their turn.
beginTurn :: Game -> IO Game
beginTurn game@(Game plyrs pidx att dir stock disc) = do
    putStrLn $ replicate 80 '-'
    putStrLn $ "Current attack: " ++ show att
    decision <- prompt game

    let player = plyrs !! pidx
    game'@(Game _ pidx' _ _ _ _)
        <- case decision of
             Nothing -> drawAndSkip game
             Just card -> shedAndContinue card game

    if pidx' == pidx  -- shedAndContinue does this when player wins
       then return game'
       else beginTurn game'

-- | Game state after player draws card(s) and skips turn.
drawAndSkip :: Game -> IO Game
drawAndSkip game@(Game plyrs pidx att dir stock disc) = do
    let player = plyrs !! pidx
    let player' = player `draw` (take att stock)
    let plyrs' = P.update plyrs pidx player'
    let pidx' = (pidx + dir) `mod` length plyrs
    let stock' = drop att stock
    putStrLn $ (P.name player) ++ " draws " ++ (C.showCards $ take att stock)
    return $ Game plyrs' pidx' 1 dir stock' disc

-- | Game state after player sheds card.
shedAndContinue :: Card -> Game -> IO Game
shedAndContinue card game@(Game plyrs pidx att dir stock disc) = do
    let player = plyrs !! pidx
    let player' = player `shed` card
    let disc' = card:disc
    let plyrs' = P.update plyrs pidx player'
    let dir' = case card of
                 Card _ C.Queen -> negate dir
                 _ -> dir
    let pidx' = if null $ P.cards player'
                   then pidx  -- round ends
                   else case card of
                          Card _ C.Jack -> (pidx + 2 * dir') `mod` length plyrs
                          _ -> (pidx + dir') `mod` length plyrs
    let att' = case card of
                 Card _ C.Two   -> if att == 1 then 2 else att + 2
                 Card _ C.Three -> if att == 1 then 3 else att + 3
                 _ -> 1
    putStrLn $ (P.name player) ++ " plays " ++ (C.showCard card)
    return $ Game plyrs' pidx' att' dir' stock disc'

-- | Prompt player to play a card (or draw card and skip turn).
prompt :: Game -> IO (Maybe Card)
prompt game@(Game plyrs pidx att _ _ _) = do
    let player = plyrs !! pidx
    let cards = P.cards player
    putStrLn $ P.name player ++ "'s turn (input 0 to skip turn and draw card)"
    putStrLn $ P.showCards player
    cardIdxStr <- getLine
    if any (== False) $ map isDigit cardIdxStr
        then do
            putStrLn "Please input card #"
            prompt game
        else do
            let cardIdx = read cardIdxStr - 1
            if cardIdx == -1
                then do
                   return Nothing
                else do
                    if cardIdx < 0 || cardIdx >= length cards
                        then do
                            putStrLn "This card does not exist, try again"
                            prompt game
                        else return $ Just (cards !! cardIdx)

-- | Deal c cards to each player in game.
dealCards :: Int -> Game -> Game
dealCards c game@(Game plyrs _ _ _ stock disc)
  | (length $ P.cards $ head plyrs) < c =
      dealCards c game { players = newPlyrs, stockPile = newStock }
  | otherwise = game
    where newPlyrs = giveOneCardEach stock plyrs
          newStock = drop c stock
          giveOneCardEach =
              zipWith (\card player -> player { P.cards = card:(P.cards player) })