#!/usr/bin/perl

$poker_hands = choose(52,5);

# display_poker ( $title, $count )
#    Print a description of the probability of a hand (described
#    by $title) that can occur in $count different ways.
sub display_poker {
    my ($desc, $ways) = @_;
    my $prob = $ways*100/$poker_hands;
    sprintf "%15d %7.4f%% %s\n", $ways, $prob, $desc;
}

# display_poker_many ( $title, $count [, $title, $count]... )
#    Print a sequence of hand probabilities.
sub display_poker_many {
    my ( %list ) = @_;
    my ( $key );

    foreach $key (sort {$list{$a} <=> $list{$b}} keys( %list ) ) {
        print display_poker( $key, $list{$key} );
    }
}

display_poker_many(
  "one pair"        => choose(13,1) * choose(4,2) * choose(12,3) *
                           (choose(4,1)**3),
  "two pairs"       => choose(13,2) * choose(4,2)**2 * choose(11,1) *
                           choose(4,1),
  "three of a kind" => choose(13,1) * choose(4,3) * choose(12,2) *
                           choose(4,1)**2,
  "full house"      => choose(13,1) * choose(4,3) * choose(12,1) *
                           choose(4,2),
  "four of a kind"  => choose(13,1) * choose(4,4) * choose(12,1) *
                           choose(4,1),
  "any straight"    => $any_str    = ( choose(10,1) * choose(4,1)**5 ),
  "any flush"       => $any_flush  = ( choose(4,1)  * choose(13,5)   ),
  "straight flush"  => $str_flush  = ( choose(10,1) * choose(4,1)    ),
  "royal flush"     => choose(4,1),
  "only straight"   => $only_str   = $any_str   - $str_flush,
  "only flush"      => $only_flush = $any_flush - $str_flush,
  "bust"            => choose(13,5) * choose(4,1)**5 - $only_str
                           - $only_flush - $str_flush
);

# choose($n, $k) is the number of ways to choose $k elements from a set
# of $n elements, when the order of selection is irrelevant.
#
sub choose {
    my ($n, $k) = @_;
    my ($result, $j) = (1, 1);

    return 0 if $k > $n || $k < 0;
    $k = ($n - $k) if ($n - $k) < $k;

    while ( $j <= $k ) {
        $result *= $n--;
        $result /= $j++;
    }
    return $result;
}

