-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathBowling.hs
143 lines (116 loc) · 5.4 KB
/
Bowling.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
module Bowling where
import Data.List (tails)
import Data.Either
import Test.HUnit
type Throw = Int
type Score = Int
type ErrorMsg = String
data Frame = Frame Throw (Maybe Throw)
deriving (Show,Eq)
data FrameResult = Regular | Spare | Strike
deriving (Show,Eq)
type Game = [Frame]
totalPins = 10
bonus = 10
showErrorMsg = putStrLn
strike = Frame 10 Nothing
result :: Frame -> Either ErrorMsg FrameResult
result (Frame 10 Nothing)= Right Strike
result (Frame x (Just y))
| knocked == totalPins = Right Spare
| knocked >= 0 && knocked < totalPins = Right Regular
where knocked = x + y
result frame = Left $ "Frame invalid: " ++ show frame
frameScore :: Frame -> [Frame] -> Either ErrorMsg Score
frameScore frame bonusFrames =
either Left calcScore $ result frame
where
bonusThrows = throws bonusFrames
calcScore res =
case (res,frame,bonusThrows) of
(Regular,Frame x (Just y), _) -> Right $ x+y
(Spare,_,(x:_)) -> Right $ bonus + x
(Strike,_,(x:y:_)) -> Right $ bonus + x + y
_ -> Left $ "Input invalid. Frame: " ++ show frame ++
". Bonus: " ++ show bonusThrows ++ "."
throws :: [Frame] -> [Throw]
throws [] = []
throws (Frame x (Just y) : xs) = x:y:throws xs
throws (Frame x Nothing : xs) = x:throws xs
numFramesGame=10
gameScore :: Game -> Either [ErrorMsg] Score
gameScore game =
let frs= take numFramesGame $ tails game
score (h:t)= frameScore h t
score [] = Left "Lista de turnos insuficiente"
scores = map score frs
in case partitionEithers scores of
([],scores) -> Right $ sum scores
(errors,_) -> Left errors
testResult = TestCase $ do
assertEqual
"Devuelve que un turno ha sido un strike si en el primer (y unico) lanzamiento se derriban todos los bolos"
(result $ Frame totalPins Nothing) (Right Strike)
assertEqual
"El turno sera un spare si en los dos lanzamientos se derriban todos los bolos"
(result $ Frame (div totalPins 2) $ Just (div totalPins 2))
(Right Spare)
assertEqual
"Si en los dos lanzamientos no se derriban todos el turno sera normal"
(result $ Frame 0 $ Just 0) (Right Regular)
assertEqual
"Si la representacion del turno no es correcta devuelve un mensaje de error"
(map ((either (take 13) undefined).result)
[Frame 5 Nothing,Frame 5 $ Just 6,
Frame 11 Nothing])
(replicate 3 "Frame invalid")
testFrameScore = TestCase $ do
assertEqual
"Si el lanzamiento es normal, el resultado del turno sera la suma de los bolos derribados en ese turno"
(frameScore (Frame 5 $ Just 4) [undefined]) (Right 9)
assertEqual
"Si el Lanzamiento es un spare el resultado es la suma del bonus mas los bolo derribados en el siguiente lanzamiento"
(frameScore (Frame 5 $ Just 5) [Frame 10 Nothing]) (Right 20)
assertEqual
"Si el lanzamiento es un strike el resultado es la suma del bonus mas los bolos derribados en los dos lanzamientos siguientes"
(map (frameScore (Frame 10 Nothing))
[[Frame 5 $ Just 4],
[Frame 10 Nothing, Frame 5 $ Just 5]])
([Right 19,Right 25])
assertEqual
"Si el tipo de resultado del turno necesita los lanzamientos para calcular la puntuacion y no se proporcionan, devuelve un mensaje de error"
(frameScore (Frame 10 Nothing) [])
(Left "Input invalid. Frame: Frame 10 Nothing. Bonus: [].")
testThrows = TestCase $
assertEqual
"Devuelve los lanzamientos (bolos derribados) de los turnos"
(map throws
[[Frame 10 Nothing, Frame 5 $ Just 5],
[Frame 5 $ Just 5, Frame 0 $ Just 0]])
([[10,5,5],[5,5,0,0]])
testGameScore =
let game 1 = take 12 $ repeat strike
game 2 = take 11 $ repeat $ Frame 5 $ Just 5
game 3 = take 10 $ repeat $ Frame 9 $ Just 0
game 4 = take 10 $ repeat $ Frame 1 $ Just 1
game 5 = replicate 3 strike ++ (replicate 7 $ Frame 0 $ Just 0)
game 6 = [strike,Frame 7 $ Just 3,Frame 9 $ Just 0,strike,
Frame 0 $ Just 8, Frame 8 $ Just 2, Frame 0 $ Just 6,
strike,strike,Frame 7 $ Just 2]
game 7 = replicate 9 (Frame 0 $ Just 0) ++ [strike,Frame 2 $ Just 3]
game 8 = replicate 8 (Frame 0 $ Just 0) ++ [strike,Frame 2 $ Just 3]
in TestCase $ do
assertEqual
"Devuelve el resultado del juego completo segun las reglas"
(map gameScore $ map game $ [1..8])
([Right 300, Right 150,Right 90,Right 20,
Right 60,Right 145,Right 15,Right 20])
assertEqual
"Si la lista de turnos es menor que el numero de turnos por juego devuelve un error especifico."
(map ((either last undefined).gameScore) [[],[strike],replicate 9 strike])
(replicate 3 "Lista de turnos insuficiente")
assertEqual
"Caso de que los turnos esten incorrectamente representados la funcion devolvera una lista con un error por cada uno de ellos"
(gameScore $ [Frame 5 Nothing, Frame 6 $ Just 5, Frame 11 Nothing, Frame (-1) Nothing] ++ replicate 5 (Frame 0 $ Just 0) ++ [Frame 10 Nothing])
(Left ["Frame invalid: Frame 5 Nothing","Frame invalid: Frame 6 (Just 5)","Frame invalid: Frame 11 Nothing","Frame invalid: Frame (-1) Nothing","Input invalid. Frame: Frame 10 Nothing. Bonus: []."])
main= runTestTT $ TestList [testResult,testFrameScore,testThrows,testGameScore]