Cribbage scoring library
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

318 lines
6.8 KiB

package cribbage
import (
"sort"
"code.rocketnine.space/tslocum/joker"
)
// ScoringType represents a set of scoring rules.
type ScoringType int
// Scoring types
const (
Peg ScoringType = 1
ShowHand ScoringType = 2
ShowCrib ScoringType = 3
)
func (t ScoringType) String() string {
switch t {
case Peg:
return "Peg"
case ShowHand:
return "ShowHand"
case ShowCrib:
return "ShowCrib"
default:
return "?"
}
}
// ScoreType represents a type of score.
type ScoreType int
// Score types and their point values
const (
Score15 ScoreType = 1 // 2
ScorePair ScoreType = 2 // 2
ScoreRun ScoreType = 3 // 1/card
ScoreFlush ScoreType = 4 // 1/card
ScoreNibs ScoreType = 5 // 2
ScoreNobs ScoreType = 6 // 1
Score31 ScoreType = 7 // 2
ScoreGo ScoreType = 8 // 1
)
func (t ScoreType) String() string {
switch t {
case Score15:
return "15"
case ScorePair:
return "Pair"
case ScoreRun:
return "Run"
case ScoreFlush:
return "Flush"
case ScoreNibs:
return "Nibs"
case ScoreNobs:
return "Nobs"
case Score31:
return "31"
case ScoreGo:
return "Go"
default:
return "?"
}
}
// Score returns the score of a pegging play or shown hand.
func Score(scoringType ScoringType, c joker.Cards, starter joker.Card) (int, ScoreResults) {
if (scoringType == ShowHand || scoringType == ShowCrib) && (starter.Face == 0 || starter.Suit == 0) {
return 0, nil
}
var points int
var results ScoreResults
if c.Len() == 0 {
return points, results
}
var scoreCards joker.Cards
var permutations []joker.Cards
if scoringType == Peg {
scoreCards = c.Reversed()
} else {
scoreCards = append(c.Copy(), starter)
sort.Sort(scoreCards)
permutations = scoreCards.Permutations()
}
// Score 15s
fifteenscore := 0
fifteenvalue := 0
if scoringType != Peg {
var allusedcards []joker.Cards
var usedcards joker.Cards
SCORE15:
for _, permhand := range permutations {
fifteenvalue = 0
usedcards = joker.Cards{}
for _, card := range permhand {
usedcards = append(usedcards, card)
fifteenvalue += Value(card)
if fifteenvalue >= 15 {
if fifteenvalue == 15 {
alreadyused := false
sort.Sort(usedcards)
for _, pastusedcards := range allusedcards {
if usedcards.Equal(pastusedcards) {
alreadyused = true
break
}
}
if !alreadyused {
allusedcards = append(allusedcards, usedcards)
fifteenscore++
sort.Sort(usedcards)
results = append(results, ScoreResult{Type: Score15, Cards: usedcards, Points: 2})
}
}
continue SCORE15
}
}
}
} else {
if Sum(scoreCards) == 15 {
results = append(results, ScoreResult{Type: Score15, Cards: scoreCards.Sorted(), Points: 2})
}
}
// Score pairs
if scoringType != Peg {
var faces []joker.CardFace
SCOREPAIR:
for _, card := range scoreCards {
for _, face := range faces {
if face == card.Face {
continue SCOREPAIR
}
}
var paircards joker.Cards
for _, compcard := range scoreCards {
if compcard.Face != card.Face {
continue
}
paircards = append(paircards, compcard)
}
if len(paircards) > 1 {
pairmultiplier := 1
if len(paircards) == 3 {
pairmultiplier = 2
} else if len(paircards) == 4 {
pairmultiplier = 3
}
sort.Sort(paircards)
results = append(results, ScoreResult{Type: ScorePair, Cards: paircards, Points: len(paircards) * pairmultiplier})
}
faces = append(faces, card.Face)
}
} else {
if len(scoreCards) > 0 {
var pairCards joker.Cards
for _, compcard := range scoreCards[1:] {
if compcard.Face != scoreCards[0].Face {
break
}
pairCards = append(pairCards, compcard)
}
pairmultiplier := 1
if len(pairCards) == 2 {
pairmultiplier = 2
} else if len(pairCards) == 3 {
pairmultiplier = 3
}
if pairCards != nil {
pairCards = append(pairCards, scoreCards[0])
sort.Sort(pairCards)
results = append(results, ScoreResult{Type: ScorePair, Cards: pairCards, Points: len(pairCards) * pairmultiplier})
}
}
}
// Score runs
var allRunCards []joker.Cards
var runCards joker.Cards
var runScore int
if scoringType == Peg {
var compHand joker.Cards
var compScore int
var runValue int
runScore = 1
// Examine the pile for a run by shortening the checked pile one card
// after each iteration.
SCOREPEGRUN:
for complen := len(scoreCards); complen > 0; complen-- {
compHand = scoreCards[0:complen].Sorted()
compScore = 1
runCards = nil
for i, compcard := range compHand {
if i > 0 {
if int(compcard.Face) == (runValue + 1) {
compScore++
} else {
continue SCOREPEGRUN
}
}
runValue = int(compcard.Face)
}
if compScore > runScore {
runScore = compScore
if runScore == len(scoreCards) {
runCards = compHand
break SCOREPEGRUN
}
}
}
if runScore >= 3 {
results = append(results, ScoreResult{Type: ScoreRun, Cards: runCards, Points: runScore})
}
} else {
for runLength := 6; runLength > 3; runLength-- {
SCOREHANDRUN:
for _, permhand := range permutations {
runCards = joker.Cards{}
runScore = 0
for i := range permhand {
if i > 0 && permhand[i].Face != permhand[i-1].Face-1 {
break
}
runScore++
runCards = append(runCards, permhand[i])
}
if runScore != runLength {
continue
}
sort.Sort(runCards)
for _, rc := range allRunCards {
containsAll := true
for _, runCard := range runCards {
if !rc.Contains(runCard) {
containsAll = false
break
}
}
if containsAll {
continue SCOREHANDRUN
}
}
results = append(results, ScoreResult{Type: ScoreRun, Cards: runCards, Points: runScore})
allRunCards = append(allRunCards, runCards)
}
}
}
// Score flushes
if scoringType != Peg {
for _, suit := range joker.StandardSuits {
suitvalue := 0
var flushCards joker.Cards
for _, card := range c {
if card.Suit == suit {
suitvalue++
flushCards = append(flushCards, card)
}
}
if starter.Suit == suit {
suitvalue++
flushCards = append(flushCards, starter)
}
if suitvalue == 5 || (suitvalue == 4 && scoringType == ShowHand) {
sort.Sort(flushCards)
results = append(results, ScoreResult{Type: ScoreFlush, Cards: flushCards, Points: suitvalue})
break
}
}
}
// Score nobs
if scoringType != Peg {
rightJack := joker.Card{joker.FaceJack, starter.Suit}
if c.Contains(rightJack) {
results = append(results, ScoreResult{Type: ScoreNobs, Cards: joker.Cards{rightJack}, Points: 1})
}
}
// Score 31
if scoringType == Peg && Sum(scoreCards) == 31 {
sort.Sort(scoreCards)
results = append(results, ScoreResult{Type: Score31, Cards: scoreCards, Points: 2})
}
for _, r := range results {
points += r.Points
}
sort.Sort(results)
return points, results
}