summaryrefslogtreecommitdiff
path: root/Game.hs
blob: 816a65c43cb37a232d0d189cb5f4755abc34f770 (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
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
    let player' = case decision of
                    Nothing -> player `draw` (take att stock)
                    Just card -> player `shed` card
    let (left, right) = splitAt pidx plyrs
    let plyrs' = left ++ [player'] ++ (tail right)

    let stock' = case decision of
                   Nothing -> tail stock
                   Just _ -> stock
    let disc' = case decision of
                  Nothing -> disc
                  Just card -> card:disc

    let att' = case decision of
                 Just (Card _ C.Two)   -> if att == 1 then 2 else att + 2
                 Just (Card _ C.Three) -> if att == 1 then 3 else att + 3
                 _ -> 1
    let dir' = case decision of
                 Just (Card _ C.Queen) -> negate dir
                 _ -> dir
    let pidx' = case decision of
                  Just (Card _ C.Jack) -> (pidx + 2 * dir') `mod` length plyrs
                  _ -> (pidx + dir') `mod` length plyrs
    let game' = Game plyrs' pidx' att' dir' stock' disc'

    case decision of
      Nothing -> do
          putStrLn $ (P.name player) ++ " draws " ++ (C.showCard $ head stock)
      Just card -> do
          putStrLn $ (P.name player) ++ " plays " ++ (C.showCard card)

    if null $ P.cards player'
       then return game'
       else beginTurn game'

-- | 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) })