## Cribbage Challenge

### Cribbage Challenge

Well if you want a challenge:

Write something that calculates the score of a cribbage hand(not the crib), and with that, show the hands for the 3 highest scores possible.

this is an example of something that isn't a challenge.

Write something that calculates the score of a cribbage hand(not the crib), and with that, show the hands for the 3 highest scores possible.

this is an example of something that isn't a challenge.

### Re: Descentbb.net forum challenge, for all users (proposal)

I think this could also include all possible size of hands.

2, 3, and 4 cards per hands. (I'm looking at the rules)

card values:

2, 3, and 4 cards per hands. (I'm looking at the rules)

card values:

Yeah, I wonder if I could do this in a single elegant python line...K = 10

Q = 10

J = 10

TEN = 10

NINE = 9

EIGHT = 8

...

skip...

...

TWO = 2

ACE = 1

:E

### Re: Descentbb.net forum challenge, for all users (proposal)

just keep it to 4 cards and the turn.

### Re: Descentbb.net forum challenge, for all users (proposal)

ok.

edit:

1524 combinations of cards that score between 31 and 29... I know that's not the challenge, but that's a lot!

/edit

I started writing some code, but I misunderstood the procedure of the game. I'll have to start over later after rereading the rules.

edit:

1524 combinations of cards that score between 31 and 29... I know that's not the challenge, but that's a lot!

/edit

I started writing some code, but I misunderstood the procedure of the game. I'll have to start over later after rereading the rules.

:E

### Re: Descentbb.net forum challenge, for all users (proposal)

The cribbage challenge looks interesting. I think I'm going to take a suboptimal stab at it tonight, once I understand the scoring rules...

### Re: Descentbb.net forum challenge, for all users (proposal)

To add to the above, Isaac, you don't need me to post challenges. Feel free to post your own and see who bites.

Here's what I *think* is a correct scoring function:

Here's what I *think* is a correct scoring function:

Code: Select all

```
# coding: UTF-8
from itertools import chain, groupby, combinations
RANKS = u'A23456789TJQK'
SUITS = u'♥♦♣♠'
VALUES = dict((c, min(n + 1, 10)) for c, n in zip(RANKS, range(len(RANKS))))
INDEXES = dict((c, n) for c, n in zip(RANKS, range(len(RANKS))))
def count(iterable):
return sum(1 for x in iterable)
def powerset(xs, min_len=0):
return chain.from_iterable(combinations(xs, k)
for k in range(len(xs), min_len - 1, -1))
def rank(card):
return card[0]
def suit(card):
return card[1]
def score(*hand):
starter = hand[-1]
ranks = sorted(rank(card) for card in hand)
suits = sorted(suit(card) for card in hand)
values = sorted(VALUES[rank] for rank in ranks)
indexes = sorted(INDEXES[rank] for rank in ranks)
# score two points for any combination adding up to 15
score = sum(2 for v in powerset(values) if sum(v) == 15)
# score two points for pairs, six for three of a kind, 12 for four
score += sum(2 for t in combinations(ranks, 2) if t[0] == t[1])
# score four points for a flush, five for including starter
if all(x == y for x, y in zip(suits[:-1], suits[1:-1])):
score += 5 if suit(starter) == suits[0] else 4
# score one point if hand contains jack of same suit as starter
score += sum(1 for card in hand[:-1]
if rank(card) == 'J' and suit(card) == suit(starter))
# score n for each disjoint run of n cards, n >= 3
runss = groupby(count(xs) for xs in powerset(indexes, 3)
if all(y - x == 1 for x, y in zip(xs, xs[1:])))
try: key, runs = runss.next() # try to get set of longest runs
except StopIteration: pass
else: score += sum(runs)
return score
assert(score(u'3♠', u'4♦', u'5♦', u'J♦', u'6♦') == 9)
assert(score(u'5♣', u'5♦', u'5♥', u'J♠', u'5♠') == 29)
assert(score(u'3♠', u'3♦', u'3♣', u'9♦', u'6♦') == 16)
assert(score(u'6♦', u'7♠', u'7♣', u'8♣', u'8♦') == 24)
assert(score(u'2♦', u'3♣', u'4♠', u'4♣', u'4♥') == 17)
```

### Re: Descentbb.net forum challenge, for all users (proposal)

Code: Select all

```
cards={"K":10,"Q":10,"J":10,"10":10,"9":9,"8":8,"7":7,"6":6,"5":5,"4":4,"3":3,"2":2,"A":1}
hands=[(str((a[0],b[0],c[0],d[0])),(a[1]+b[1]+c[1]+d[1])) for a in cards.items() for b in cards.items() for c in cards.items() for d in cards.items() if a[1]+b[1]+c[1]+d[1]<31 and a[1]+b[1]+c[1]+d[1]>29]
print len(hands)
```

This is how far I've got, but I don't get why there's pegs in the game. I need to find more clear instructions, but I have lots of homework for the next few days (I didn't realize there would be a challenge posted so quickly).

:E

### Re: Descentbb.net forum challenge, for all users (proposal)

it has pegs to keep score. The challenge concerns the counting at the end of a round(the show).

basically score according to this:

basically score according to this:

wikipedia wrote:

fifteen-twos

- two points for each separate combination of two or more cards totalling
exactly fifteenruns

- three points for a run of three consecutive cards (regardless of suit)
- four points for completing a run of four
- five points for completing a run of five
pairs

- two points for a pair of cards of a kind
- six points for three cards of a kind (known as a "pair royal", comprising three distinct pairs)
- twelve points for four cards of a kind (a "double pair royal", comprising six distinct pairs)
flush

- four points for a flush, where all four cards in the hand are of the same suit, with an additional point if the starter card is also of that suit.
It is only a flush in the crib if all five cards, the four in the crib and the starter card, are of the same suit.nobs

- one point for holding the Jack of the same suit as the starter card

### Re: Descentbb.net forum challenge, for all users (proposal)

So these are additional points on top of the points calculated by each card. I see.

:E

### Re: Descentbb.net forum challenge, for all users (proposal)

No, only score using that list. you are over-thinking the problem Issac.

### Re: Descentbb.net forum challenge, for all users (proposal)

One observation that I made for my solution is that the six points for the three of a kind can be counted as just two points for each of the three pairs in the three of a kind. A similar observation can be made for four of a kind. So if it simplifies, you only need to count distinct pairs.

My code for scoring runs though is pretty messy. I think I'm failing to see some kind of elegant simplification.

My code for scoring runs though is pretty messy. I think I'm failing to see some kind of elegant simplification.

### Re: Descentbb.net forum challenge, for all users (proposal)

you can use the number of pairs to limit the length of a given run. That would technically not simplify the solution.

### Re: Descentbb.net forum challenge, for all users (proposal)

OK, I had some time to optimize the "n-of-a-kind" scoring, and I think that the "runs" scoring is faster and easier to read, although I still think it could somehow be simpler:

edit: Simplified "runs" scoring by another line.

Code: Select all

```
# coding: UTF-8
from itertools import count, izip, chain, groupby, combinations
RANKS = u'A23456789TJQK'
SUITS = u'♥♦♣♠'
VALUES = dict((c, min(n + 1, 10)) for c, n in izip(RANKS, range(len(RANKS))))
INDEXES = dict((c, n) for c, n in izip(RANKS, range(len(RANKS))))
def powerset(xs, min_len=0):
return chain.from_iterable(
combinations(xs, k) for k in range(len(xs), min_len - 1, -1))
def product(xs):
return reduce(lambda x, y: x * y, xs, 1)
def rank(card):
return card[0]
def suit(card):
return card[1]
def score(*hand):
starter = hand[-1]
ranks = sorted(rank(card) for card in hand)
suits = sorted(suit(card) for card in hand)
values = sorted(VALUES[rank] for rank in ranks)
indexes = sorted(INDEXES[rank] for rank in ranks)
# score two points for any combination adding up to 15
score = sum(2 for xs in powerset(values) if sum(xs) == 15)
# score two points for pairs, six for three of a kind, 12 for four
score += sum((0, 0, 2, 6, 12)[len(list(xs))] for k, xs in groupby(ranks))
# score four points for a flush, five for including starter
if all(x == y for x, y in izip(suits[:-1], suits[1:-1])):
score += 5 if suit(starter) == suits[0] else 4
# score one point if hand contains jack of same suit as starter
score += sum(1 for card in hand[:-1]
if rank(card) == 'J' and suit(card) == suit(starter))
# score n for each disjoint run of n cards, n >= 3
gs = ((i - j, list(xs)) for (i, xs), j in izip(groupby(indexes), count()))
runs = filter(lambda xs: len(xs) >= 3, # keep only a run of size >= 3
(list(xs) for i, xs in groupby(gs, lambda xs: xs[0])))
score += sum(len(r) * product(len(rank) for i, rank in r) for r in runs)
return score
assert(score(u'3♠', u'4♦', u'5♦', u'J♦', u'6♦') == 9)
assert(score(u'5♣', u'5♦', u'5♥', u'J♠', u'5♠') == 29)
assert(score(u'3♠', u'3♦', u'3♣', u'9♦', u'6♦') == 16)
assert(score(u'6♦', u'7♠', u'7♣', u'8♣', u'8♦') == 24)
assert(score(u'2♦', u'3♣', u'4♠', u'4♣', u'4♥') == 17)
```

### Re: Descentbb.net forum challenge, for all users (proposal)

I think I finally understand the instructions of the challenge, now that I've watched a few games and read different versions of the instructions.

(remember Isaac is slow)

I'm going to first do the UI (click the cards to change them):

http://testing.isaacg.net/crib/cirb.html <-- the first sprite sheet I ever used in a program.

It will allow the user to not only declare what hand he/she has, but there will be another way (not yet added) of checking off cards that have been eliminated from the probability of being obtained by the holder.

1: Show the value and type of hand the holder has.

2: Show the 3 highest possible hands of the cards remaining in the deck, less the eliminated cards.

Am I on track? I assume I finally am.

(remember Isaac is slow)

I'm going to first do the UI (click the cards to change them):

http://testing.isaacg.net/crib/cirb.html <-- the first sprite sheet I ever used in a program.

It will allow the user to not only declare what hand he/she has, but there will be another way (not yet added) of checking off cards that have been eliminated from the probability of being obtained by the holder.

1: Show the value and type of hand the holder has.

2: Show the 3 highest possible hands of the cards remaining in the deck, less the eliminated cards.

Am I on track? I assume I finally am.

:E

### Re: Descentbb.net forum challenge, for all users (proposal)

I think it's far outside the scope of the challenge as stated, but if that seems interesting to you, why not.

### Re: Descentbb.net forum challenge, for all users (proposal)

Also, looking over your code, to create an iterator for every possible hand, simply:
Substitute 5 with 4 to not include the starter. This also handles the problem of getting duplicate cards, but you'll also need to distinguish cards by their suit for this to work right. Something like this should work (untested):

Code: Select all

```
from itertools import combinations
hands = combinations(cards.items(), 5)
```

Code: Select all

```
from itertools import product
cards = list(product(ranks, suits))
```

### Re: Descentbb.net forum challenge, for all users (proposal)

interesting. I have not yet tried product() or combinations() before. I ended up having to add list() around each of those to make them work. Very cool stuff in itertools! Thanks.

:E

### Re: Descentbb.net forum challenge, for all users (proposal)

So list() will put everything into memory all at once, as opposed to just being an iterator generating the items lazily. This is fine for small sequences like a list of all possible cards that you'll be using over and over again, but you should think twice for using list() with all 52 choose 5 possible hands. Iterators don't support all of the operations that a list does (like len()), but this shouldn't be a problem.

### Re: Descentbb.net forum challenge, for all users (proposal)

But with out list() it only can return <itertools.combinations object at 0xb76aaa04> .

Maybe I'm not following the pydoc correctly...

Maybe I'm not following the pydoc correctly...

:E

### Re: Descentbb.net forum challenge, for all users (proposal)

That's normal. It can't print out the elements because doing so would exhaust the iterator, making it useless thereafter. Also, iterators can be infinite, which can take a while to print. You don't ever want to call list() on an infinite iterator either unless you're prepared for some thrashing. In the past, I might have done this once or twice on accident.

A common way to use an iterator is something like:
Or in this case, maybe (but probably not):
Just remember, whenever you want to reiterate freshly over all the hands, you'll need to recreate the iterator by calling combinations(). This might seem like a disadvantage, but it's really not--unlike lists, iterators are cheap to create, since all of the work is done lazily while iterating.

Most library functions that you might think take lists are actually taking iterators, lists being iterable.

A common way to use an iterator is something like:

Code: Select all

```
for hand in hands:
pass # your code here
```

Code: Select all

```
for a, b, c, d, e in hands:
pass # your code here
```

Most library functions that you might think take lists are actually taking iterators, lists being iterable.

### Re: Descentbb.net forum challenge, for all users (proposal)

I'd take a stab at challenges, when I had time.

I'd also fall relatively short of the coding prowess present in the forums here.

I'd also fall relatively short of the coding prowess present in the forums here.

Arch Linux x86-64, Openbox

"We'll just set a new course for that empty region over there, near that blackish, holeish thing. " Zapp Brannigan

"We'll just set a new course for that empty region over there, near that blackish, holeish thing. " Zapp Brannigan