Issue #229, May 2013

Fifteens. Why are they so important to *Cribbage*, and how do you calculate
them? And what about pairs? Read on as Dave continues to step through the
construction of a *Cribbage* game written as a shell script.

We're still building out the *Cribbage* game with the
six-choose-four
challenge that's at the very beginning of the game when the players in
a two-player game discard two of their six cards into the
“crib”,
a third hand that alternates between players. Think of it like
playing draw poker, except when the players discard their cards, the dealer could also
pick them up and play them as a second hand. That'd be weird, but interesting,
wouldn't it? Hmmm....

No, let's stay focused!

In my last article, we left the script at a point where it was able to pull out all two-card, three-card and four-card combinations of cards to ascertain which of them add up to 15 or otherwise offer point opportunities. Now let's jump in and actually calculate values and see what happens.

Specifically, here's where we left off:

$ cribbage.sh Hand: AD, AS, 2D, 3C, 5C, KC. Subhand 0: AD AS 2D 3C total 15-point value of that hand: 0 Subhand 4: AD AS 3C KC total 15-point value of that hand: 2 Subhand 14: 2D 3C 5C KC total 15-point value of that hand: 4

If we were looking only for combinations that add up to 15, the script has identified the best possible combination—that of a 2, 3, 5 and king. The problem is, runs are worth lots of points too, and the run of AD, AS, 2D, 3C is worth AD+2+3=3, AS+2+3=3, and the pair of aces adds another 2, so that's eight points, far more than the two fifteens. But, we'll get to that later.

For now, let's look at how the fifteens are calculated by just examining the two-card case:

for subhand in {0..5} do sum=0 for thecard in ${fourtwo[$subhand]} do sum=$(( $sum + ${c15[$thecard]} )) done if [ $sum -eq 15 ] ; then points=$(( $points + 2 )) fi done

Remember that the `$fourtwo` array is an enumerated
list of all possible two-card combinations out of four (for example, 1+2,
1+3, 1+4, 2+3 and so on). The `$points`
variable accumulates how many fifteens are found as the function tests two-card,
three-card and, finally, the one possible four-card combination, ending
with the echo statement:

echo " total 15-point value of that hand: $points"

We'll tweak that as the function expands in capabilities, but for now, that's useful.

The next step is to calculate pairs. It turns out that we don't need any
additional code to calculate the value of three of a kind or four of a kind
(though in years of playing *Cribbage*, I have never had four of a kind in my
hand!), because they are unto themselves combinations of pairs. That is, if
a player has 3D, 3S and 3H, it's worth six points: two points for 3D+3S,
two points for 3D+3H and two points for 3S+3H—handy, really!

Because the `calc15()` function (shown in my last
article) already offers a lot of the
infrastructure we'll need, we're just going to expand on it, even
though it technically won't be calculating only 15-point values any
more. That's okay; we'll end up renaming the function, but for now,
let's just code.

To extract pairs, the loop is slightly different:

twocards=${fourtwo[$subhand]} card1=${twocards:0:1} card2=${twocards:2}

Placing this within the snippet `for subhand in {0..5}`
will let us test all two-card combinations of the four-card
hand given to the function.

There's a bit of fancy variable referencing here too. It turns out we can extract substrings at a variable reference by using the following notation:

${string:position:length}

In the first instance, `card1` will end up being the
first value in the `twocards`variable, which itself is extracted from the
`fourtwo[]` array. Its format is “X Y”, so the second reference
needs to start at 2. Being lazy, we just grab the rest of the string,
which means we can omit the `:length` parameter.

Why have the interim variable `twocards`? Because the shell can figure
out only a certain level of complexity,
and writing something like:

${{fourtwo[$subhand]}:0:1}

just gives me a headache.

The next step is simply to compare the two cards and see if they're the same rank:

if [ ${c15[$card1]} = ${c15[$card2]} ] ; then echo "we've got a pair: ${c15[$card1]} and ${c15[$card2]}" fi

This all looks good, but there's a glaring bug in the code, as is immediately obvious with some debugging info:

Subhand 14: 9S 10H JH KD calc15() given ranks: 9 10 10 10 PAIRS: testing two cards 0 and 1 from 0 1 PAIRS: testing two cards 0 and 2 from 0 2 PAIRS: testing two cards 0 and 3 from 0 3 PAIRS: testing two cards 1 and 2 from 1 2 we've got a pair: 10 and 10 PAIRS: testing two cards 1 and 3 from 1 3 we've got a pair: 10 and 10 PAIRS: testing two cards 2 and 3 from 2 3 we've got a pair: 10 and 10

The problem is that `calc15()` is being given the ranks of the cards after
they've been scrubbed to just point values, so a 10H, JH and KD all
look like they are point value 10. That works great for calculating
fifteens, but a 10H+KD is most assuredly not a valid pair.

The fix is easy. We can just have `calc15()` get both the four normalized
ranks and the four original ranks as parameters. Recall that in the
function `handvalue4()` ranks are normalized through code blocks like this:

# now fix rank to normalize for value=10 case $r1 in 11|12|13) nr1=10 ;; *) nr1=$r1 ;; esac

So `$r1` already is the proper rank of the first card
(that is, 1–13), and `$nr1`
is the normalized rank (where a 10 and a K have value 10). Then, invoking
`calc15()` is just a tiny bit more complex:

calc15 $nr1 $nr2 $nr3 $nr4 $r1 $r2 $r3 $r4

For notational convenience, let's also grab the 5th–8th parameters and
reassign them into a local array `$cr15[]` like this:

cr15[0]=$5; cr15[1]=$6; cr15[2]=$7; cr15[3]=$8

Now the fix to calculate proper pairs is quite easy:

Subhand 14: 10S JS QC QD calc15() given ranks: 10 10 10 10 PAIRS: testing two cards 0 and 1 from 0 1 PAIRS: testing two cards 0 and 2 from 0 2 PAIRS: testing two cards 0 and 3 from 0 3 PAIRS: testing two cards 1 and 2 from 1 2 PAIRS: testing two cards 1 and 3 from 1 3 PAIRS: testing two cards 2 and 3 from 2 3 we've got a pair: 12 and 12

And, I'm out of space for this article. In my next article, we'll continue expanding on the pair calculations and add the final piece we need before we can actually clean it up and pick the best four out of six cards: testing for a flush, the situation where all four cards are of the same suit.

Copyright © 1994 - 2018 Linux Journal. All rights reserved.