module Data.TableTennis ( Playable , Player (..) , Team (..) , GameLength (..) , Game (..) , Side (..) , newSinglesGame , newDoublesGame , winPoint , serviceLength , valueOf , serviceToChange , winner , changeService , swapSides ) where class Playable a where rotateSide :: a -> a data Player = Player { name :: String , handicap :: Int } deriving (Show) data Team = Team { topPlayer :: Player , bottomPlayer :: Player } deriving (Show) instance Playable Player where rotateSide a = a instance Playable Team where rotateSide (Team pTop pBottom) = Team pBottom pTop data GameLength = Eleven | TwentyOne deriving (Show) data Game a = Game { leftSide :: a , rightSide :: a , leftScore :: Int , rightScore :: Int , gameLength :: GameLength , serving :: Side } deriving (Show) data Side = LeftSide | RightSide deriving (Show) -- |Add a point to the specified Side's score winPoint :: Playable a => Game a -> Side -> Game a winPoint (Game l r ls rs gl s) LeftSide = Game l r (ls + 1) rs gl s winPoint (Game l r ls rs gl s) RightSide = Game l r ls (rs + 1) gl s -- |New SinglesGame with specified Players/GameLength/service Side, scores = 0 -- with the scores set to 0 newSinglesGame :: Player -> Player -> GameLength -> Side -> Game Player newSinglesGame leftPlayer rightPlayer gameLen side = Game leftPlayer rightPlayer 0 0 gameLen side -- |A new DoublesGame with specified Teams/GameLength/serving Side -- with the scores set to 0 newDoublesGame :: Team -> Team -> GameLength -> Side -> Game Team newDoublesGame leftTeam rightTeam gameLen side = Game leftTeam rightTeam 0 0 gameLen side -- |Convert GameLength to numerical representation valueOf :: GameLength -> Int valueOf Eleven = 11 valueOf TwentyOne = 21 -- |How many serves each player gets according to GameLength serviceLength :: GameLength -> Int serviceLength Eleven = 3 serviceLength TwentyOne = 5 -- |True if it is another Player/Team's turn to serve serviceToChange :: Playable a => Game a -> Bool serviceToChange g@(Game _ _ lScore rScore gameLen _) | ((lScore + rScore) > (valueOf gameLen)) == True = True | (lScore + rScore) `rem` (serviceLength gameLen) == 0 = True | otherwise = False -- |The winner of a Game or Nothing if nobody has won winner :: Playable a => Game a -> Maybe a winner (Game leftSide rightSide lScore rScore gameLen _) | (lScore - rScore) > 1 && lScore >= (valueOf gameLen) = Just leftSide | (rScore - lScore) > 1 && rScore >= (valueOf gameLen) = Just rightSide | otherwise = Nothing where totalScore = lScore + rScore -- |Makes the receiver become the server, rotates the serving side -- Precondition: we have already determined it is time to change service changeService :: Playable a => Game a -> Game a changeService g@(Game lSide rSide lScore rScore gameLen LeftSide) = Game (rotateSide lSide) rSide lScore rScore gameLen RightSide changeService g@(Game lSide rSide lScore rScore gameLen RightSide) = Game lSide (rotateSide rSide) lScore rScore gameLen LeftSide -- |Exchange sides of the table (typically after a Game is won) swapSides :: Playable a => Game a -> Game a swapSides (Game left right lS rS gameLen side) = Game right left lS rS gameLen side