Task

Create a program to parse a single five card poker hand and rank it according to this list of poker hands.



A poker hand is specified as a space separated list of five playing cards.

Each input card has two characters indicating face and suit.



Example

2d       (two of diamonds).


Faces are:    a, 2, 3, 4, 5, 6, 7, 8, 9, 10, j, q, k

Suits are:    h (hearts),   d (diamonds),   c (clubs),   and   s (spades),   or

alternatively,   the unicode card-suit characters:     ♥ ♦ ♣ ♠ 



Duplicate cards are illegal.

The program should analyze a single hand and produce one of the following outputs:

 straight-flush
 four-of-a-kind
 full-house
 flush
 straight
 three-of-a-kind
 two-pair
 one-pair
 high-card
 invalid



Examples

   2♥ 2♦ 2♣ k♣ q♦:   three-of-a-kind
   2♥ 5♥ 7♦ 8♣ 9♠:   high-card
   a♥ 2♦ 3♣ 4♣ 5♦:   straight
   2♥ 3♥ 2♦ 3♣ 3♦:   full-house
   2♥ 7♥ 2♦ 3♣ 3♦:   two-pair
   2♥ 7♥ 7♦ 7♣ 7♠:   four-of-a-kind 
   10♥ j♥ q♥ k♥ a♥:  straight-flush
   4♥ 4♠ k♠ 5♦ 10♠:  one-pair
   q♣ 10♣ 7♣ 6♣ q♣:  invalid

The programs output for the above examples should be displayed here on this page.



Extra credit

 use the playing card characters introduced with Unicode 6.0 (U+1F0A1 - U+1F0DE).
 allow two jokers
 use the symbol   joker
 duplicates would be allowed (for jokers only)
 five-of-a-kind would then be the highest hand


More extra credit examples

   joker  2♦  2♠  k♠  q♦:     three-of-a-kind
   joker  5♥  7♦  8♠  9♦:     straight
   joker  2♦  3♠  4♠  5♠:     straight
   joker  3♥  2♦  3♠  3♦:     four-of-a-kind
   joker  7♥  2♦  3♠  3♦:     three-of-a-kind
   joker  7♥  7♦  7♠  7♣:     five-of-a-kind
   joker  j♥  q♥  k♥  A♥:     straight-flush
   joker  4♣  k♣  5♦ 10♠:     one-pair
   joker  k♣  7♣  6♣  4♣:     flush
   joker  2♦  joker  4♠  5♠:  straight
   joker  Q♦  joker  A♠ 10♠:  straight
   joker  Q♦  joker  A♦ 10♦:  straight-flush
   joker  2♦  2♠  joker  q♦:  four-of-a-kind



Related tasks

 Playing cards
 Card shuffles
 Deal cards_for_FreeCell
 War Card_Game
 Go Fish


 
local
val rec ins = fn x : int*'a => fn [] => [x]
                  |  ll as h::t      => if #1 x<= (#1 h) then x::ll else h::ins x t
val rec acount = fn 
 (n,a::(b::t)) =>  if a=b then acount (n+1,b::t) else (n,a)::acount(1,b::t) 
   |  (n,[t])  =>  [(n,t)]
in                                                                             (* helper count and sort functions *)
  val rec sortBy1st = fn [] => [] | h::t => ins h (sortBy1st t)
  val addcount      = fn ll => acount (1,ll)
end;
 
 
 
val showHand = fn input =>
 let
 exception Cheat of string 
                                                                              (* replace a j q k by their numbers *)
 val translateCardstrings = fn inputstr =>
     String.tokens (fn #" "=>true|_=>false) (String.translate (fn #"a"=>"1"| #"j"=>"11"| #"q"=>"12"| #"k"=>"13" | a=>str a ) inputstr )
 
                                                                 (* parse numbers and characters into int*strings and order those *)
 val parseFacesSuits =  fn cardcodes =>
  sortBy1st (List.map (fn el => (valOf (Int.fromString el ),String.extract (el,String.size el -1,NONE) )) cardcodes )
     handle Option => raise Cheat "parse"
 
                                                                (* replace the list of face numbers by a list of every face with its count and order it / descending count *)
 val countAndSort =fn li =>
   let   val hand = ListPair.unzip li   in  (rev (sortBy1st (addcount (#1 hand))) , #2 hand ) end;
 
 
 val score = fn
   ( (4,_)::t , _ )        => "four-of-a-kind"
 | ( (3,_)::(2,_)::t , _ ) => "full-house"
 | ( (3,_)::t,_)           => "three-of-a-kind"
 | ( (2,_)::(2,_)::t,_)    => "two-pair"
 | ( (2,_)::t,_)           => "one-pair"
 | (x as (1,_)::t,ll)      => if  #2 (hd x ) - (#2 (hd (rev x))) =4 orelse ( #2 (hd (rev x))  = 1 andalso #2 (hd (tl (rev x))) =10 )
                                 then
                                    if  List.all (fn x => hd ll=x) ll
			              then "straight-flush"
			              else "straight"
			         else if  List.all (fn x => hd ll=x) ll then "flush" else  "high-card" 
 | _                       => "invalid"
 
 
 
                                                                      (* return 'invalid' if any duplicates or invalid codes *)
 val validate = fn lpair : (int * string) list =>
   let val rec uniq = fn ([],y) =>true|(x,y) => List.filter (fn a=>a= hd x) y = [hd x] andalso uniq(tl x,y) 
   in
    if         List.all (fn x :int*string  =>  #1 x > 0 andalso  #1 x < 14 )  lpair
      andalso  List.all (fn (x) => Option.isSome ( List.find (fn a=> a= #2x) ["c","d","h","s"] ) )  lpair
      andalso  uniq (lpair ,lpair) 
    then lpair
    else raise Cheat "value"
   end
 
in
 
 
   ( score o countAndSort  o  validate  o  parseFacesSuits  o  translateCardstrings )  input      handle Cheat ch => "invalid"
 
end;
 