Skip to content

Instantly share code, notes, and snippets.

@ygrenzinger
Created August 14, 2019 23:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ygrenzinger/d4f30eb9c4139abfccd65606c5bd3f0e to your computer and use it in GitHub Desktop.
Save ygrenzinger/d4f30eb9c4139abfccd65606c5bd3f0e to your computer and use it in GitHub Desktop.
Bowling Kata in Haskell
module ScoreCalculator ( bowlingScore ) where
import Data.Char
data Roll = Strike | Spare | PinHit Int deriving Eq
parseRawRolls :: String -> [Roll] -> [Roll]
parseRawRolls [] rolls = reverse rolls
parseRawRolls (' ' : rest) rolls = parseRawRolls rest rolls
parseRawRolls ('-':rest) rolls = parseRawRolls rest ((PinHit 0) : rolls)
parseRawRolls ('/':rest) rolls = parseRawRolls rest (Spare : rolls)
parseRawRolls ('X':rest) rolls = parseRawRolls rest (Strike : rolls)
parseRawRolls (r:rest) rolls = parseRawRolls rest (PinHit (digitToInt r) : rolls)
computeSpareScore :: [Roll] -> Int
computeSpareScore ((PinHit x) : _) = 10 + x
computeSpareScore (Strike : _) = 10 + 10
computeSpareScore _ = 10
computeStrikeScore :: [Roll] -> Int
computeStrikeScore (Strike : (Strike : _)) = 10 + 10 + 10
computeStrikeScore (Strike : ((PinHit x) : _)) = 10 + 10 + x
computeStrikeScore ( _ : Spare : _) = 10 + 10
computeStrikeScore ((PinHit first) : (PinHit second): _) = 10 + first + second
computeStrikeScore _ = 10
computeScore :: Int -> Int -> [Roll] -> Int
computeScore _ score [] = score
computeScore 10 score _ = score
computeScore frameCount score (Strike:nextRolls) = computeScore (frameCount + 1) (score + computeStrikeScore nextRolls) nextRolls
computeScore frameCount score (_:Spare:nextRolls) = computeScore (frameCount + 1) (score + computeSpareScore nextRolls) nextRolls
computeScore frameCount score ((PinHit a):(PinHit b):nextRolls) = computeScore (frameCount + 1) (score + a + b) nextRolls
bowlingScore :: String -> Int
bowlingScore rolls = computeScore 0 0 (parseRawRolls rolls [])
import Test.Hspec
import ScoreCalculator
main :: IO ()
main = hspec $ do
describe "Bowling Score calculator" $ do
it "should compute 0 when all rolls are a miss" $ do
bowlingScore "-- -- -- -- -- -- -- -- -- --" `shouldBe` 0
it "should compute 90 when always doing a first roll with 9 pins down" $ do
bowlingScore "9- 9- 9- 9- 9- 9- 9- 9- 9- 9-" `shouldBe` 90
it "should compute 20 when all roll with 1 pin down" $ do
bowlingScore "11 11 11 11 11 11 11 11 11 11" `shouldBe` 20
it "should compute 20 when first roll is a spare followed by a strike" $ do
bowlingScore "7/ X -- -- -- -- -- -- -- --" `shouldBe` 30
it "should compute 12 when first roll is a spare followed by a normal hit and a miss" $ do
bowlingScore "7/ 1- -- -- -- -- -- -- -- --" `shouldBe` 12
it "should compute 12 when first roll is a spare" $ do
bowlingScore "7/ 1- -- -- -- -- -- -- -- --" `shouldBe` 12
it "should compute 16 when first roll is a strike" $ do
bowlingScore "X 12 -- -- -- -- -- -- -- --" `shouldBe` 16
it "should compute 30 when first roll is a strike followed by a spare" $ do
bowlingScore "X 1/ -- -- -- -- -- -- -- --" `shouldBe` 30
it "should compute 60 when first three rolls are a strike" $ do
bowlingScore "X X X -- -- -- -- -- -- --" `shouldBe` 60
it "should compute 60 when first three rolls are a strike" $ do
bowlingScore "X X X -- -- -- -- -- -- --" `shouldBe` 60
it "should compute 300 when all rolls are a strike" $ do
bowlingScore "X X X X X X X X X X X X" `shouldBe` 300
it "should compute 300 when all frames are a spare" $ do
bowlingScore "5/ 5/ 5/ 5/ 5/ 5/ 5/ 5/ 5/ 5/5" `shouldBe` 150
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment