-
Notifications
You must be signed in to change notification settings - Fork 1
/
Utils.hs
260 lines (217 loc) · 9.42 KB
/
Utils.hs
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
module Utils where
import UnoDataModels
import Control.Monad.State as State
import System.IO.Unsafe (unsafePerformIO)
import System.Random
import System.Posix.Process.ByteString
import System.Exit
import Control.Arrow
import Text.Read as Read
import Config
-------------------------------------------------
-- Field Checking
-------------------------------------------------
isRobotPlayer :: GameState -> Bool
isRobotPlayer _game = whoseTurn _game /= realPlayer _game
-------------------------------------------------
-- Info Retrieving
-----------------------------------------------
getCurrPlayerName :: GameState -> String
getCurrPlayerName _game = getPlayerName (whoseTurn _game) _game
-- @Int playerId
-- @Int #of cards in hand
countPlayerHandCards :: Int -> GameState -> Int
countPlayerHandCards _playerId _game = length $ cardsInHand (getPlayerState _playerId _game)
-- @Int Player ID
getPlayerName :: Int -> GameState -> String
getPlayerName i _game = doGetPlayerName i (players _game )
doGetPlayerName :: Int -> [PlayerState] -> String
doGetPlayerName _playerId _players = name $ _players !! _playerId
getPlayerState :: Int -> GameState -> PlayerState
getPlayerState _playerId _game = players _game !! _playerId
getPlayerCards :: Int -> GameState -> [Card]
getPlayerCards _playerId _game = cardsInHand $ getPlayerState _playerId _game
-------------------------------------------------
-- Display
-------------------------------------------------
showState :: Game ()
showState = do
countTurn
_game <- State.get
State.lift $ putStrLn $ "\n" ++"======================== Game Status ============================" ++"\n"
State.lift $ putStrLn $ "No." ++ show (ithTurn _game)
State.lift $ putStr $ "Current Direction: " ++ showDirection _game ++"\n"
State.lift $ putStr $ "Current Card: "
State.lift $ displayCard $ currCard _game
State.lift $ putStr "\n"
showCards :: [Card] -> String
showCards [] = ""
showCards (_card:_cards) = showCards _cards ++ "\n" ++ show (length _cards+1) ++ ": " ++ show _card
showNextTurn :: Game ()
showNextTurn = do
_game <- State.get
if isRobotPlayer _game then
State.lift $ putStrLn $ " => Next turn goes to " ++ getCurrPlayerName _game
else
State.lift $ putStrLn $ " => It's your turn, " ++ getCurrPlayerName _game ++ "!"
showDropCard :: Card -> Game()
showDropCard _card = do
_game <- State.get
if isRobotPlayer _game then do
State.lift $ putStr $ getCurrPlayerName _game ++ " dropped "
State.lift $ displayCard $ _card
State.lift $ putStr "\n"
else do
State.lift $ putStr $ "You dropped "
State.lift $ displayCard $ _card
State.lift $ putStr "\n"
showAllCardInHand :: Game ()
showAllCardInHand = do
_game <- State.get
State.lift $ putStr $ "\n"++ "Your cards: "
State.lift $ displayCards $ (cardsInHand ( players _game !! whoseTurn _game))
State.lift $ putStr "\n"
showDirection :: GameState -> String
showDirection _game
| dir _game == Clockwise = doShowDir (players _game) ++ name (head (players _game))
| otherwise = doShowDir (reverse (players _game)) ++ name (last (players _game))
doShowDir :: [PlayerState] -> String
doShowDir [] = ""
doShowDir (_player:_players) = name _player ++ " -> " ++ doShowDir _players
-- @Int Winner ID
showWinner :: Int -> Game()
showWinner _winnerId = do
_game <- State.get
if realPlayer _game == _winnerId then
State.lift $ putStrLn "Congrats! You win the game!"
else
State.lift $ putStrLn $ name (players _game !! _winnerId) ++ " win !!"
-------------------------------------------------
-- Action
-------------------------------------------------
-- @Int Winner ID
getWinnerId :: [PlayerState] -> Int
getWinnerId _players = pId $ minimum _players
showScores :: [(String, Int)] -> String
showScores = foldr ((++).(\s -> fst s ++ ": " ++ show (snd s) ++ " | " )) ""
getScores :: [PlayerState] -> [(String, Int)]
getScores = map (name Control.Arrow.&&& score)
-- getScores = map (\p -> (name p, score p))
updateScores :: [PlayerState] -> [PlayerState]
updateScores = map(\p -> p{score=calScore (cardsInHand p)})
calScore :: [Card] -> Int
calScore = foldr ((+).num) 0
countTurn :: Game()
countTurn = do
_game <- State.get
let count = ithTurn _game +1
put _game{ithTurn=count}
--TO DO: cases:
-- 1. no card to draw
-- 2. not enough card to draw
-- 3. after drawing, deck become empty
-- @Int Player
drawCard :: Int -> GameState -> GameState
drawCard _ _game@GameState{deck=[], players=_players} = _game
drawCard usr _game@GameState{deck=(card:ds), players=_players} = _game{deck=ds, players=p}
where
u@PlayerState{cardsInHand=_cardsInHand} = players _game !! usr
p = take usr _players ++ [u{cardsInHand=card:_cardsInHand}] ++
drop (usr+1) _players
-- First Int: nums of cards to draw
-- Second Int: playerID
drawCards :: Int -> Int -> GameState -> GameState
drawCards 0 _ _game = _game
drawCards n usr _game = drawCards (n-1) usr (drawCard usr _game)
-- @Int next ith turn
-- @Int playerID of next turn
nextTurn :: Int -> GameState -> Int
nextTurn 0 _game= whoseTurn _game
nextTurn i _game@GameState{whoseTurn=_whoseTurn,players=_players,dir=_dir} = nextTurn (i-1) _game{whoseTurn = doGetNextTurn _whoseTurn _players _dir}
-- @int Current turn
-- @[PlayerState]
-- @Direction
-- @Int NextTurn
doGetNextTurn :: Int -> [PlayerState]-> Direction -> Int
doGetNextTurn _whoseTurn _players _dir = varifyTurnNum (_whoseTurn + dirt _dir) (length _players)
-- varify the index of current player, making sure it goes in a manner of cycle
-- @Int CurrentTurn
-- @Int number of players
-- @Int Final decision of current turn
varifyTurnNum :: Int -> Int-> Int
varifyTurnNum _whoseTurn num_p
| _whoseTurn == num_p = 0
| _whoseTurn < 0 = num_p - 1
| otherwise = _whoseTurn
-- turn direction into operation
dirt :: Direction -> Int
dirt Clockwise = 1
dirt CounterClockwise = -1
reverseDir :: Direction -> Direction
reverseDir Clockwise = CounterClockwise
reverseDir CounterClockwise = Clockwise
-------------------------------------------------
-- Helper
-------------------------------------------------
-- @Int max number
-- @Int return number in [0,max]
randomInt :: Int -> Int
randomInt n = unsafePerformIO $ getStdRandom $ randomR (0,n-1)
helpInfo :: IO ()
helpInfo = putStr $ unlines
[
"Helpful Commands:",
"/rule:\t\t\tDisplay some basic rules to play the game",
"/effect:\t\tDisplay what all cards do",
"/help:\t\t\tDisplay this help information dialog",
"/quit:\t\t\tExit the game immediately\n"
]
ruleInfo :: IO ()
ruleInfo = putStr $ unlines
[
"How to play the card game? There are some basic rules to follow:",
"1. The aim of the game is to be the first player to play all cards in hand, or",
" to be the player to score the highest points when all cards in the deck are drawn.",
"2. The score one player gain is counted by the cards held by the other players.",
" Numbers cards count their face value, all action cards count 20, and Wild and Wild Draw Four cards count 50.",
"3. On your turn, you must do one of the following:",
"\t play a card matching the discard in color, number or symbol",
"\t play a Wild card, or a Wild Draw Four card",
"\t draw the top card of the deck",
"4. If a player draws a card that is playable, the player has the option of either keeping it or playing it immediately.",
"5. When one player calls \"Uno\", it means there is only one card in his/her hand.",
"6. In a two-player game, the Reverse card acts like a Skip card, thus the other player misses a turn.\n"
]
-- prompt :: IO ()
-- prompt = do
-- iostring <- getLine
-- case readMaybe iostring of
getHelp :: String -> IO ()
getHelp p = case p of
"/help" -> helpInfo
"/rule" -> ruleInfo
"/quit" -> exitImmediately ExitSuccess
-- "/state" -> execStateT showState
"/effect" -> putStrLn showFuncCard
_ -> putStrLn "Input \"/help\" to get help information"
showCardDesc :: CardType -> String
showCardDesc Skip = "Skip: Next player in sequence misses a turn\n"
showCardDesc DrawTwo = "Draw two: Next player in sequence draws two cards and misses a turn\n"
showCardDesc Reverse = "Reverse: Order of play switches directions\n"
showCardDesc Wild = "Wild: Player declares next color(any color) to be matched\n"
showCardDesc WildDrawFour = "Wild draw four: Player declares next color to be matched; next player in sequence draws four cards and loses a turn.\n"
showCardDesc _ = "Regular Card: No effect!"
showFuncCard :: String
showFuncCard = showCardDesc Skip ++ showCardDesc DrawTwo ++ showCardDesc Reverse ++
showCardDesc Wild ++ showCardDesc WildDrawFour
getLineInt :: Int -> IO Int
getLineInt _max = do
putStr "> "
_numStr <- getLine
case readMaybe _numStr of
Just x -> if x < _max && x > 0 then
return x
else do
putStrLn "Too large number entered, please enter again:"
getLineInt _max
Nothing -> getHelp _numStr >> getLineInt _max