diff --git a/README.md b/README.md index 921b4d9..e08a9f4 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,13 @@ List of implemented features: - time delayed actions - fade on button click - bottom & up seatbolds +- detecting end of game - automove on timebank end - hightlighting of active player - calculation of possible actions +- autoskip when no action is required - handling bet rounds until showdown +- detecting winner(-s) and awarding them - detecting & comparing of poker combinations - *another not really cool stuff* @@ -31,13 +34,13 @@ List of implemented features: ## Status One phrase review: \ -*Almost done ... base version* +*Base version seemed to be done* -Nothing really complete here yet, -but you can watch the flop, do some moves and play to -your heart content with a slider. +Release candidate for base version, +you can play to your heart content versus yourself, +although some bugs are sneaking nearby, probably. -Developing ... +Developing AI ... ![serious coding](/docs/images/serious%20coding.gif) @@ -62,3 +65,5 @@ to verify results. For launching tests: `stack test` + +*Don't even try to run if you don't know what you're exactly doing!* diff --git a/img/text/loss.png b/img/text/loss.png new file mode 100644 index 0000000..252c039 Binary files /dev/null and b/img/text/loss.png differ diff --git a/img/text/win.png b/img/text/win.png new file mode 100644 index 0000000..3ff7730 Binary files /dev/null and b/img/text/win.png differ diff --git a/lambdem-poker.cabal b/lambdem-poker.cabal index fe9dd0c..692eb1b 100644 --- a/lambdem-poker.cabal +++ b/lambdem-poker.cabal @@ -1,5 +1,5 @@ name: lambdem-poker -version: 0.4 +version: 0.5 synopsis: Poker client -- description: homepage: https://github.com/cmc-haskell-2018/lambdem-poker diff --git a/src/Client.hs b/src/Client.hs index 5b32641..fc0fd7a 100644 --- a/src/Client.hs +++ b/src/Client.hs @@ -14,7 +14,7 @@ import Poker.Logic.Dealer import Poker.Logic.Trading import Poker.Logic.Types -import Debug.Trace +--import Debug.Trace ------------------------------------------------------------------------------- -- * Game launch related functions @@ -47,6 +47,7 @@ createTableScreenWith generator imgs = TableScreen , players = [Player Human " Hero" 1500 SB Bottom Nothing False False 0 (Move Waiting 0) 0, Player Human "Opponent" 1500 BB Top Nothing True False 0 (Move Waiting 0) 0] + , hero = " Hero" , street = Preflop , handCount = 1 , dealer = Bottom @@ -64,8 +65,12 @@ createTableScreenWith generator imgs = TableScreen -- | Update game parameters depending on game state. updateGame :: Float -> TableScreen -> TableScreen -updateGame timePassed screen - | state screen == Dealing_Hand = +updateGame timePassed screen + | state screen == Start_Hand = + if (checkGameEnd $ players screen) + then screen { state = Finish_Game } + else screen { state = Dealing_Hand } + | state screen == Dealing_Hand = if (timer screen < dealTime) then screen { timer = timer screen + timePassed } else screen @@ -87,8 +92,9 @@ updateGame timePassed screen | state screen == Start_Round = if (street screen == Showdown) then screen - { state = Finish_Hand - , timer = 0 + { state = Finish_Hand + , timer = 0 + , players = openHands $ players screen } else screen { state = Bet_Round @@ -98,7 +104,8 @@ updateGame timePassed screen , deck = snd $ snd boardDealResult } | state screen == Bet_Round = - if (checkSkipForActivePlayer $ players screen) + if (checkSkipForActivePlayer activePlayer maxBet $ + countCanMovePlayers (players screen)) then screen { state = Next_Move } else screen { state = case activePlayerType of @@ -137,7 +144,7 @@ updateGame timePassed screen then screen { state = Finish_Hand , timer = 0 - , players = applyMoveResults (players screen) + , players = openHands $ applyMoveResults (players screen) } else if (activePlayerPosition == lastPosition) then if (checkReTrade (players screen) maxBet) @@ -148,14 +155,14 @@ updateGame timePassed screen , street = succ $ street screen } else screen - { state = Bet_Round - , players = toggleNewActivePlayer (players screen) nextPosition - } + { state = Bet_Round + , players = toggleNewActivePlayer (players screen) nextPosition + } | state screen == Finish_Hand = if (timer screen < showdownTime) then screen { timer = timer screen + timePassed } else screen - { state = Dealing_Hand + { state = Start_Hand , timer = 0 , players = changePlayerPositions $ computeHandResults (players screen) (board screen) diff --git a/src/Poker/Interface/Handlers.hs b/src/Poker/Interface/Handlers.hs index bc9ee7c..6d4eb18 100644 --- a/src/Poker/Interface/Handlers.hs +++ b/src/Poker/Interface/Handlers.hs @@ -9,8 +9,6 @@ import Poker.Interface.Offsets import Poker.Logic.Trading import Poker.Logic.Types -import Debug.Trace - ------------------------------------------------------------------------------- -- * Handler functions ------------------------------------------------------------------------------- diff --git a/src/Poker/Interface/Loader.hs b/src/Poker/Interface/Loader.hs index f834b15..60cd2fc 100644 --- a/src/Poker/Interface/Loader.hs +++ b/src/Poker/Interface/Loader.hs @@ -15,6 +15,8 @@ import Poker.Logic.Types loadedTableImages :: IO TableImages loadedTableImages = do Just imgBackground <- loadJuicyPNG "img/background.png" + Just imgWin <- loadJuicyPNG "img/text/win.png" + Just imgLoss <- loadJuicyPNG "img/text/loss.png" Just imgTable <- loadJuicyPNG "img/table.png" Just imgSeatBold <- loadJuicyPNG "img/seatbold.png" Just imgSeatBoldActive <- loadJuicyPNG "img/seatbold active.png" @@ -32,6 +34,8 @@ loadedTableImages = do imgsChips <- loadChipLayout return TableImages { background = imgBackground + , win = imgWin + , loss = imgLoss , table = imgTable , seatBold = imgSeatBold , seatBoldActive = imgSeatBoldActive diff --git a/src/Poker/Interface/Offsets.hs b/src/Poker/Interface/Offsets.hs index 78ca519..982e62c 100644 --- a/src/Poker/Interface/Offsets.hs +++ b/src/Poker/Interface/Offsets.hs @@ -125,3 +125,7 @@ betWindowOffset = (buttonOffset * 1.5 - 33, -250) -- | Offset for test in bet window. betWindowTextOffset :: (Float, Float) betWindowTextOffset = (-20, -6) + +-- | Vertical offset for result message +resultMessageOffset :: Float +resultMessageOffset = 70 diff --git a/src/Poker/Interface/Renderer.hs b/src/Poker/Interface/Renderer.hs index 757ba71..352a5b8 100644 --- a/src/Poker/Interface/Renderer.hs +++ b/src/Poker/Interface/Renderer.hs @@ -11,8 +11,6 @@ import Poker.Interface.Offsets import Poker.Logic.Trading import Poker.Logic.Types -import Debug.Trace - ------------------------------------------------------------------------------- -- * Render functions ------------------------------------------------------------------------------- @@ -23,10 +21,12 @@ drawTableScreen screen | state screen == Dealing_Hand = pictures ([tableWithDealerChip] ++ map (\p -> playerOnSeatBold p) (players screen)) | state screen == Waiting_User_Input || - state screen == Show_Click = pictures [tableWithDealerChip, potWithBoard, playersHands, playersBets, + state screen == Show_Click = pictures [tableWithDealerChip, potWithBoard, playersHands, playersWihtBets, drawButtons possibleActions (button $ images screen, buttonClicked $ images screen) (buttonTexts $ images screen) (pressed activePlayer), sliderImage, smallButtons, betWindowImage] - | otherwise = pictures [tableWithDealerChip, potWithBoard, playersHands, playersBets] + | state screen == Finish_Game = pictures [background $ images screen, table $ images screen, + playersWihtBets, resultMessage] + | otherwise = pictures [tableWithDealerChip, potWithBoard, playersHands, playersWihtBets] where chipImages = (chipLayout $ images screen) playerOnSeatBold p = pictures [drawPlayerSeatBold p (case active p of @@ -41,7 +41,7 @@ drawTableScreen screen Bankrupted -> blank Folded -> blank _ -> pictures [drawPlayerHand p (deckLayout $ images screen)]) (players screen)) - playersBets = pictures (map (\p -> pictures [playerOnSeatBold p, drawPlayerBet p chipImages]) (players screen)) + playersWihtBets = pictures (map (\p -> pictures [playerOnSeatBold p, drawPlayerBet p chipImages]) (players screen)) activePlayer = getActivePlayer $ players screen maxBet = countMaxBet $ players screen possibleActions = getPossibleActions activePlayer maxBet @@ -55,6 +55,9 @@ drawTableScreen screen betWindowImage = case fst possibleActions /= All_In of True -> drawBetWindow (currentValue $ sliderData screen) (betWindow $ images screen) False -> blank + resultMessage = case checkWin (players screen) (hero screen) of + True -> translate 0 resultMessageOffset (win $ images screen) + False -> translate 0 resultMessageOffset (loss $ images screen) -- | Draw player seatbold. drawPlayerSeatBold :: Player -> Picture -> Picture diff --git a/src/Poker/Interface/Types.hs b/src/Poker/Interface/Types.hs index ed3c28e..63767ed 100644 --- a/src/Poker/Interface/Types.hs +++ b/src/Poker/Interface/Types.hs @@ -15,6 +15,7 @@ data TableScreen = TableScreen { state :: GameState -- ^ current game state , timer :: Float -- ^ for detecting time , players :: [Player] -- ^ info about every player + , hero :: String -- ^ name of hero , street :: Street -- ^ current street , handCount :: Int -- ^ current hand number , dealer :: Seat -- ^ position of dealer @@ -29,6 +30,8 @@ data TableScreen = TableScreen -- | Contain all images relative to table game screen. data TableImages = TableImages { background :: Picture + , win :: Picture + , loss :: Picture , table :: Picture , seatBold :: Picture , seatBoldActive :: Picture diff --git a/src/Poker/Logic/Calculations.hs b/src/Poker/Logic/Calculations.hs index bad85c6..40e50d0 100644 --- a/src/Poker/Logic/Calculations.hs +++ b/src/Poker/Logic/Calculations.hs @@ -5,7 +5,7 @@ import Data.List (sort) import Poker.Logic.Types -import Debug.Trace +--import Debug.Trace ------------------------------------------------------------------------------- -- * Functions to operate with cards @@ -85,7 +85,8 @@ computeCombination :: Maybe (Card, Card) -> [Card] -> Combination computeCombination handCards board = Combination { handRank = fst handRankComputations , structure = fst $ snd handRankComputations - , kicker = snd $ snd handRankComputations } + , kicker = snd $ snd handRankComputations + } where handRankComputations = computeHandRank allCards allCards = case handCards of @@ -144,3 +145,17 @@ countRanks cards = foldl (\ranks card -> addRank ranks $ fromEnum (cardRank card takeEqualBestN :: Int -> Int -> [Int] -> [Int] takeEqualBestN n num list = snd . unzip . take n $ reverse (filter (\x -> fst x == num) (zip list [0..12])) + +-- | Convert combination list to bool list with marked top combinations. +-- Receive combination list zipped with bool list that indicates if +-- the combination require to participate in comparing. +markWinningCombinations :: [(Bool, Combination)] -> [Bool] +markWinningCombinations participateAndCombinations = bitWinners + where + filteredWithIndexes = map (\((_,c),i) -> (c,i)) $ filter (\pAc -> fst $ fst pAc) + (zip participateAndCombinations [0..length participateAndCombinations]) + sorted = reverse $ sort filteredWithIndexes + winners = (head sorted:takeWhile (\(c, _) -> c == fst (head sorted)) (tail sorted)) + bitWinners = foldl (\bitmap index -> + fst (splitAt index bitmap) ++ [True] ++ tail (snd $ splitAt index bitmap)) + (replicate (length participateAndCombinations) False) (snd $ unzip winners) diff --git a/src/Poker/Logic/Dealer.hs b/src/Poker/Logic/Dealer.hs index d7f6ce8..1ec76b8 100644 --- a/src/Poker/Logic/Dealer.hs +++ b/src/Poker/Logic/Dealer.hs @@ -59,23 +59,26 @@ dealBoard randomizer deck board street -- * Operations with players ------------------------------------------------------------------------------- --- | Take blind from player. +-- | Take blind from player and mark bankrupted players. takeBlind :: Player -> Int -> Player takeBlind player blind | balance player == 0 = player + { move = Move Bankrupted 0 } | balance player <= blind = player { move = Move All_In_ed (balance player) } | otherwise = player - { move = Move Raised blind } + { move = Move Waiting blind } --- | Take blinds from players. +-- | Take blinds from players and mark bankrupted players. takeBlinds :: [Player] -> Int -> [Player] takeBlinds [] _ = [] takeBlinds (p:ps) bb = let takeBB player blind = case position player of SB -> takeBlind player (blind `div` 2) BB -> takeBlind player blind - _ -> player + _ -> if (balance player == 0) + then player { move = Move Bankrupted 0 } + else player in (takeBB p bb : takeBlinds ps bb) -- | Hide hand depending on player type and settings. @@ -86,6 +89,10 @@ hideHands players = map AI -> player { hideHand = hideAIhand }) players +-- | Open all hands. +openHands :: [Player] -> [Player] +openHands players = map (\player -> player { hideHand = False }) players + ------------------------------------------------------------------------------- -- * Constants ------------------------------------------------------------------------------- diff --git a/src/Poker/Logic/Trading.hs b/src/Poker/Logic/Trading.hs index 2fb7713..c5c948d 100644 --- a/src/Poker/Logic/Trading.hs +++ b/src/Poker/Logic/Trading.hs @@ -1,8 +1,9 @@ -- | Contains stuff to process bet rounds. module Poker.Logic.Trading where +import Poker.Logic.Calculations import Poker.Logic.Types - +import Debug.Trace ------------------------------------------------------------------------------- -- * Operations with positions ------------------------------------------------------------------------------- @@ -52,16 +53,16 @@ getSeatOfPosition pos players -- * Computations with player(-s) ------------------------------------------------------------------------------- --- | Check if active player is skippable. -checkSkipForActivePlayer :: [Player] -> Bool -checkSkipForActivePlayer [] = False -checkSkipForActivePlayer players - | active $ head players = case action . move $ head players of +-- | Check if active player is skippable depending on max bet and amount of +-- players in hand that aren't all-in. +checkSkipForActivePlayer :: Player -> Int -> Int -> Bool +checkSkipForActivePlayer player maxBet livePlayers = + case action $ move player of Bankrupted -> True Folded -> True All_In_ed -> True - _ -> False - | otherwise = checkSkipForActivePlayer $ tail players + Waiting -> (betSize $ move player) == maxBet && livePlayers == 1 + _ -> (betSize $ move player) == maxBet -- | Return amount of players left in hand. countInHandPlayers :: [Player] -> Int @@ -72,6 +73,16 @@ countInHandPlayers players = foldl1 (+) (map _ -> 1) players) +-- | Return amount of players in hand that can require move. +countCanMovePlayers :: [Player] -> Int +countCanMovePlayers players = foldl1 (+) (map + (\player -> case action $ move player of + Bankrupted -> 0 + Folded -> 0 + All_In_ed -> 0 + _ -> 1) + players) + -- | Return maximal bet that occured. countMaxBet :: [Player] -> Int countMaxBet players = maximum (map @@ -80,15 +91,28 @@ countMaxBet players = maximum (map -- | Return if repeating of trade is needed. checkReTrade :: [Player] -> Int -> Bool -checkReTrade players bet = or (map +checkReTrade players bet = any (\player -> - mv player /= Waiting && mv player /= Folded && - mv player /= All_In_ed && bt player /= bet) - players) + (mv player == Called || mv player == Raised || + mv player == Checked) && + bt player /= bet) + players where mv p = action $ move p bt p = betSize $ move p +-- | Return if game ended. +checkGameEnd :: [Player] -> Bool +checkGameEnd players = sum (map (\player -> + if (balance player == 0) + then 0 + else 1) players) == 1 + +-- | Return if player with given name won the game. +checkWin :: [Player] -> String -> Bool +checkWin players playerName = any (\player -> + name player == playerName && balance player /= 0) players + -- | Return default move to proposed bet size when human didn't made any input. autoHumanMove :: Player -> Int -> Move autoHumanMove player bet @@ -150,12 +174,12 @@ applyMoveResults :: [Player] -> [Player] applyMoveResults players = map (\player -> player { balance = balance player - bet player - , move = case action $ move player of - Bankrupted -> Move Bankrupted 0 - Folded -> Move Folded 0 - All_In_ed -> Move All_In_ed 0 - _ -> Move Waiting 0 , active = False + , move = case action $ move player of + Bankrupted -> Move Bankrupted 0 + Folded -> Move Folded 0 + All_In_ed -> Move All_In_ed 0 + _ -> Move Waiting 0 , invested = invested player + bet player }) players @@ -171,10 +195,51 @@ computeHandResults players board = Folded -> player _ -> player { balance = balance player + snd tookFromEach }) (fst tookFromEach) - else players + else if (notFinished) + then computeHandResults clearedPlayers board + else clearedPlayers + where + maxInvested = maximum (map (\player -> invested player) players) + tookFromEach = takePotFromPlayers players maxInvested + winResults = markWinnersActive players board + markedPlayers = snd winResults + winnersAmount = fst winResults + minWinning = minimum (map (\player -> invested player) $ + filter (\player -> active player) markedPlayers) + takeResults = takePotFromPlayers markedPlayers minWinning + takenPlayers = fst takeResults + winnersPot = snd takeResults + winnerAward = winnersPot `div` winnersAmount + leftPart = winnersPot - winnerAward * winnersAmount + awardedPlayers = giveLeftPart leftPart $ map (\player -> case active player of + True -> player { balance = balance player + winnerAward } + False -> player) takenPlayers + notFinished = any (\player -> invested player > 0) takenPlayers + clearedPlayers = map (\player -> player { active = False }) awardedPlayers + +-- | Give award to first active player. +giveLeftPart :: Int -> [Player] -> [Player] +giveLeftPart _ [] = [] +giveLeftPart award players + | active $ head players = + ((head players) { balance = balance (head players) + award } : tail players) + | otherwise = (head players : giveLeftPart award (tail players)) + +-- | Return amount of winners among player that invested something and mark them as active. +markWinnersActive :: [Player] -> [Card] -> (Int, [Player]) +markWinnersActive players board = (winnersAmount, markedPlayers) where - maxInvested = maximum (map (\player -> invested player) players) - tookFromEach = takePotFromPlayers players maxInvested + combinations = map (\player -> computeCombination (hand player) board) players + playersWithCombinations = zip players combinations + markedCombinations = + map (\(player, combination) -> (invested player > 0, combination)) playersWithCombinations + winningCombinations = markWinningCombinations markedCombinations + winnersAmount = foldl1 (+) $ map (\x-> case x of + True -> 1 + False -> 0) winningCombinations + markedPlayers = map (\(player, isWinner) -> case isWinner of + True -> player { active = True } + False -> player) $ zip players winningCombinations -- | Take from player part of invested sized in pot. takePotFromPlayer :: Player -> Int -> (Player, Int) @@ -220,7 +285,7 @@ writeButtonClick btn players -- | Time to get response from AI player. aiThinkTime :: Float -aiThinkTime = 2.0 +aiThinkTime = 1.0 -- | Time to get response from human player. humanThinkTime :: Float diff --git a/src/Poker/Logic/Types.hs b/src/Poker/Logic/Types.hs index afd73a9..7c93f7a 100644 --- a/src/Poker/Logic/Types.hs +++ b/src/Poker/Logic/Types.hs @@ -13,15 +13,17 @@ import Data.List -- | Possible game states. data GameState - = Dealing_Hand + = Start_Hand + | Dealing_Hand | Posting_Blinds + | Start_Round | Bet_Round | Show_Click | Waiting_User_Input | AI_Thinking | Next_Move - | Start_Round | Finish_Hand + | Finish_Game deriving (Eq) -- | Contain all personal player data. @@ -122,7 +124,7 @@ data Combination = Combination { handRank :: HandRank , structure :: [CardRank] -- ^ card ranks to indicate combination strength , kicker :: [CardRank] -- ^ kicker card ranks - } deriving (Eq, Ord) + } deriving (Eq, Ord, Show) -- | Hand ranks. data HandRank diff --git a/test/Test.hs b/test/Test.hs index 0ff3508..d89108b 100644 --- a/test/Test.hs +++ b/test/Test.hs @@ -3,38 +3,40 @@ import Poker.Logic.Types main :: IO () main = do - putStrLn ("\n 1: " ++ show (computeHandRank hand00r)) - putStrLn ("\n 2: " ++ show (computeHandRank hand00)) - putStrLn ("\n 3: " ++ show (computeHandRank hand01)) - putStrLn ("\n 4: " ++ show (computeHandRank hand02)) - putStrLn ("\n 5: " ++ show (computeHandRank hand03)) - putStrLn ("\n 6: " ++ show (computeHandRank hand04)) - putStrLn ("\n 7: " ++ show (computeHandRank hand05)) - putStrLn ("\n 8: " ++ show (computeHandRank hand06)) - putStrLn ("\n 9: " ++ show (computeHandRank hand07)) - putStrLn ("\n 10: " ++ show (computeHandRank hand08)) - putStrLn ("\n 11: " ++ show (computeHandRank hand09)) - putStrLn ("\n 12: " ++ show (computeHandRank hand10)) - putStrLn ("\n 13: " ++ show (computeHandRank hand11)) - putStrLn ("\n 14: " ++ show (computeHandRank hand12)) - putStrLn ("\n 14: " ++ show (computeHandRank hand13)) - putStrLn ("\n" ++ show (getCombination hand00r > getCombination hand00)) - putStrLn ("\n" ++ show (getCombination hand00 > getCombination hand01)) - putStrLn ("\n" ++ show (getCombination hand02 > getCombination hand03)) - putStrLn ("\n" ++ show (getCombination hand03 > getCombination hand04)) - putStrLn ("\n" ++ show (getCombination hand05 > getCombination hand06)) - putStrLn ("\n" ++ show (getCombination hand07 > getCombination hand07c)) - putStrLn ("\n" ++ show (getCombination hand10 > getCombination hand12)) - putStrLn ("\n" ++ show (getCombination hand12 < getCombination hand12c)) - putStrLn ("\n" ++ show (getCombination hand09 > getCombination hand09c)) - putStrLn ("\n" ++ show (getCombination hand08 > getCombination hand08c)) +-- putStrLn ("\n 1: " ++ show (computeHandRank hand00r)) +-- putStrLn ("\n 2: " ++ show (computeHandRank hand00)) +-- putStrLn ("\n 3: " ++ show (computeHandRank hand01)) +-- putStrLn ("\n 4: " ++ show (computeHandRank hand02)) +-- putStrLn ("\n 5: " ++ show (computeHandRank hand03)) +-- putStrLn ("\n 6: " ++ show (computeHandRank hand04)) +-- putStrLn ("\n 7: " ++ show (computeHandRank hand05)) +-- putStrLn ("\n 8: " ++ show (computeHandRank hand06)) +-- putStrLn ("\n 9: " ++ show (computeHandRank hand07)) +-- putStrLn ("\n 10: " ++ show (computeHandRank hand08)) +-- putStrLn ("\n 11: " ++ show (computeHandRank hand09)) +-- putStrLn ("\n 12: " ++ show (computeHandRank hand10)) +-- putStrLn ("\n 13: " ++ show (computeHandRank hand11)) +-- putStrLn ("\n 14: " ++ show (computeHandRank hand12)) +-- putStrLn ("\n 14: " ++ show (computeHandRank hand13)) +-- putStrLn ("\n" ++ show (getCombination hand00r > getCombination hand00)) +-- putStrLn ("\n" ++ show (getCombination hand00 > getCombination hand01)) +-- putStrLn ("\n" ++ show (getCombination hand02 > getCombination hand03)) +-- putStrLn ("\n" ++ show (getCombination hand03 > getCombination hand04)) +-- putStrLn ("\n" ++ show (getCombination hand05 > getCombination hand06)) +-- putStrLn ("\n" ++ show (getCombination hand07 > getCombination hand07c)) +-- putStrLn ("\n" ++ show (getCombination hand10 > getCombination hand12)) +-- putStrLn ("\n" ++ show (getCombination hand12 < getCombination hand12c)) +-- putStrLn ("\n" ++ show (getCombination hand09 > getCombination hand09c)) +-- putStrLn ("\n" ++ show (getCombination hand08 > getCombination hand08c)) + putStrLn ("\n" ++ show (markWinningCombinations markedCombinations)) where handRankComputations cards = computeHandRank cards getCombination cards = Combination { handRank = fst $ handRankComputations cards , structure = fst . snd $ handRankComputations cards , kicker = snd . snd $ handRankComputations cards } - + combinations = map getCombination [hand00, hand01, hand02, hand05, hand00r, hand06] + markedCombinations = zip (replicate (length combinations) True) combinations -- | Test cases for combinations.