0

This is the code I have so far:

data Suit = Diamond | Club | Heart | Spade
  deriving (Read, Enum, Eq, Bounded)

data Rank = Two | Three | Four
     | Five | Six | Seven | Eight | Nine | Ten 
     | Jack | Queen | King | Ace
  deriving (Read, Enum, Eq, Ord, Bounded)

and I am trying to map each value, either Rank or Suit to a unique prime number.

primeMapper :: Either Rank Suit -> Int

should be the final function and I want to iterate over each Suit and set it to the first four primes:

primeMapper [Diamond .. Spade] = [2,3,5,7]

and each Rank equal to the rest of the primes up until the 17th:

primeMapper [Two .. Ace] = drop 4 . take 17 $ primes

assuming I have a generating function called primes.

This code, however throws errors obviously because it generates a list from a list. How can I achieve what I am trying to do? Let me know if I can explain it better.

The ultimate goal is to have a hash table that gives unique IDs to each cards based on prime factors, and then generate prime factorization and use modulo to quickly compare poker hands.

  • Do you have a good resource (a book) for learning haskell yet? You'll want to get up to speed on pattern matching. Something to get you started: `primeMapper (Left Two) = undefined ; primeMapper (Right Diamond) = undefined ; ...etc` . Also read up on the Enum class – jberryman Nov 30 '18 at 00:31

4 Answers4

2

Ultimately I solved what I am trying to do by hand as so:

primeMapper :: Either Suit Rank -> Int
primeMapper x = case x of
        Left Diamond -> 2
        Left Club -> 3
        Left Heart -> 5
        Left Spade -> 7
        Right Two -> 11
        Right Three -> 13
        Right Four -> 17
        Right Five -> 19
        Right Six -> 23
        Right Seven -> 29
        Right Eight -> 31
        Right Nine -> 37
        Right Ten -> 41
        Right Jack -> 43
        Right Queen -> 47
        Right King -> 53
        Right Ace -> 59

... was there a more concise way to do this rather than write each case out by hand?

  • More concise way: `[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59] !! case x of Left s -> fromEnum s; Right r -> 4 + fromEnum r`. – Daniel Wagner Nov 30 '18 at 13:34
  • Even more concise: `([2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59] !!) . either fromEnum ((+4) . fromEnum)` (although now we're pushing the limits of readability). – chepner Nov 30 '18 at 14:35
2

Your solution using pattern matching is best, though I would prefer

primeMapper :: Either Suit Rank -> Int
primeMapper (Left Diamond) = 2
primeMapper (Left Club)    = 3
...

rather than your long case expression.

However you could also use lookup :: Eq a => a -> [(a, b)] -> Maybe b

import Data.Maybe (fromJust)

primeMapper :: Either Suit Rank -> Int
primeMapper = fromJust . flip lookup zippedPrimes
  where
  zippedPrimes = zip suitranks primes
  suitranks    = fmap Left suits ++ fmap Right ranks :: [Either Suit Rank]
  suits        = fromEnum minBound
  ranks        = fromEnum minBound
Adam Smith
  • 52,157
  • 12
  • 73
  • 112
1

Depending on what you plan to use this for, you may not need to use primes or prime factorizations at all; you can get fast conversion to and from plain numbers just by picking one or the other of the suit or rank for a base conversion. Here I'll pick suit -- there are four suits, so take the first digit in base 4 as the suit and the remaining digits as the rank.

encode :: (Suit, Rank) -> Int
encode (s, r) = fromEnum s + 4 * fromEnum r

decode :: Int -> (Suit, Rank)
decode n = (toEnum s, toEnum r) where (r, s) = n `quotRem` 4

You can verify in ghci that this gives a unique number to each card:

> [encode (s, r) | r <- [minBound .. maxBound], s <- [minBound .. maxBound]]
[0,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]

And that they decode appropriately:

> map decode [0..51] == [(s, r) | r <- [minBound .. maxBound], s <- [minBound .. maxBound]]
True
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
0

You can adapt some of this I hope.

no2s3s5s = \n -> take n $ scanl (\b a -> a+b) 11 $ cycle [2,4,2,4,6,2,6,4]
rnk = filter (/=49) $ no2s3s5s 14  
stv = [2,3,5,7]
_deck = [ b*v | b <- stv, v <- rnk]
_Dia = take 13.drop (0*13) $ _deck
_Clb = take 13.drop (1*13) $ _deck
_Hrt = take 13.drop (2*13) $ _deck
_Spd = take 13.drop (3*13) $ _deck

_Dia

[22,26,34,38,46,58,62,74,82,86,94,106,118]

_Clb

[33,39,51,57,69,87,93,111,123,129,141,159,177]

_Hrt

[55,65,85,95,115,145,155,185,205,215,235,265,295]

_Spd

[77,91,119,133,161,203,217,259,287,301,329,371,413]

_deck

[22,26,34,38,46,58,62,74,82,86,94,106,118,33,39,51,57,69,87,93,111,123,129,141,159,177,55,65,85,95,115,145,155,185,205,215,235,265,295,77,91,119,133,161,203,217,259,287,301,329,371,413]

length _deck

52

Your multiples (_deck) are all unique. I normally use no2s3s5s with a computed (limited) factor list and the mod function to generate a long prime list.

fp_mora
  • 718
  • 6
  • 11