Home > front end >  How can I fix my haskell code to count the first person points in rock paper scissors?
How can I fix my haskell code to count the first person points in rock paper scissors?

Time:04-29

I would like to count how many times the first player win a game. Can anybody help me to fix it ?

data RPS = Rock | Paper | Scissors deriving (Eq) 

beats :: RPS -> RPS
beats rps = case rps of 
    Rock -> Scissors
    Paper -> Rock
    Scissors -> Paper


firstBeats :: [RPS] -> [RPS] -> Int
firstBeats (a:ax) (b:bx)  
 | a==Rock && b==Scissors = 1   (firstBeats ax) : (firstBeats bx)
 | a==Scissors && b==Paper = 1   (firstBeats ax) : (firstBeats bx)
 | a==Paper && b==Rock = 1  (firstBeats ax) : (firstBeats bx)
 | otherwise = (a: firstBeats ax)   (b : firstBeats bx)

Examples:
firstBeats [Rock] [Paper]    == 0
firstBeats [Rock] [Scissors] == 1
firstBeats [Paper, Scissors] [Rock, Paper] == 2
firstBeats [Paper, Scissors, Paper] [Rock, Paper, Scissors] == 2
firstBeats (replicate 20 Paper) (replicate 20 Rock) == 20

CodePudding user response:

The main issue is that firstBeats takes two parameters, so you should pass the tails of the two lists. Furthermore you should cover the case where one of the two lists, or both are empty, so:

firstBeats :: [RPS] -> [RPS] -> Int
firstBeats (a:ax) (b:bx)  
 | a == Rock && b == Scissors = 1   firstBeats ax bx
 | a == Scissors && b == Paper = 1   firstBeats ax bx
 | a == Paper && b == Rock = 1   firstBeats ax bx
 | otherwise = firstBeats ax bx
firstBeats _ _ = 0

But this is not elegant and requires a lot of repetition. You can define a function that checks if the first item has beaten the second with:

hasBeaten :: RPS -> RPS -> Bool
hasBeaten Rock Scissors = True
hasBeaten Scissors Paper = True
hasBeaten Paper Rock = True
hasBeaten _ _ = False

Then you can just implement this as:

firstBeats :: [RPS] -> [RPS] -> Int
firstBeats (a:as) (b:bs)
    | hasBeaten a b = 1   firstBeats ax bx
    | otherwise = firstBeats ax bx
firstBeats _ _ = 0

But the modeling also is not very nice. One would expect to work with a list of 2-tuples of RPSs, such that it is impossible to pass two lists whith a different number of items. Then it is:

firstBeats :: [(RPS, RPS)] -> Int
firstBeats = length . filter (uncurry hasBeaten)
  • Related