Score a Cribbage Hand This challenge is to score a Cribbage hand.  If you don't play Cribbage, you've got some learning to do.  We play with a standard poker deck, and a hand consists of four cards plus the "up card".  There are two types of hand: normal, and a 'crib hand'.

Cards come in the format vs where v is one of: A23456789TJQK (T for ten) and s is one of SCDH.  A hand will be given in the form (for example)

AS 2D 3H JS | 4S


where 4S is the up card.  A crib hand will have the format

JD 3C 4H 5H | 5S !


Face cards have a value of 10, and the ace has a value of 1.  Scoring is performed as follows.


Fifteens: for each subset of five cards whose sum is 15, add two points.
Pairs: for each pair of cards with the same rank (not value), add two points.
Runs: for each maximal run of consecutive cards of length longer than 2, add the length of the run in points.
Flush: if all five cards are the same suit, add five points.  Otherwise, if all but the up card are the same suit, add four points.  If this is a crib hand, the four-point variant is not counted.
Nobs: if there is a jack in hand with the same suit of the up card, add one point.


Notes:


Triples and fours of a kind are not special -- there are three pairs in a triple, so a triple is worth 6 points.
Runs can overlap.  For example, AS AH 2D 3C | 2C (a double double run) has four runs of length 3 and two pair, so is worth 3+3+3+3+2+2 = 16 points.  
Only maximal runs are counted, so KS QD JD TC | 9S is worth 5 points, since it is a run of 5.  The sub-runs are not counted.


House Rule:

It's impossible to score 19 points in a hand.  Instead of zero, report a score of 19.

Examples:

5S 5H 5D JS | KS
21

AS 2D 3H JS | 4S !
9

JD 3C 4H 5H | 5S
12

9S 8S 7S 6S | 5H !
9

9S 8S 7S 6S | 5H
13

8D 7D 6D 5D | 4D !
14

8D 7D 6D 5D | 4D
14

AD KD 3C QD | 6D
19


This is code golf.  Shortest solution wins.
C, 364 388 chars

It's big and ugly (though not as big as it once was):

char*L="CA23456789TJQKDHS",b[20],p[15],r[5],s[5],v,i=4,t,m,q;
g(j){++p[r[i]=strchr(L,b[j])-L];s[i]=strchr(L,b[j+1])-L;}
f(j,u){u==15?v+=2:++j&lt;5&amp;&amp;f(j,u,f(j,u+(r[j]&gt;9?10:r[j])));}
main(){gets(b);for(g(14);i--;r[i]^11|s[i]^s[4]||++v)g(i*3);
for(f(i,0);++i&lt;15;v+=q?q*q-q:t&gt;2?t*m:0,t=q?t+1:0,m=q?m*q:1)q=p[i];
while(++t&lt;5&amp;&amp;s[t]==*s);v+=t&gt;4-!b[16]?t:0;printf("%d\n",v?v:19);}


(Line breaks were added to make it easier to read; those aren't included in the above tally.)

The problem description didn't specify if the code needed to check for invalid input, so naturally I assumed that the program was free to misbehave at will if the input, say, contained extra whitespace.

Here's the ungolfed version:

#include &lt;stdio.h&gt;
#include &lt;string.h&gt;

/* A-K correspond to values 1-13. Suit values are arbitrary.
 */
static char const *symbols="CA23456789TJQKDHS";

/* Used as both an input buffer and to bucket cards by rank.
 */
static char buf[20];

/* The cards.
 */
static int rank[5], suit[5];

/* The cards broken down by rank.
 */
static int buckets[15];

static int score;
static int touching, matching, i;

/* Read card number i from buf at position j.
 */
static void getcard(int j)
{
    rank[i] = strchr(symbols, buf[j]) - symbols;
    suit[i] = strchr(symbols, buf[j+1]) - symbols;
    ++buckets[rank[i];
}

/* Recursively find all combinations that add up to fifteen.
 */
static void fifteens(int j, int total)
{
    for ( ; j &lt; 5 ; ++j) {
        int subtotal = total + (rank[j] &gt; 9 ? 10 : rank[j]);
        if (subtotal == 15)
            score += 2;
        else if (subtotal &lt; 15)
            fifteens(j + 1, subtotal);
    }
}

int main(void)
{
    fgets(buf, sizeof buf, stdin);
    score = 0;

    /* Read cards from buf */
    for (i = 0 ; i &lt; 4 ; ++i)
        getcard(i * 3);
    getcard(14);

    /* Score fifteens */
    fifteens(0, 0);

    /* Score any runs and/or pairs */
    touching = 0;
    matching = 1;
    for (i = 1 ; i &lt; 15 ; ++i) {
        if (buckets[i]) {
            score += buckets[i] * (buckets[i] - 1);
            ++touching;
            matching *= buckets[i];
        } else {
            if (touching &gt; 2)
                score += touching * matching;
            touching = 0;
            matching = 1;
        }
    }

    /* Check for flush */
    for (i = 1 ; i &lt; 5 &amp;&amp; suit[i] == suit[0] ; ++i) ;
    if (i &gt;= (buf[17] == '!' ? 5 : 4))
        score += i;

    /* Check for hisnob */
    for (i = 0 ; i &lt; 4 ; ++i)
        if (rank[i] == 11 &amp;&amp; suit[i] == suit[4])
            ++score;

    printf("%d\n", score ? score : 19);
    return 0;
}